Dla wyjaśnienia - czy chcesz, aby stderr poszedł do ekranu, a także do pliku?
Charles Duffy
Zrobiłem to, edytuję swój post, aby to wyjaśnić. Wierzę, że rozwiązanie lhunatha wystarczy. Dzięki za pomoc wszystkim!
jparanich
Odpowiedzi:
784
Zakładam, że nadal chcesz zobaczyć STDERR i STDOUT na terminalu. Możesz skorzystać z odpowiedzi Josha Kelleya, ale uważam, że trzymanie się tailw tle, które powoduje, że twój plik dziennika jest bardzo hackish i cludgy. Zauważ, jak musisz zachować eks FD i zrobić później porządek, zabijając go, a technicznie powinno to być zrobione w trap '...' EXIT.
Nie ma lepszego sposobu, aby to zrobić, a już odkrył go: tee.
Tylko, zamiast używać go tylko do standardowego wyjścia, przygotuj koszulkę dla standardowej i jedną dla standardowej. Jak to osiągniesz? Podstawianie procesów i przekierowywanie plików:
command >>(tee -a stdout.log)2>>(tee -a stderr.log >&2)
Podzielmy to i wyjaśnijmy:
>>(..)
>(...)(podstawienie procesu) tworzy FIFO i pozwala teena nasłuchanie. Następnie używa >(przekierowanie pliku), aby przekierować STDOUT commanddo FIFO, którego teesłucha twój pierwszy .
To samo dotyczy drugiego:
2>>(tee -a stderr.log >&2)
Ponownie używamy podstawiania procesów, aby utworzyć teeproces, który odczytuje ze STDIN i wrzuca go do stderr.log. teewyprowadza swoje wejście z powrotem na STDOUT, ale ponieważ jego wejściem jest nasz STDERR, chcemy ponownie przekierować teeSTDOUT do naszego STDERR. Następnie używamy przekierowania pliku, aby przekierować commandSTDERR na wejście FIFO ( teeSTDIN).
Zastępowanie procesów jest jedną z tych naprawdę uroczych rzeczy, które dostajesz jako bonus wyboru bashjako powłoki zamiast w przeciwieństwie do sh(POSIX lub Bourne).
W sh, trzeba robić rzeczy ręcznie:
out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out""$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log <"$out"&
tee -a stderr.log <"$err">&2&
command >"$out"2>"$err"
Próbowałem tego: $ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)co działa, ale czeka na dane wejściowe. Czy istnieje prosty powód, dla którego tak się dzieje?
Justin
1
@SillyFreak Nie rozumiem, co chcesz robić ani jaki masz problem. test echa; Wyjście nie generuje żadnego wyjścia na standardowym wyjściu, więc err pozostanie pusty.
lhunath,
1
dzięki za ten komentarz; Zorientowałem się, jaki był potem mój błąd logiczny: po wywołaniu jako interaktywna powłoka, bash wypisuje wiersz polecenia i powtarza wyjście do stderr. Jednak jeśli stderr zostanie przekierowany, domyślnie bash zaczyna się jako nieinteraktywny; porównaj /bin/bash 2> erri/bin/bash -i 2> err
Silly Freak
15
A dla tych, którzy „widzą, wierzą”, szybki test:(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
To po prostu przekierowuje stderrdo stdout, więc tee echa zarówno do logowania, jak i do ekranu. Może coś mi brakuje, ponieważ niektóre inne rozwiązania wydają się bardzo skomplikowane.
Uwaga: Od wersji bash 4 możesz używać |&skrótu dla 2>&1 |:
Działa to dobrze, jeśli chcesz, aby zarówno stdout (kanał 1), jak i stderr (kanał 2) były zalogowane do tego samego pliku (pojedynczy plik zawierający mieszaninę zarówno stdout, jak i sterr). Drugie, bardziej skomplikowane rozwiązanie pozwala rozdzielić stdout i stderr na 2 różne pliki (odpowiednio stdout.log i stderr.log). Czasami jest to ważne, czasem nie.
Tyler Rick
19
Inne rozwiązania są w wielu przypadkach znacznie bardziej skomplikowane niż to konieczne. Ten działa dla mnie idealnie.
dkaminy
13
Problem z tą metodą polega na tym, że tracisz kod wyjścia / statusu z procesu aaa.sh, co może być ważne (np. Podczas używania w pliku makefile). Nie masz tego problemu z zaakceptowaną odpowiedzią.
Stefaan
9
jeśli nie masz nic przeciwko scaleniu stdout / stderr, to ./aaa.sh |& tee aaa.logdziała (w bash).
jfs
5
@Stefaan Wierzę, że możesz zachować status wyjścia, jeśli poprzedzisz łańcuch poleceń, a set -o pipefailpo nim ;lub &&jeśli się nie mylę.
David
59
Może to być przydatne dla osób, które znajdą to za pośrednictwem Google. Po prostu odkomentuj przykład, który chcesz wypróbować. Oczywiście możesz zmienić nazwę plików wyjściowych.
#!/bin/bash
STATUSFILE=x.out
LOGFILE=x.log
### All output to screen### Do nothing, this is the default### All Output to one file, nothing to the screen#exec > ${LOGFILE} 2>&1### All output to one file and all output to the screen#exec > >(tee ${LOGFILE}) 2>&1### All output to one file, STDOUT to the screen#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)### All output to one file, STDERR to the screen### Note you need both of these lines for this to work#exec 3>&1#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen#exec > ${STATUSFILE} 2>${LOGFILE}### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)### STDOUT to STATUSFILE and screen, STDERR to LOGFILE#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}### STDOUT to STATUSFILE, STDERR to LOGFILE and screen#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)
echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}
Nie, i sądzę, że exec może użyć jakiegoś wyjaśnienia. exec >oznacza, przenieś cel deskryptora pliku do określonego miejsca docelowego. Wartość domyślna to 1, więc exec > /dev/nullprzenosi wyjście stdout do / dev / null od teraz w tej sesji. Bieżące deskryptory plików dla tej sesji można zobaczyć wykonując ls -l /dev/fd/. Spróbuj! Następnie zobaczyć, co się dzieje, kiedy wydać exec 2>/tmp/stderr.log.Dodatkowo exec 3>&1środki, należy utworzyć nowy deskryptor pliku z numerem 3, i przekierować je do docelowego pliku deskryptora 1. Na przykład, cel był ekran, gdy polecenie zostało wydane.
ogień
Przykład pokazujący stdout i stderr zarówno na ekranie, jak i osobnych plikach jest niesamowity !!! Wielkie dzięki!
Marcello de Sales
21
Aby przekierować stderr do pliku, wyświetl stdout na ekranie, a także zapisz stdout do pliku:
./aaa.sh 2> ccc.out | tee ./bbb.out
EDYCJA : Aby wyświetlić stderr i stdout na ekranie, a także zapisać oba pliki, możesz użyć przekierowania We / Wy bash :
#!/bin/bash# Create a new file descriptor 4, pointed at the file# which will receive stderr.
exec 4<>ccc.out
# Also print the contents of this file to screen.
tail -f ccc.out &# Run the command; tee stdout as normal, and send stderr# to our file descriptor 4../aaa.sh 2>&4| tee bbb.out
# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1
Oczekuję, że użytkownik chce, aby stderr poszedł do swojej konsoli oprócz pliku, chociaż nie zostało to wyraźnie określone.
Charles Duffy
2
Powinienem był być bardziej zrozumiały, chciałem też stderr na ekranie. Nadal podobało mi się rozwiązanie Josha Kelleya, ale uważam, że lhunath bardziej odpowiada moim potrzebom. Dzięki chłopaki!
jparanich
18
Innymi słowy, chcesz połączyć stdout z jednym filtrem ( tee bbb.out), a stderr z innym filtrem ( tee ccc.out). Nie ma standardowego sposobu na podłączenie czegokolwiek innego niż standardowe wyjście do innego polecenia, ale można obejść ten problem, żonglując deskryptorami plików.
{{./aaa.sh | tee bbb.out;}2>&11>&3| tee ccc.out;}3>&11>&2
W bash (i ksh i zsh), ale nie w innych powłokach POSIX, takich jak dash, możesz użyć podstawiania procesów :
./aaa.sh >>(tee bbb.out)2>>(tee ccc.out)
Uważaj, że w bash to polecenie powraca natychmiast po ./aaa.shzakończeniu, nawet jeśli teepolecenia są nadal wykonywane (ksh i zsh czekają na podprocesy). Może to stanowić problem, jeśli robisz coś takiego ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out. W takim przypadku użyj żonglowania deskryptorem pliku lub ksh / zsh.
Wydaje się, że to jedyna odpowiedź, która pozwala zachować strumienie stdout / stderr bez zmian (np. Nie łącząc ich). Słodkie!
user1338062,
Najprostsze podejście sh, przydatne do zadań cron, w których podstawienie procesu nie jest dostępne.
Roger Dueck
13
Jeśli używasz bash:
# Redirect standard out and standard error separately% cmd >stdout-redirect 2>stderr-redirect
# Redirect standard error and out together% cmd >stdout-redirect 2>&1# Merge standard error with standard out and pipe% cmd 2>&1|cmd2
W moim przypadku skrypt wykonywał polecenie podczas przekierowywania zarówno stdout, jak i stderr do pliku, coś w stylu:
cmd > log 2>&1
Musiałem go zaktualizować tak, aby w przypadku awarii podjąć pewne działania w oparciu o komunikaty o błędach. Mógłbym oczywiście usunąć duplikat 2>&1i przechwycić stderr ze skryptu, ale wtedy komunikaty o błędach nie trafią do pliku dziennika w celach informacyjnych. Chociaż zaakceptowana odpowiedź @lhunath ma robić to samo, przekierowuje stdouti prowadzi stderrdo różnych plików, co nie jest tym, czego chcę, ale pomogło mi znaleźć dokładnie to, czego potrzebuję:
(cmd 2>>(tee /dev/stderr))> log
W związku z powyższym dziennik będzie zawierał kopię obu stdouti stderrmogę przechwytywać stderrz mojego skryptu bez obaw stdout.
Poniższe będzie działać dla KornShell (ksh), gdzie podstawienie procesu nie jest dostępne,
# create a combined(stdin and stdout) collector
exec 3<> combined.log
# stream stderr instead of stdout to tee, while draining all stdout to the collector./aaa.sh 2>&11>&3| tee -a stderr.log 1>&3# cleanup collector
exec 3>&-
Prawdziwy podstęp tutaj jest sekwencja z 2>&1 1>&3którym w naszym przypadku przekierowuje stderrdo stdouti przekierowuje stdoutdo deskryptora 3. W tym momencie stderri stdoutnie zostały jeszcze połączone.
W efekcie stderr(as stdin) jest przekazywane do miejsca, w teektórym się loguje, stderr.loga także przekierowuje do deskryptora 3.
A deskryptor 3rejestruje to combined.logcały czas. Więc combined.logzawiera zarówno stdouti stderr.
Odpowiedzi:
Zakładam, że nadal chcesz zobaczyć STDERR i STDOUT na terminalu. Możesz skorzystać z odpowiedzi Josha Kelleya, ale uważam, że trzymanie się
tail
w tle, które powoduje, że twój plik dziennika jest bardzo hackish i cludgy. Zauważ, jak musisz zachować eks FD i zrobić później porządek, zabijając go, a technicznie powinno to być zrobione wtrap '...' EXIT
.Nie ma lepszego sposobu, aby to zrobić, a już odkrył go:
tee
.Tylko, zamiast używać go tylko do standardowego wyjścia, przygotuj koszulkę dla standardowej i jedną dla standardowej. Jak to osiągniesz? Podstawianie procesów i przekierowywanie plików:
Podzielmy to i wyjaśnijmy:
>(...)
(podstawienie procesu) tworzy FIFO i pozwalatee
na nasłuchanie. Następnie używa>
(przekierowanie pliku), aby przekierować STDOUTcommand
do FIFO, któregotee
słucha twój pierwszy .To samo dotyczy drugiego:
Ponownie używamy podstawiania procesów, aby utworzyć
tee
proces, który odczytuje ze STDIN i wrzuca go dostderr.log
.tee
wyprowadza swoje wejście z powrotem na STDOUT, ale ponieważ jego wejściem jest nasz STDERR, chcemy ponownie przekierowaćtee
STDOUT do naszego STDERR. Następnie używamy przekierowania pliku, aby przekierowaćcommand
STDERR na wejście FIFO (tee
STDIN).Zobacz http://mywiki.wooledge.org/BashGuide/InputAndOutput
Zastępowanie procesów jest jedną z tych naprawdę uroczych rzeczy, które dostajesz jako bonus wyboru
bash
jako powłoki zamiast w przeciwieństwie dosh
(POSIX lub Bourne).W
sh
, trzeba robić rzeczy ręcznie:źródło
$ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)
co działa, ale czeka na dane wejściowe. Czy istnieje prosty powód, dla którego tak się dzieje?/bin/bash 2> err
i/bin/bash -i 2> err
(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
dlaczego nie po prostu:
To po prostu przekierowuje
stderr
dostdout
, więc tee echa zarówno do logowania, jak i do ekranu. Może coś mi brakuje, ponieważ niektóre inne rozwiązania wydają się bardzo skomplikowane.Uwaga: Od wersji bash 4 możesz używać
|&
skrótu dla2>&1 |
:źródło
./aaa.sh |& tee aaa.log
działa (w bash).set -o pipefail
po nim;
lub&&
jeśli się nie mylę.Może to być przydatne dla osób, które znajdą to za pośrednictwem Google. Po prostu odkomentuj przykład, który chcesz wypróbować. Oczywiście możesz zmienić nazwę plików wyjściowych.
źródło
exec >
oznacza, przenieś cel deskryptora pliku do określonego miejsca docelowego. Wartość domyślna to 1, więcexec > /dev/null
przenosi wyjście stdout do / dev / null od teraz w tej sesji. Bieżące deskryptory plików dla tej sesji można zobaczyć wykonującls -l /dev/fd/
. Spróbuj! Następnie zobaczyć, co się dzieje, kiedy wydaćexec 2>/tmp/stderr.log.
Dodatkowoexec 3>&1
środki, należy utworzyć nowy deskryptor pliku z numerem 3, i przekierować je do docelowego pliku deskryptora 1. Na przykład, cel był ekran, gdy polecenie zostało wydane.Aby przekierować stderr do pliku, wyświetl stdout na ekranie, a także zapisz stdout do pliku:
EDYCJA : Aby wyświetlić stderr i stdout na ekranie, a także zapisać oba pliki, możesz użyć przekierowania We / Wy bash :
źródło
Innymi słowy, chcesz połączyć stdout z jednym filtrem (
tee bbb.out
), a stderr z innym filtrem (tee ccc.out
). Nie ma standardowego sposobu na podłączenie czegokolwiek innego niż standardowe wyjście do innego polecenia, ale można obejść ten problem, żonglując deskryptorami plików.Zobacz także Jak grepować standardowy strumień błędów (stderr)? a kiedy użyjesz dodatkowego deskryptora pliku?
W bash (i ksh i zsh), ale nie w innych powłokach POSIX, takich jak dash, możesz użyć podstawiania procesów :
Uważaj, że w bash to polecenie powraca natychmiast po
./aaa.sh
zakończeniu, nawet jeślitee
polecenia są nadal wykonywane (ksh i zsh czekają na podprocesy). Może to stanowić problem, jeśli robisz coś takiego./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out
. W takim przypadku użyj żonglowania deskryptorem pliku lub ksh / zsh.źródło
sh
, przydatne do zadań cron, w których podstawienie procesu nie jest dostępne.Jeśli używasz bash:
Kredyt (brak odpowiedzi z góry mojej głowy) idzie tutaj: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html
źródło
W moim przypadku skrypt wykonywał polecenie podczas przekierowywania zarówno stdout, jak i stderr do pliku, coś w stylu:
Musiałem go zaktualizować tak, aby w przypadku awarii podjąć pewne działania w oparciu o komunikaty o błędach. Mógłbym oczywiście usunąć duplikat
2>&1
i przechwycić stderr ze skryptu, ale wtedy komunikaty o błędach nie trafią do pliku dziennika w celach informacyjnych. Chociaż zaakceptowana odpowiedź @lhunath ma robić to samo, przekierowujestdout
i prowadzistderr
do różnych plików, co nie jest tym, czego chcę, ale pomogło mi znaleźć dokładnie to, czego potrzebuję:W związku z powyższym dziennik będzie zawierał kopię obu
stdout
istderr
mogę przechwytywaćstderr
z mojego skryptu bez obawstdout
.źródło
Poniższe będzie działać dla KornShell (ksh), gdzie podstawienie procesu nie jest dostępne,
Prawdziwy podstęp tutaj jest sekwencja z
2>&1 1>&3
którym w naszym przypadku przekierowujestderr
dostdout
i przekierowujestdout
do deskryptora3
. W tym momenciestderr
istdout
nie zostały jeszcze połączone.W efekcie
stderr
(asstdin
) jest przekazywane do miejsca, wtee
którym się loguje,stderr.log
a także przekierowuje do deskryptora 3.A deskryptor
3
rejestruje tocombined.log
cały czas. Więccombined.log
zawiera zarównostdout
istderr
.źródło
Jeśli używasz zsh , możesz użyć wielu przekierowań, więc nawet nie potrzebujesz
tee
:Tutaj po prostu przekierowujesz każdy strumień do siebie i do pliku docelowego.
Pełny przykład
Zauważ, że wymaga to ustawienia
MULTIOS
opcji (która jest domyślna).źródło