2010-02-18 26 views
6

Vorrei creare una pipe in uno script ksh (usando exec) che va in pipe a un tee e invia l'output a una pipe.Creare una pipe che scrive su più file (tee)

attuale:

#Redirect EVERYTHING 
exec 3>&1 #Save STDOUT as 3 
exec 4>&2 #Save STDERR as 4 
exec 1>${Log} #Redirect STDOUT to a log 
exec 2>&1 #Redirect STDERR to STDOUT 

Cos'hai mi piacerebbe come fare (ma non ho la sintassi corretta):

#Redirect EVERYTHING 
exec 3>&1 #Save STDOUT as 3 
exec 4>&2 #Save STDERR as 4 
exec 1>tee -a ${Log} >&3 #Redirect STDOUT to a log 
exec 2>&1 #Redirect STDERR to STDOUT 

Come posso creare questa pipa?

+0

In bash, almeno, l'ultima riga in ciascuno dei vostri blocchi sarebbe meglio descritto come "allegare STDOUT STDERR" o "reindirizzare STDERR a STDOUT". Successivamente 2> dump-stdout sarà vuoto, ma tutto ciò che tu echo> & 2 andrà nello stesso posto di quello che fai eco> & 1. – dubiousjim

risposta

5

Ecco una soluzione che utilizzo. Funziona con ksh sul mio Mac. È ben incapsulato nelle funzioni start_logging() e stop_logging() per semplificare la vita.

Il codice simile a questo, in pratica:

# Optional: 
# Set the name and location of the log file. 
# OUTPUT_LOG=output.log # default 
# Set the name and location of the named pipe used. 
# OUTPUT_PIPE=output.pipe # default 

start_logging 
# Default is to append to an existing log file. 
# start_logging delete_existing_logfile 
echo "This is on standard out" 
echo "This is on standard err" >&2 
stop_logging 

Ecco l'intero file. Le funzioni di avvio e arresto insieme all'esempio sopra sono tutte in fondo al file. Per semplificare l'utilizzo, basta inserire le funzioni di avvio e arresto nel proprio file e recuperarle negli script in cui è necessario il logging.

#!/bin/sh 

# Author: Harvey Chapman <hchapman _AT_ 3gfp.com> 
# Description: POSIX shell functions that can be used with tee to simultaneously put 
#    stderr and stdout to both a file and stdout 
# 
# Based on: 
# Re: How to redirect stderr and stdout to a file plus display at the same time 
# http://www.travishartwell.net/blog/2006/08/19_2220 

# 
# Original example function from Travis Hartwell's blog. 
# Note: I've made minor changes to it. 
example() 
{ 
    OUTPUT_LOG=output.log 
    OUTPUT_PIPE=output.pipe 

    # This should really be -p to test that it's a pipe. 
    if [ ! -e $OUTPUT_PIPE ]; then 
     mkfifo $OUTPUT_PIPE 
    fi 

    # This should really be -f to test that it's a regular file. 
    if [ -e $OUTPUT_LOG ]; then 
     rm $OUTPUT_LOG 
    fi 

    exec 3>&1 4>&2 
    tee $OUTPUT_LOG < $OUTPUT_PIPE >&3 & 
    tpid=$! 
    exec > $OUTPUT_PIPE 2>&1 

    echo "This is on standard out" 
    echo "This is on standard err" >&2 

    exec 1>&3 3>&- 2>&4 4>&- 
    wait $tpid 

    rm $OUTPUT_PIPE 
} 

# A slightly reduced version of example() 
example2() 
{ 
    OUTPUT_LOG=output.log 
    OUTPUT_PIPE=output.pipe 

    rm -f $OUTPUT_PIPE 
    mkfifo $OUTPUT_PIPE 
    rm -f $OUTPUT_LOG 

    tee $OUTPUT_LOG < $OUTPUT_PIPE & 
    tpid=$! 

    exec 3>&1 4>&2 >$OUTPUT_PIPE 2>&1 

    echo "This is on standard out" 
    echo "This is on standard err" >&2 

    exec 1>&3 3>&- 2>&4 4>&- 
    wait $tpid 
    rm -f $OUTPUT_PIPE 
} 

# 
# Logging methods based on above. See the example below for how to use them. 
# 

# Usage: start_logging [delete_existing_logfile] 
start_logging() 
{ 
    # Check to see if OUTPUT_LOG and OUTPUT_PIPE need to be defined. 
    if [ -z "$OUTPUT_LOG" ]; then 
    OUTPUT_LOG=output.log 
    fi 
    if [ -z "$OUTPUT_PIPE" ]; then 
    OUTPUT_PIPE=output.pipe 
    fi 
    # Make sure that we're not already logging. 
    if [ -n "$OUTPUT_PID" ]; then 
    echo "Logging already started!" 
    return 1 
    fi 

    # Always remove the log and pipe first. 
    rm -f $OUTPUT_PIPE 
    # Delete the logfile first if told to. 
    if [ "$1" = delete_existing_logfile ]; then 
    rm -f $OUTPUT_LOG 
    fi 

    mkfifo $OUTPUT_PIPE 
    tee -a $OUTPUT_LOG < $OUTPUT_PIPE & 
    OUTPUT_PID=$! 

    exec 3>&1 4>&2 >$OUTPUT_PIPE 2>&1 
} 

stop_logging() 
{ 
    # Make sure that we're currently logging. 
    if [ -z "$OUTPUT_PID" ]; then 
    echo "Logging not yet started!" 
    return 1 
    fi 
    exec 1>&3 3>&- 2>&4 4>&- 
    wait $OUTPUT_PID 
    rm -f $OUTPUT_PIPE 
    unset OUTPUT_PID 
} 

example3() 
{ 
    start_logging 
    #start_logging delete_existing_logfile 
    echo "This is on standard out" 
    echo "This is on standard err" >&2 
    stop_logging 
} 

#example 
#example2 
example3 
+0

Grazie per aver fornito un'implementazione, mi piacciono molto queste funzioni. –

0

So che bash non è ksh, ma c'è molta sovrapposizione, quindi forse questo funzionerà anche lì.

process1 N> >(process2) 

Crea una subshell in esecuzione process2. Che subshell riceve come suo stdin i dati dal descrittore di file N. di process1 Così, in particolare, si potrebbe fare:

process1 1> >(tee -a mylog >&3) 

Non so se questo potrebbe anche funzionare se process1 è sostituito con exec, ma si potrebbe dare è una prova.

+0

Purtroppo, la sintassi non funziona nemmeno in Ksh. –

0

Ci sono |& e >&p in ksh, ma non sono riuscito a farle fare ciò che state cercando. Forse puoi.

0

Invece di:

exec 1>tee -a ${Log} >&3

fare semplicemente:

tee -a ${Log} >&3 &

tee si biforca in secondo piano, e consumerà il processo chiamante (vale a dire del vostro script) STDIN come era al momento che tee biforcuta.

+0

Lo proverò appena ne avrò la possibilità, grazie. –

+0

Non ha funzionato nei miei test. Tee ha iniziato e ha scritto per accedere, ma non ha consumato stdout. –

+0

potrebbe esserci qualcos'altro di sbagliato nella configurazione del tuo filehandle; avvia tutto e usa 'lsof' per vedere/convalidare cosa va dove. – vladr

6

Ho elaborato una soluzione utilizzando named pipes.

#!/bin/ksh 

LOG=~/testLog.log 
PIPE=~/logPipe 
mkfifo ${PIPE} 
exec 3>&1 #Save STDOUT as 3 
exec 4>&2 #Save STDERR as 4 
tee -a ${LOG} <${PIPE} >&3 & #Start tee off the logpipe in the background 
exec 1>${PIPE} #Redirect stdout to the pipe 
exec 2>&1 #Redirect STDERR to STDOUT 

echo "TEST" 
echo Test 2 

ls | grep -i "test" 

rm -f ${PIPE} #Remove the pipe 
+0

Una soluzione molto concisa. Grazie. –

Problemi correlati