2009-06-12 13 views
14

jest alternatywą dla "tee", która przechwytuje STDOUT/STDERR wykonywanej komendy i kończy działanie z tym samym kodem wyjścia, co przetworzone polecenie. Coś jak następuje:Stan tee i wyjścia

eet -a some.log -- mycommand --foo --bar 

Gdzie „EET” to wyimaginowana alternatywą dla „tee” :) (środki -a dołączyć, - dzieli przechwycony polecenia) To nie powinno być trudne do włamywanie takiego polecenia, ale może to już istnieje i nie jestem tego świadomy?

Dzięki.

+1

Zakładam, że prawdziwe pytanie brzmi: jak wydzielić wyjście i przechwycić status wyjścia. Jeśli tak: możliwy duplikat [bash: tee output AND capture exit status] (http://stackoverflow.com/questions/1221833/bash-tee-output-and-capture-exit-status) – lesmana

Odpowiedz

5

Oto eet. Działa z każdym Bash'em, z którego mogę skorzystać, od 2.05b do 4.0.

#!/bin/bash 
tee_args=() 
while [[ $# > 0 && $1 != -- ]]; do 
    tee_args=("${tee_args[@]}" "$1") 
    shift 
done 
shift 
# now ${tee_args[*]} has the arguments before --, 
# and $* has the arguments after -- 

# redirect standard out through a pipe to tee 
exec | tee "${tee_args[@]}" 

# do the *real* exec of the desired program 
exec "[email protected]" 

(pipefail i $PIPESTATUS są ładne, ale wspominam je wprowadza w 3.1 lub coś koło tego.)

+0

To dziwne - ale to nie działa dla mnie: jirka @ debian: ~/monitor $ exec | wc -c \\ 0 \\ 0 jirka @ debian: ~/monitor $ exec echo a \\ a (\\ oznacza znak nowej linii) – jpalecek

0

G'day,

Zakładając ciachnięcie lub zsh,

my_command >>my_log 2>&1 

N.B. Sekwencja przekierowania i powielania STDERR na STDOUT jest znacząca!

Edytuj: Ups. Nie zdawałem sobie sprawy, że chcesz zobaczyć także wynik na ekranie. To oczywiście przekieruje całe wyjście do pliku my_log.

HTH

okrzyki,

+1

Wysyła wszystkie dane wyjściowe do my_log , ale w przeciwieństwie do tee, nie pojawia się również na konsoli. –

+0

@Steve, masz rację. Mój błąd.) -: –

9

natknął się na parę tutaj http://www.perlmonks.org/?node_id=597613 ciekawych rozwiązań.

1) Istnieje $ PIPESTATUS zmienna dostępna w bash:

false | tee /dev/null 
    [ $PIPESTATUS -eq 0 ] || exit $PIPESTATUS 

2) i najprostszą prototyp "EET" w Perl może wyglądać następująco:

open MAKE, "command 2>&1 |" or die; 
    open (LOGFILE, ">>some.log") or die; 
    while (<MAKE>) { print LOGFILE $_; print } 
    close MAKE; # to get $? 
    my $exit = $? >> 8; 
    close LOGFILE; 
-1
#!/bin/sh 
logfile="$1" 
shift 
exec 2>&1 
exec "[email protected]" | tee "$logfile" 

Mam nadzieję, że to działa dla ciebie.

+0

Uruchom to z argumentami "foo false" - powinno wrócić z kodem wyjścia 1 (z false), ale otrzymuję 0 (prawdopodobnie z tee). –

+0

Mój zły. Muszę to zrobić w starym stylu. PIPE =/tmp/$$. Pipe; mkfifo "$ PIPE"; logfile = "$ 1"; shift; trójnik "$ logfile" <"$ PIPE" &; "$ @" 2> & 1> "$ PIPE"; status = $ ?; rm "$ PIPE"; exit $ status –

0
{ mycommand --foo --bar 2>&1; ret=$?; } | tee -a some.log; (exit $ret) 
+3

Przynajmniej w bash, $ ret nie jest dostępny poza {list}, więc to nie działa. –

20

Działa z bash:

(
    set -o pipefail 
    mycommand --foo --bar | tee some.log 
) 

Nawiasy są po to aby ograniczyć wpływ pipefail do tylko jednego polecenia.

Z bash (1) strony man:

Zwracany rurociągu jest kod zakończenia ostatniego polecenia, chyba że opcja pipefail jest włączony. Jeśli włączone jest pipefail, status powrotu potoku jest wartością ostatniego (prawostronnego) polecenia, aby zakończyć z niezerowym stanem lub zero, jeśli wszystkie polecenia zakończą się pomyślnie.
1

Korn shell, wszystko w 1 linii:

foo; RET_VAL=$?; if test ${RET_VAL} != 0;then echo $RET_VAL; echo Error occurred!>/tmp/out.err;exit 2;fi |tee >>/tmp/out.err ; if test ${RET_VAL} != 0;then exit $RET_VAL;fi 
1

To jest to, co uważam za najlepsze rozwiązanie czystej Bourne-shell użyć jako podstawy, na których można zbudować swój "EET":

# You want to pipe command1 through command2: 
exec 4>&1 
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1` 
# $exitstatus now has command1's exit status. 

myślę, że to jest najlepszy wyjaśnione od wewnątrz - Command1 wykona i wydrukować jego regularne wyjścia na standardowe wyjście (deskryptor pliku 1), a następnie po jej zakończeniu printf wykona i kod wyjścia druku Command1 w sprawie jej stdout , ale to stdout jest przekierowywane do deskryptora pliku 3.

Podczas gdy polecenie 1 jest uruchomione, jego standardowe wyjście jest przesyłane do polecenia 2 (wyjście printf nigdy nie wysyła go do polecenia2, ponieważ wysyłamy je do deskryptora pliku 3 zamiast 1, co odczytuje potok). Następnie przekierowujemy dane wyjściowe polecenia 2 do deskryptora pliku 4, tak że również pozostaje poza deskryptorem pliku 1 - ponieważ chcemy, aby deskryptor pliku 1 był wolny nieco później, ponieważ przeniesiemy dane wyjściowe printf na deskryptor pliku 3 z powrotem do deskryptora pliku 1 - ponieważ to właśnie zrobi podstawienie polecenia (backticks), a to zostanie umieszczone w zmiennej.

Ostatnia część magii to pierwsza exec 4>&1, którą zrobiliśmy jako osobne polecenie - otwiera deskryptor 4 pliku jako kopię wyjściowej powłoki zewnętrznej. Podstawienie komendy spowoduje uchwycenie tego, co jest napisane standardowo z perspektywy poleceń wewnątrz niego - ale ponieważ wyjście command2 przejdzie do deskryptora pliku 4, jeśli chodzi o podstawianie komend, podstawienie komendy nie przechwytuje go - jednak po "wyjściu" z podstawiania poleceń, w rzeczywistości nadal idzie do ogólnego deskryptora pliku skryptu 1.

(exec 4>&1 musi być oddzielnym poleceniem, ponieważ wiele popularnych powłok nie podoba się podczas próby napisz do deskryptora pliku wewnątrz podstawiania komend, który jest otwierany w poleceniu "zewnętrznym", które używa podstawienia.Tak jest to najprostszy przenośny sposób, aby to zrobić.)

Możesz na to spojrzeć w mniej techniczny sposób i więcej playf ul sposób, tak jakby wyjścia poleceń przeniosły się nawzajem: command1 pipes to command2, a następnie wyjście printf przeskakuje polecenie 2, tak aby polecenie2 go nie przechwyciło, a następnie wyjście polecenia 2 przeskakuje i wychodzi z podstawiania poleceń tak jak printf ląduje w samą porę, aby zostać przechwyconym przez podstawienie, tak że kończy się on zmienną, a dane wyjściowe polecenia 2 są na swój wesoły sposób zapisywane na standardowe wyjście, tak jak w normalnej rurze.

Ponadto, jak rozumiem, $? nadal będzie zawierał kod powrotu drugiego polecenia w potoku, ponieważ przypisania zmiennych, podstawienia komend i komendy złożone są efektywnie przezroczyste dla kodu powrotu komendy wewnątrz nich, więc zwracany status polecenia command2 powinien zostać propagowany.

Ograniczeniem jest to, że możliwe jest, że polecenie 1 zakończy się w końcu przy użyciu deskryptorów plików 3 lub 4, lub że polecenie 2 lub dowolne z późniejszych poleceń użyje deskryptora pliku 4, więc aby być bardziej niezawodnym, należy:

exec 4>&1 
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1` 
exec 4>&- 

Zauważ, że mogę użyć polecenia złożone w moim przykładzie, ale podpowłok (używając () zamiast { } będzie również działać, choć być może być mniej skuteczny.)

komendy dziedziczy deskryptory plików z procesu, który uruchamia je, więc cała druga linia odziedziczy deskryptor pliku cztery, i komenda złożona, po której następuje 3>&1, dziedziczy trzy deskryptor pliku.Tak więc 4>&- upewnia się, że wewnętrzne polecenie złożone nie dziedziczy deskryptora pliku cztery, a 3>&- nie dziedziczy deskryptora pliku trzy, więc command1 dostaje "czystsze", bardziej standardowe środowisko. Możesz także przesunąć wewnętrzną linię 4>&- obok 3>&-, ale rozumiem, dlaczego nie ograniczać jej zakresu tak bardzo, jak to tylko możliwe.

Nie jestem pewien, jak często rzeczy używają deskryptorów plików trzy i cztery bezpośrednio - myślę, że większość programów czasu używają syscalls, które zwracają nie używane w chwili obecnej deskryptory plików, ale czasami kod zapisuje do deskryptora pliku 3 bezpośrednio, myślę (mógłbym sobie wyobrazić program sprawdzający deskryptor pliku, czy jest otwarty i używający go, jeśli jest, lub zachowujący się inaczej, jeśli nie jest). A więc ten drugi prawdopodobnie najlepiej jest pamiętać i używać w przypadkach ogólnego przeznaczenia.

--- OUTDATED zawartości poniżej tej linii ---

Ze względów historycznych, tutaj jest moje oryginalne, nie-przenośny-to-wszystko-muszle odpowiedzieć:

[edit] Mój zły, to nie działa z bash, ponieważ bash wymaga dodatkowego rozpieszczania przy manipulowaniu deskryptorami plików, zaktualizuję to tak szybko, jak tylko będę mógł. [/ EDIT]

Czysta Bourne shell rozwiązanie:

exitstatus=`{ 3>&- command1; } 1>&3; printf $?` 3>&1 | command2 
# $exitstatus now has command1's exit status. 

Jest to podstawa, na której można budować swój "EET". Slap w pewnym analizowania argumentów wiersza poleceń, a wszystko to, skręcić command2 do „tee” z odpowiednimi opcjami itp

bardzo szczegółowe wyjaśnienie jest następujące:

Na najwyższym poziomie, oświadczenie jest tylko rura pomiędzy dwoma poleceniami:

commandA | command2 

commandA z kolei rozkłada się jednym poleceniu przekierowania pliku deskryptora 3 do deskryptora 1 (stdout):

commandB 3>&1 

Oznacza to, że powłoka będzie oczekiwała, że ​​komenda B napisze coś do deskryptora pliku 3 - jeśli deskryptor 3 pliku nigdy nie zostanie otwarty, byłby to błąd. Oznacza to również, że command2 dostanie cokolwiek commandB wyjścia na obydwu deskryptorów 1 (stdout) oraz 3.

commandB z kolei jest przypisanie zmiennej za pomocą podstawienia polecenia:

VAR_FOO=`commandC` 

Wiemy, że przypisania zmiennych nie drukuj cokolwiek na deskryptorach plików (a stdout commandC jest przechwytywany dla podstawienia), więc wiemy, że commandB jako całość nie wypisze niczego na standardowe wyjście. polecenie2 będzie więc tylko zobaczyć, co commandC zapisuje do pliku deskryptora 3.

I commandC to dwie komendy, gdzie druga komenda kod zakończenia pierwszego:

commandD ; printf $? 

Więc teraz wiemy, że przypisanie zmiennej w ostatnim kroku będzie zawierał status wyjścia commandD.

Teraz commandD rozkłada się do innego podstawowego przekierowania, z stdout danej comman do pliku deskryptora 3:

commandE 1>&3 

Więc teraz wiemy, że pisanie rzeczy do pliku deskryptora 3, a więc ostatecznie do polecenie2 jest COMMANDE użytkownika stdout.

Wreszcie: COMMANDE jest „komenda związek” (można również użyć podpowłoce tutaj, ale to nie jest tak wydajny), owijając się wokół innego mniej powszechnie postrzegane typu „przekierowania”:

{ 3>&- command1; } 

(To 3>&- jest nieco trudny, więc na koniec do niego wrócimy.) Tak więc złożone polecenia powodują, że średnik jest obowiązkowy, gdy ostatnie polecenie i ostatni nawias znajdują się na tej samej linii, dlatego właśnie to istnieje. Więc wiemy, że złożone komendy zwracają kod zakończenia ostatniego polecenia i dziedziczą deskryptory plików jak wszystko inne, więc teraz wiemy, że standardowe polecenie command1 wypływa z komendy złożonej, przekierowuje do deskryptora pliku 3, aby uniknąć przechwycenia przez podstawienie komendy w międzyczasie podstawienie komendy przechwytuje pozostały stdout printf, który odtwarza stan wyjścia polecenia 1 po zakończeniu.

A teraz za trudny bit: 3>&- mówi "zamknij deskryptor pliku 3". Możesz pomyśleć: "dlaczego zamykasz go, kiedy właśnie przekierowałeś do niego dane wyjściowe polecenia 1?" Cóż, jeśli przyjrzysz się uważnie, zobaczysz, że polecenia close only only command1 w komendzie złożonej (wewnątrz nawiasów klamrowych) w szczególności, podczas gdy przekierowanie wpływa na całe polecenie złożone.

Oto co się dzieje: do czasu uruchomienia poszczególnych poleceń komendy złożonej, otwarty deskryptor pliku powłoki 3. Procesy dziedziczą deskryptory plików, więc polecenie 1 domyślnie będzie działało z deskryptorem 3 pliku otwartym i wskazującym na to samo miejsce. Jest to złe, ponieważ czasami programy rzeczywiście oczekują określonych deskryptorów plików jako specjalnych rzeczy - mogą zachowywać się inaczej po uruchomieniu z otwartym 3 deskryptorem pliku. Najbardziej niezawodnym rozwiązaniem jest po prostu zamknięcie deskryptora 3 (lub dowolnej liczby) tylko dla polecenia 1, więc działa tak, jakby nigdy nie otwierano deskryptora 3 pliku.

Powiązane problemy