2010-02-18 6 views
6

Ich möchte eine Pipe in einem ksh-Skript (mit exec), die Pipe zu einem Tee ist, und sendet die Ausgabe an eine Pipe.Erstellen Sie eine Pipe, die in mehrere Dateien schreibt (tee)

Aktuell:

#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 

Was hast würde ich wie zu tun (aber ich habe nicht die korrekte Syntax haben):

#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 

Wie kann ich erstelle diese Pfeife?

+0

In bash zumindest die letzte Zeile in jedem Ihrer Blöcke besser als „STDOUT STDERR anhängen“ beschrieben werden würde oder „redirect STDERR zu STDOUT“. Danach wird 2> dump-stdout leer sein, aber alles, was du zurückgibst> & 2 wird an die gleiche Stelle gehen wie das, was du echost> & 1. – dubiousjim

Antwort

5

Hier ist eine Lösung, die ich verwende. Es funktioniert unter ksh auf meinem Mac. Es ist gut in die Funktionen start_logging() und stop_logging() eingekapselt, um das Leben einfacher zu machen.

Der Code sieht wie dies in der Praxis:

# 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 

Hier ist die gesamte Datei. Die Start- und Stopp-Funktionen sowie das obige Beispiel befinden sich am Ende der Datei. Um die Verwendung zu vereinfachen, fügen Sie die Start- und Stopp-Funktionen in eine eigene Datei ein und speichern sie in den Skripts, in denen Sie die Protokollierung benötigen.

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

Dank für die Bereitstellung einer Implementierung, mag ich diese Funktionen wirklich. –

0

Ich weiß Bash nicht ksh, aber es gibt eine Menge Überschneidungen, also wird das vielleicht auch dort funktionieren.

process1 N> >(process2) 

Erstellt ein laufendes Subshell process2. Das Sub-Shell empfängt als die Daten aus process1 der Dateideskriptor N. stdin So insbesondere die Sie tun können:

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

Ich weiß nicht, ob dies würde auch funktionieren, wenn process1 mit exec ersetzt wird, aber man konnte geben es ist ein Versuch.

+0

Leider funktioniert die Syntax nicht einmal in Ksh. –

0

Es gibt |& und >&p in ksh, aber ich konnte sie nicht tun, was Sie suchen. Vielleicht ist es Ihnen möglich.

0

Statt:

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

tun einfach:

tee -a ${Log} >&3 &

tee wird in den Hintergrund Gabel und wird den Anrufprozess (dh Ihres Skripts) STDIN als sie verbrauchen war zu der Zeit, dass tee gegabelt wurde.

+0

Ich werde das versuchen, sobald ich eine Chance habe, danke. –

+0

Es hat nicht in meinem Test funktioniert. T-Shirt gestartet und schrieb, um zu loggen, aber es verbrauchte nicht stdout. –

+0

Es kann etwas anderes in Ihrem Dateihandle-Setup falsch sein; Beginne das Ganze und benutze 'lsof', um zu sehen, was wo hingeht. – vladr

6

Ich arbeitete eine Lösung mit 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

Eine sehr prägnante Lösung. Danke. –