Przekierowanie stderr i stdout w Bash
Chcę przekierować zarówno stdout, jak i stderr procesu do jednego pliku. Jak to zrobić w Bash?
13 answers
Spójrz tutaj . Powinno być:
yourcommand &>filename
(przekierowuje zarówno stdout
jak i stderr
do nazwy pliku).
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
2010-10-07 05:44:01
do_something 2>&1 | tee -a some_file
To przekieruje stderr na stdout i stdout na some_file
i wydrukuj go na 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
2015-10-27 10:33:02
Możesz przekierować stderr do stdout i stdout do pliku:
some_command >file.log 2>&1
Zobacz http://tldp.org/LDP/abs/html/io-redirection.html
Ten format jest preferowany niż najpopularniejszy & > format, który działa tylko w bash. W powłoce Bourne ' a można to zinterpretować jako uruchamianie polecenia w tle. Również format jest bardziej czytelny 2 (jest STDERR) przekierowany do 1 (STDOUT).
EDIT: zmieniono kolejność wskazaną w komentarze
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
2018-05-11 04:54:54
# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-
# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE
# Redirect STDERR to STDOUT
exec 2>&1
echo "This line will appear in $LOG_FILE, not 'on screen'"
Teraz proste echo zapisze do $LOG_FILE. Przydatne do demonizacji.
Do autora oryginalnego postu,
To zależy, co musisz osiągnąć. Jeśli potrzebujesz tylko przekierować w / z polecenia wywołanego ze skryptu, odpowiedzi są już podane. Mój dotyczy przekierowania wewnątrz bieżącego skryptu, który wpływa na wszystkie polecenia / wbudowane (zawiera forki) po wspomnianym fragmencie kodu.Kolejnym fajnym rozwiązaniem jest przekierowanie do obu std-err/out I do loggera lub pliku dziennika naraz, co wiąże się z podzieleniem "strumienia" na dwa. Ta funkcjonalność jest dostarczana przez polecenie 'tee', które może zapisywać / dołączać do kilku deskryptorów plików (plików, gniazd, rur, itp.) jednocześnie: TEE FILE1 FILE2... >(cmd1) >(cmd2)...
exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT
get_pids_of_ppid() {
local ppid="$1"
RETVAL=''
local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
RETVAL="$pids"
}
# Needed to kill processes running in background
cleanup() {
local current_pid element
local pids=( "$$" )
running_pids=("${pids[@]}")
while :; do
current_pid="${running_pids[0]}"
[ -z "$current_pid" ] && break
running_pids=("${running_pids[@]:1}")
get_pids_of_ppid $current_pid
local new_pids="$RETVAL"
[ -z "$new_pids" ] && continue
for element in $new_pids; do
running_pids+=("$element")
pids=("$element" "${pids[@]}")
done
done
kill ${pids[@]} 2>/dev/null
}
Od początku. Załóżmy, że mamy terminal podłączony do /dev /stdout(FD #1) i/dev / stderr(FD #2). W praktyce może to być rura, gniazdo lub cokolwiek innego.
- Stwórz FDs #3 i # 4 i punkt do tej samej "lokalizacji" co odpowiednio #1 i #2. Zmiana FD # 1 nie wpływa na FD #3 od teraz. Teraz FDs # 3 i #4 wskazują odpowiednio na STDOUT i STDERR. Będą one używane jako real terminal STDOUT i STDERR.
- 1> >(...) przekierowuje STDOUT do polecenia w parens
- parens(sub-shell) wykonuje odczyt 'tee' ze STDOUT (pipe) exec 'a i przekierowuje do polecenia' logger ' poprzez inny pipe do sub-shell w parens. W tym samym czasie kopiuje to samo wejście do FD #3 (terminal)
- druga część, bardzo podobna, jest o zrobieniu tej samej sztuczki dla STDERR i FDs #2 i # 4.
Wynik uruchomienia skryptu o powyższej linii i dodatkowo tej:
echo "Will end up in STDOUT(terminal) and /var/log/messages"
... jest następujące:
$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages
Jeśli chcesz zobaczyć wyraźniejszy obraz, dodaj te 2 linie do skryptu:
ls -l /proc/self/fd/
ps xf
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-10-27 16:58:10
bash your_script.sh 1>file.log 2>&1
1>file.log
poleca powłoce wysłanie STDOUT do pliku file.log
, a 2>&1
każe przekierować STDERR (deskryptor pliku 2) do STDOUT (deskryptor pliku 1).
Uwaga: kolejność ma znaczenie jako liw.fi 2>&1 1>file.log
nie działa.
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-12 09:33:48
Co ciekawe, to działa:
yourcommand &> filename
Ale to daje błąd składni:
yourcommand &>> filename
syntax error near unexpected token `>'
Musisz użyć:
yourcommand 1>> filename 2>&1
Krótka odpowiedź: Command >filename 2>&1
lub Command &>filename
Wyjaśnienie:
Rozważ następujący kod, który wyświetla słowo " stdout "na stdout i słowo" stderror " na stderror.
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror
Zauważ, że operator ' & ' mówi bashowi, że 2 jest deskryptorem pliku (który wskazuje na stderr), a nie nazwą pliku. Jeśli pominęliśmy'&', polecenie to wyświetli stdout
na stdout, utworzy plik o nazwie " 2 " i tam zapisze stderror
.
Eksperymentując z powyższym kodem, możesz możesz zobaczyć na własne oczy, jak działają operatorzy przekierowań. Na przykład, zmieniając plik, który z dwóch deskryptorów 1,2
zostanie przekierowany do /dev/null
, następujące dwie linie kodu usuwają odpowiednio wszystko ze stdout i wszystko ze stderror (wypisując to, co pozostało).
$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout
Teraz możemy wyjaśnić, dlaczego rozwiązanie, dlaczego poniższy kod nie generuje wyjścia:
(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1
Aby to naprawdę zrozumieć, Gorąco polecam przeczytać tę stronę w pliku tabele deskryptorów . Zakładając, że to czytałeś, możemy kontynuować. Zauważ, że Bash przetwarza od lewej do prawej; dlatego Bash widzi najpierw >/dev/null
(co jest takie samo jak 1>/dev/null
) i ustawia deskryptor pliku 1, aby wskazywał na /dev / null zamiast na standardowe wyjście. Po wykonaniu tego zadania Bash porusza się w prawo i widzi 2>&1
. To ustawia deskryptor pliku 2 , aby wskazywał na ten sam plik jako deskryptor pliku 1 (a nie na deskryptor pliku 1!!!! (zobacz ten zasób na wskaźnikach aby uzyskać więcej info)). Ponieważ deskryptor pliku 1 wskazuje na /dev /null, A deskryptor pliku 2 wskazuje na ten sam plik co deskryptor pliku 1, deskryptor pliku 2 wskazuje teraz również na/dev / null. Tak więc oba deskryptory plików wskazują na /dev / null i dlatego żadne wyjście nie jest renderowane.
Aby sprawdzić, czy naprawdę rozumiesz pojęcie, spróbuj odgadnąć wyjście, gdy zmienimy kolejność przekierowań:
(echo "stdout"; echo "stderror" >&2) 2>&1 >/dev/null
Stderror
Rozumowanie tutaj jest takie, że ocena od lewej do tak, Bash widzi 2> & 1, a więc ustawia deskryptor pliku 2 tak, aby wskazywał na to samo miejsce, co deskryptor pliku 1, czyli stdout. Następnie ustawia deskryptor pliku 1 (pamiętaj, że >/dev/null = 1 > /dev / null), aby wskazywał na >/dev/null, usuwając w ten sposób wszystko, co zwykle zostanie wysłane do standardowego wyjścia. Pozostaje nam więc tylko to, co nie zostało wysłane na stdout w podshell (kod w nawiasach) - tzn. "stderror".
Ciekawostką jest to, że mimo że 1 jest tylko wskaźnikiem na stdout przekierowanie wskaźnika 2 do 1 przez 2>&1
nie tworzy łańcucha wskaźników 2 - > 1 - > stdout. Gdyby tak się stało, w wyniku przekierowania 1 do /dev/null, kod 2>&1 >/dev/null
dałby łańcuch wskaźników 2 -> 1 - > /dev / null, a tym samym kod nie generowałby niczego, w przeciwieństwie do tego, co widzieliśmy powyżej.
Na koniec chciałbym zauważyć, że jest prostszy sposób, aby to zrobić:
Z sekcji 3.6.4 tutaj widzimy, że możemy użyć operatora &>
do przekierowania obu stdout i stderr. Tak więc, aby przekierować zarówno wyjście stderr jak i stdout dowolnego polecenia do \dev\null
(które usuwa wyjście), po prostu wpisujemy
$ command &> /dev/null
lub w przypadku mojego przykładu:
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null
Kluczowe terminy:
- deskryptory plików zachowują się jak wskaźniki (chociaż deskryptory plików nie są takie same jak wskaźniki plików)
- przekierowanie deskryptora pliku " a "na deskryptor pliku "b", który wskazuje na plik "f", powoduje, że deskryptor pliku "a" wskazuje na to samo miejsce co deskryptor pliku b-plik "f". Nie tworzy łańcucha wskaźników a -> b - > f
- Z powodu powyższego, porządek ma znaczenie,
2>&1 >/dev/null
jest !=>/dev/null 2>&1
. Jeden generuje wyjście, a drugi nie!
W końcu rzućcie okiem na te wspaniałe zasoby:]}
Dokumentacja Bash dotycząca przekierowań, Wyjaśnienie tabel deskryptorów plików, Wprowadzenie do wskaźników
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
2018-04-25 21:37:05
LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]"
exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )
Jest to związane: zapisywanie stdOut & stderr do syslog.
To prawie działa, ale nie z xinted ; (
Chciałem, aby wyjście ze stdout plus stderr było zapisane w pliku dziennika, a stderr nadal na konsoli. Musiałem więc zduplikować wyjście stderr przez tee.
Oto rozwiązanie, które znalazłem:
command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
- Pierwsza Zamiana stderr i stdout
- następnie dołącz stdout do pliku dziennika
- przeciągnij stderr do tee i dodaj go również do pliku dziennika
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-01-19 19:21:18
"najprostszy" sposób (tylko bash4): ls * 2>&- 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
2016-05-31 08:44:42
Następujące funkcje mogą być użyte do automatyzacji procesu przełączania wyjść beetwen stdout/stderr i pliku dziennika.
#!/bin/bash
#set -x
# global vars
OUTPUTS_REDIRECTED="false"
LOGFILE=/dev/stdout
# "private" function used by redirect_outputs_to_logfile()
function save_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
exec 3>&1
exec 4>&2
trap restore_standard_outputs EXIT
}
# Params: $1 => logfile to write to
function redirect_outputs_to_logfile {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
save_standard_outputs
exec 1>>${LOGFILE%.log}.log
exec 2>&1
OUTPUTS_REDIRECTED="true"
}
# "private" function used by save_standard_outputs()
function restore_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
exit 1;
fi
exec 1>&- #closes FD 1 (logfile)
exec 2>&- #closes FD 2 (logfile)
exec 2>&4 #restore stderr
exec 1>&3 #restore stdout
OUTPUTS_REDIRECTED="false"
}
Przykład użycia wewnątrz skryptu:
echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs
echo "this goes to 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
2018-07-26 20:49:40
Dla tcsh, muszę użyć następującego polecenia:
command >& file
Jeśli użyjesz command &> file
, wyświetli błąd "Invalid null command".
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-04-23 05:07:32
@fernando-fabreti
Dodając do tego, co zrobiłeś, zmieniłem nieco funkcje i usunąłem & - zamknięcie i działało dla mnie.
function saveStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
exec 3>&1
exec 4>&2
trap restoreStandardOutputs EXIT
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
}
# Params: $1 => logfile to write to
function redirectOutputsToLogfile {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
saveStandardOutputs
exec 1>>${LOGFILE}
exec 2>&1
OUTPUTS_REDIRECTED="true"
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
}
function restoreStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
exec 1>&3 #restore stdout
exec 2>&4 #restore stderr
OUTPUTS_REDIRECTED="false"
fi
}
LOGFILE_NAME="tmp/one.log"
OUTPUTS_REDIRECTED="false"
echo "this goes to stdout"
redirectOutputsToLogfile $LOGFILE_NAME
echo "this goes to logfile"
echo "${LOGFILE_NAME}"
restoreStandardOutputs
echo "After restore this goes to 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
2018-09-14 16:35:13