Jak zapisać stderr do pliku podczas używania" tee " z rurką?
Wiem jak użyć tee
do zapisu wyjścia (STDOUT
) z aaa.sh
do bbb.out
, jednocześnie wyświetlając go w terminalu:
./aaa.sh | tee bbb.out
Jak mam teraz napisać STDERR
do pliku o nazwie ccc.out
, mając go nadal wyświetlany?
10 answers
Zakładam, że nadal chcesz widzieć STDERR i STDOUT na terminalu. Możesz skorzystać z odpowiedzi Josha Kelleya, ale uważam, że trzymanie tail
wokół w tle, co sprawia, że Twój plik dziennika jest bardzo hakerski i grudkowy. Zauważ, jak należy zachować exra FD i zrobić sprzątanie po nim, zabijając go i technicznie powinieneś to robić w trap '...' EXIT
.
Jest na to lepszy sposób, a ty już to odkryłeś: tee
.
Tylko, zamiast po prostu używać go do swojego stdout, miej tee dla stdout i jeden dla stderr. Jak tego dokonasz? Podstawianie procesów i przekierowywanie plików:
command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
Podzielmy się i wyjaśnijmy:
> >(..)
>(...)
(process substitution) tworzy FIFO i pozwala tee
słuchać na nim. Następnie używa >
(przekierowanie plików), aby przekierować STDOUT z command
do FIFO, którego słucha twoja pierwsza tee
.
To samo dla drugiego:
2> >(tee -a stderr.log >&2)
Ponownie używamy podstawienia procesu, aby tee
proces, który odczytuje ze standardowego wejścia i wrzuca go do stderr.log
. tee
wysyła swoje wejście z powrotem na STDOUT, ale ponieważ jego wejście jest naszym STDERR, chcemy ponownie przekierować STDOUT tee
na nasz STDERR. Następnie używamy przekierowania plików, aby przekierować STDERR command
na wejście FIFO (tee
'S STDIN).
Zobacz http://mywiki.wooledge.org/BashGuide/InputAndOutput
Zastąpienie procesu jest jedną z tych naprawdę uroczych rzeczy, które dostajesz jako bonus wyboru bash
jako powłoki jako w przeciwieństwie do sh
(POSIX lub Bourne).
W sh
, musiałbyś 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"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-21 13:28:50
Dlaczego nie po prostu:
./aaa.sh 2>&1 | tee -a log
To po prostu przekierowuje stderr
do stdout
, więc tee wyświetla zarówno log, jak i screen. Może coś mi umyka, bo niektóre inne rozwiązania wydają się naprawdę skomplikowane.
Uwaga: od wersji bash 4 możesz używać |&
jako skrótu 2>&1 |
:
./aaa.sh |& tee -a log
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-13 11:42:20
Może to być przydatne dla osób, które znajdują to za pośrednictwem google. Po prostu odkomentuj przykład, który chcesz wypróbować. Oczywiście możesz zmienić nazwy 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}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-09 18:21:29
Aby przekierować stderr do pliku, wyświetl stdout na ekran, a także Zapisz stdout do pliku:
./aaa.sh 2>ccc.out | tee ./bbb.out
EDIT: aby wyświetlić zarówno stderr, jak i stdout na ekranie, a także zapisać oba do pliku, możesz użyć przekierowania I/O Basha:
#!/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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-28 03:40:18
Innymi słowy, chcesz podłączyć stdout do jednego filtra (tee bbb.out
) i stderr do innego filtra (tee ccc.out
). Nie ma standardowego sposobu przesyłania czegokolwiek innego niż stdout do innego polecenia, ale można to obejść żonglując deskryptorami plików.
{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2
Zobacz także Jak grep standard error stream (stderr)? i Kiedy można użyć dodatkowego deskryptora pliku?
W bash (oraz ksh i zsh), ale nie w innych powłokach POSIX, takich jak dash, można użyć podstawienie procesu :
./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)
Uważaj, że w bash polecenie to powraca natychmiast po zakończeniu ./aaa.sh
, nawet jeśli tee
polecenia są nadal wykonywane (ksh i zsh czekają na podprocesy). Może to być problem, jeśli zrobisz coś takiego jak ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out
. W takim przypadku zamiast tego użyj deskryptora pliku lub KSH / zsh.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:31
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
Kredyt (nie odpowiadam z czubka głowy) idzie tutaj: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-28 02:00:05
W moim przypadku skrypt uruchamiał polecenie podczas przekierowywania zarówno stdout, jak i stderr do pliku, coś w stylu:
cmd > log 2>&1
Musiałem go zaktualizować tak, że gdy wystąpi awaria, podjąć pewne działania w oparciu o komunikaty o błędach. Mógłbym oczywiście usunąć dup 2>&1
i przechwycić stderr ze skryptu, ale wtedy komunikaty o błędach nie trafią do pliku dziennika w celach informacyjnych. Podczas gdy zaakceptowana odpowiedź od @lhunath ma zrobić to samo, przekierowuje stdout
i stderr
na inne pliki, co nie jest tym, czego chcę, ale pomogło mi wymyślić dokładne rozwiązanie, którego potrzebuję:
(cmd 2> >(tee /dev/stderr)) > log
Z powyższego log będzie miał kopię stdout
i stderr
i mogę przechwycić stderr
z mojego skryptu bez martwienia się o stdout
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-31 12:41:38
Jeśli używasz zsh , możesz użyć wielu przekierowań, więc nawet nie potrzebujesz tee
:
./cmd 1>&1 2>&2 1>out_file 2>err_file
Tutaj po prostu przekierowujesz każdy strumień do samego siebie i pliku docelowego.
Pełny przykład
% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err
Należy pamiętać, że wymaga to ustawienia opcji MULTIOS
(która jest domyślna).
MULTIOS
Wykonywać implicit
tee
S lubcat
s przy próbie wielokrotnego przekierowania (zobacz przekierowanie ).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-27 18:42:02
W przypadku KornShell(ksh), gdzie zastępowanie procesu nie jest dostępne, należy wykonać następujące czynności.]}
# 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>&1 1>&3 | tee -a stderr.log 1>&3
# cleanup collector
exec 3>&-
Prawdziwą sztuczką jest sekwencja 2>&1 1>&3
, która w naszym przypadku przekierowuje stderr
na stdout
i przekierowuje stdout
na deskryptor 3
. W tym momencie stderr
i stdout
nie są jeszcze połączone.
W efekcie, stderr
(jako stdin
) jest przekazywany do tee
, gdzie loguje się do stderr.log
, a także przekierowuje do deskryptora 3.
I deskryptor 3
zapisuje go do Cały czas. Zatem combined.log
zawiera zarówno stdout
, jak i stderr
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-31 23:08:41
Podobnie jak przyjęta odpowiedź dobrze wyjaśniona przez lhunath , możesz użyć
command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
Uważaj, niż Jeśli używasz Basha, możesz mieć jakiś problem .
Pozwól mi wziąć matthew-wilcoxson exemple.
A dla tych, którzy "widzieć znaczy wierzyć", szybki test:]}(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
Osobiście, kiedy próbuję, mam taki wynik:
user@computer:~$ (echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
user@computer:~$ Test Out
Test Err
Obie wiadomości nie pojawiają się na tym samym poziomie. Why Test Out
seem to be site like if it is my previous dowództwo ?
Prompt jest na pustej linii, pozwól mi myśleć, że proces nie jest zakończony, a kiedy nacisnę {[10] } to to naprawić.
Kiedy sprawdzam zawartość plików, jest ok, przekierowanie działa.
function outerr() {
echo "out" # stdout
echo >&2 "err" # stderr
}
user@computer:~$ outerr
out
err
user@computer:~$ outerr >/dev/null
err
user@computer:~$ outerr 2>/dev/null
out
Próbuje ponownie przekierować, ale z tą funkcją.
function test_redirect() {
fout="stdout.log"
ferr="stderr.log"
echo "$ outerr"
(outerr) > >(tee "$fout") 2> >(tee "$ferr" >&2)
echo "# $fout content :"
cat "$fout"
echo "# $ferr content :"
cat "$ferr"
}
Osobiście mam taki wynik:
user@computer:~$ test_redirect
$ outerr
# stdout.log content :
out
out
err
# stderr.log content :
err
user@computer:~$
Brak zachęty na pustej linii, ale nie widzę normalnego wyjścia, stdout.zawartość dziennika wydaje się być błędna, tylko stderr./ align = "left" / ok. Jeśli go ponownie uruchomić, wyjście może być inne...
Więc dlaczego ?Ponieważ, jak wyjaśniono tutaj :
Uważaj, że w bash Komenda ta powraca natychmiast po zakończeniu [pierwsze polecenie], nawet jeśli polecenia tee są nadal wykonywane (KSH i zsh czekają na podprocesy)
Więc, jeśli używasz Basha, preferuj użycie lepszego exemple podanego w ta inna odpowiedź :
{ { outerr | tee "$fout"; } 2>&1 1>&3 | tee "$ferr"; } 3>&1 1>&2
Naprawi poprzednie problemy.
Teraz, pytanie brzmi, jak odzyskać kod statusu zakończenia ?$?
nie działa.
Nie znalazłem lepszego rozwiązania niż przełącznik na pipefail z set -o pipefail
(set +o pipefail
aby wyłączyć) i użyć ${PIPESTATUS[0]}
w ten sposób
function outerr() {
echo "out"
echo >&2 "err"
return 11
}
function test_outerr() {
local - # To preserve set option
! [[ -o pipefail ]] && set -o pipefail; # Or use second part directly
local fout="stdout.log"
local ferr="stderr.log"
echo "$ outerr"
{ { outerr | tee "$fout"; } 2>&1 1>&3 | tee "$ferr"; } 3>&1 1>&2
# First save the status or it will be lost
local status="${PIPESTATUS[0]}" # Save first, the second is 0, perhaps tee status code.
echo "==="
echo "# $fout content :"
echo "<==="
cat "$fout"
echo "===>"
echo "# $ferr content :"
echo "<==="
cat "$ferr"
echo "===>"
if (( status > 0 )); then
echo "Fail $status > 0"
return "$status" # or whatever
fi
}
user@computer:~$ test_outerr
$ outerr
err
out
===
# stdout.log content :
<===
out
===>
# stderr.log content :
<===
err
===>
Fail 11 > 0
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-12 11:12:20