2010-02-18 8 views
6

Me gustaría crear una tubería en una secuencia de comandos ksh (usando exec) que la tubería esté en una T, y envíe la salida a una tubería.Crear una tubería que escriba en varios archivos (tee)

actual:

#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 

¿Qué he había como hacer (pero no tienen la sintaxis correcta):

#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 

¿Cómo puedo crear esta tubería?

+0

En bash, al menos, la última línea en cada uno de sus bloques sería mejor descrito como "adjuntar STDOUT a STDERR" o "redirigir STDERR a STDOUT". Después 2> dump-stdout estará vacío, pero cualquier cosa que repita> & 2 irá al mismo lugar que lo que echo> & 1. – dubiousjim

Respuesta

5

Aquí hay una solución que uso. Funciona bajo ksh en mi Mac. Está muy bien encapsulado en las funciones start_logging() y stop_logging() para facilitar la vida.

El código es el siguiente en la práctica:

# 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 

Aquí es todo el archivo. Las funciones de inicio y detención junto con el ejemplo anterior se encuentran en la parte inferior del archivo. Para que sea más fácil de usar, solo coloque las funciones de inicio y detención en su propio archivo y guárdelas en las secuencias de comandos donde necesita el registro.

#!/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

Gracias por proporcionarme una implementación, realmente me gustan esas funciones. –

0

Sé bash no ksh, pero hay mucha superposición, así que quizás esto funcione también allí.

process1 N> >(process2) 

Crea un proceso de subshell running2. Subnivel que recibe como su StdIn los datos de N. descriptor de archivo de proceso1 Así, en particular, se puede hacer:

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

No sé si esto también funcionaría si process1 se sustituye con exec, pero que podría dar Es un intento.

+0

Lamentablemente, no la sintaxis ni siquiera funciona en Ksh. –

0

Hay |& y >&p en ksh, pero no pude hacer que hicieran lo que estaba buscando. Tal vez tu puedas.

0

En lugar de:

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

se limitan a:

tee -a ${Log} >&3 &

tee se bifurcará a un segundo plano, y consumirá el proceso de llamada (es decir, de su script) STDIN como Fue en el momento en que se bifurcó tee.

+0

Lo intentaré tan pronto como tenga la oportunidad, gracias. –

+0

No funcionó en mi prueba. Tee comenzó y escribió para iniciar sesión, pero no consumió stdout. –

+0

puede haber algo más incorrecto en su configuración de manejo de archivos; comienza todo y utiliza 'lsof' para ver/validar lo que va donde. – vladr

6

He resuelto una solución usando 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 solución muy concisa. Gracias. –

Cuestiones relacionadas