Chciałbym powiedzieć, że jest to zaskakująco przydatne pytanie. Wiele osób nie wie, jak to zrobić, ponieważ nie muszą tak często robić i nie jest to najlepiej udokumentowane zachowanie Bash.
Robert Wm Ruedisueli
2
Czasami dobrze jest zobaczyć wynik (jak zwykle) ORAZ przekierować go do pliku. Zobacz odpowiedź Marko poniżej. (Mówię to tutaj, ponieważ łatwo jest spojrzeć na pierwszą zaakceptowaną odpowiedź, jeśli jest to wystarczające do rozwiązania problemu, ale inne odpowiedzi często dostarczają przydatnych informacji.)
Ta składnia jest przestarzała zgodnie z Wiki Bash Hackers . Czy to jest
Salman von Abbas,
20
Według wiki.bash-hackers.org/scripting/obsolete wydaje się, że jest przestarzały w tym sensie, że nie jest częścią POSIX, ale strona podręcznika użytkownika bash nie wspomina o usunięciu go z bash w najbliższej przyszłości. Strona man określa preferencje dla „&>” over ”> &”, które w przeciwnym razie są równoważne.
chepner
13
Myślę, że nie powinniśmy używać &>, ponieważ nie ma go w POSIX, a popularne powłoki takie jak „dash” nie obsługują go.
Sam Watkins,
27
Dodatkowa wskazówka: Jeśli używasz tego w skrypcie, upewnij się, że zaczyna się on #!/bin/bashod #!/bin/sh, ponieważ in wymaga bash.
Tor Klingberg
8
Lub & >>, aby dołączyć zamiast zastąpić.
Alexander Gonchiy
448
do_something 2>&1| tee -a some_file
Spowoduje to przekierowanie stderr do stdout i stdout do some_filei wydrukowanie go do stdout.
W systemie AIX (ksh) Twoje rozwiązanie działa. Akceptowana odpowiedź do_something &>filenamenie. +1.
Wstrzymano
11
@Daniel, ale to pytanie dotyczy konkretnie bash
John La Rooy,
3
Mam Ambiguous output redirect.pomysł, dlaczego?
Alexandre Holden Daly
1
Mam skrypt ruby (którego nie chcę w żaden sposób modyfikować), który wyświetla komunikaty o błędach pogrubioną czerwoną czcionką. Ten skrypt ruby jest następnie wywoływany z mojego skryptu bash (który mogę modyfikować). Kiedy używam powyższego, drukuje komunikaty o błędach w postaci zwykłego tekstu, pomijając formatowanie. Czy jest jakiś sposób, aby zachować formatowanie ekranowe i uzyskać wyjście (zarówno stdout, jak i stderr) również w pliku?
atlantis
8
Zauważ, że (domyślnie) ma to efekt uboczny, który $?nie odnosi się już do statusu wyjścia do_something, ale do statusu wyjścia tee.
Flimm,
255
Możesz przekierować stderr na stdout, a stdout do pliku:
Ten format jest preferowany niż najpopularniejszy &> format, który działa tylko w bash. W powłoce Bourne'a można to interpretować jako uruchamianie polecenia w tle. Również format jest bardziej czytelny 2 (jest STDERR) przekierowany na 1 (STDOUT).
EDYCJA: zmieniła kolejność, jak wskazano w komentarzach
To przekierowuje stderr do oryginalnego stdout, a nie do pliku, do którego zmierza stdout. Umieść „2> i 1” po „> file.log” i to działa.
1
Jaka jest zaleta tego podejścia w porównaniu z plikiem some_command &> file.log?
ubermonkey
6
Jeśli chcesz dołączyć do pliku, musisz to zrobić w ten sposób: echo „foo” 2> & 1 1 >> bar.txt AFAIK nie ma sposobu, aby dołączyć za pomocą &>
SlappyTheFish
9
Argh, przepraszam, echo „foo” 1 >> bar.txt 2> i 1
SlappyTheFish
11
Myślę, że interpretacja, że 2> i 1 przekierowuje stderr na stdout, jest błędna; Uważam, że dokładniej jest powiedzieć, że wysyła stderr w to samo miejsce, w którym aktualnie wybiera się stdout. Dlatego miejsce 2> i 1 po pierwszym przekierowaniu jest niezbędne.
jdg
201
# 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 się w $ LOG_FILE. Przydatny do demonizacji.
Do autora oryginalnego postu
To zależy od tego, co musisz osiągnąć. Jeśli musisz przekierować polecenie / polecenie z skryptu, odpowiedzi są już podane. Mój dotyczy przekierowania w ramach bieżącego skryptu, który wpływa na wszystkie polecenia / wbudowane (w tym widelce) po wspomnianym fragmencie kodu.
Innym fajnym rozwiązaniem jest przekierowanie zarówno do standardowego wyjścia / wyjścia ORAZ do programu rejestrującego lub pliku dziennika jednocześnie, co obejmuje podzielenie „strumienia” na dwa. Funkcję tę zapewnia komenda „tee”, która może zapisywać / dołączać jednocześnie do kilku deskryptorów plików (plików, gniazd, potoków itp.): Tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...
exec 3>&14>&21>>(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"]&&continuefor element in $new_pids;do
running_pids+=("$element")
pids=("$element""${pids[@]}")donedone
kill ${pids[@]}2>/dev/null
}
Tak więc od samego 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.
Utwórz FD 3 i 4 i wskaż tę samą „lokalizację”, co odpowiednio 1 i 2. Zmiana FD # 1 nie wpływa odtąd na FD # 3. Teraz FD 3 i 4 wskazują odpowiednio na STDOUT i STDERR. Będą one używane jako rzeczywisty terminal STDOUT i STDERR.
1>> (...) przekierowuje STDOUT na polecenie w parens
parens (podpowłoka) wykonuje odczyt „tee” z STDOUT exec (potok) i przekierowuje do polecenia „logger” za pomocą innego potoku do podpowłoki w parens. Jednocześnie kopiuje to samo wejście do FD # 3 (terminal)
druga część, bardzo podobna, dotyczy wykonania tej samej sztuczki dla STDERR i FD # 2 i # 4.
Wynik uruchomienia skryptu o powyższej linii i dodatkowo tej:
echo "Will end up in STDOUT(terminal) and /var/log/messages"
...następująco:
$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep2315: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 wiersze do skryptu:
tylko jeden wyjątek. w pierwszym przykładzie napisałeś: exec 1 <> $ LOG_FILE. powoduje to, że oryginalny plik dziennika jest zawsze własnością. dla prawdziwego logowania lepszy sposób to: exec 1 >> $ PLIK_LOGU, ponieważ dziennik jest zawsze dołączany.
Znik
4
To prawda, choć zależy to od intencji. Moje podejście polega na tym, aby zawsze tworzyć unikalny plik dziennika ze znacznikiem czasu. Drugim jest dołączenie. Oba sposoby można „logrotować”. Wolę osobne pliki, które wymagają mniejszego analizowania, ale jak powiedziałem, cokolwiek sprawia, że twoja łódź pływa :)
quizac
1
Twoje drugie rozwiązanie ma charakter informacyjny, ale co z całym kodem czyszczenia? Nie wydaje się to istotne, a jeśli tak, to tylko zamazuje dobry przykład. Chciałbym również, aby został on nieco przerobiony, tak aby FD 1 i 2 nie były przekierowywane do programu rejestrującego, ale raczej 3 i 4, aby wszystko, co wywołuje ten skrypt, mogło manipulować 1 i 2 dalej przy wspólnym założeniu stdout == 1 i stderr == 2, ale moje krótkie eksperymenty sugerują, że jest to bardziej złożone.
JFlo
1
Bardziej podoba mi się to z kodem czyszczenia. Może to być trochę odwrócenie uwagi od podstawowego przykładu, ale usunięcie go spowoduje, że przykład będzie niekompletny. Sieć jest już pełna przykładów bez obsługi błędów lub przynajmniej przyjazną notatkę, że nadal potrzebuje około stu linii kodu, aby korzystanie z niej było bezpieczne.
Zoltan K.,
1
Chciałem rozwinąć kod czyszczenia. Jest to część skryptu, który demonizuje ergo, staje się odporny na sygnał HANG-UP. „tee” i „logger” są procesami spawnowanymi przez ten sam PPID i dziedziczą pułapkę HUP z głównego skryptu bash. Kiedy główny proces umiera, zostają odziedziczone przez init [1]. Nie staną się zombie (defunc). Kod czyszczenia zapewnia, że wszystkie zadania w tle zostaną zabite, jeśli główny skrypt umrze. Dotyczy to również każdego innego procesu, który mógł zostać utworzony i działał w tle.
quiz z
41
bash your_script.sh 1>file.log 2>&1
1>file.loginstruuje powłokę, aby wysłała STDOUT do pliku file.logi 2>&1każe przekierować STDERR (deskryptor pliku 2) do STDOUT (deskryptor pliku 1).
Uwaga: kolejność ma znaczenie, jak wskazano liw.fi, 2>&1 1>file.lognie działa.
Zauważ, że operator „&” mówi bashowi, że 2 to deskryptor pliku (który wskazuje na stderr), a nie nazwa pliku. Jeśli pominiemy „&”, to polecenie wypisuje stdoutna standardowe wyjście, tworzy plik o nazwie „2” i stderrortam zapisuje .
Eksperymentując z powyższym kodem, możesz przekonać się dokładnie, jak działają operatory przekierowania. Na przykład, zmieniając plik, który z dwóch deskryptorów 1,2zostanie przekierowany do /dev/nullnastępujących dwóch wierszy kodu, usuń wszystko odpowiednio ze standardowego wyjścia, a wszystko odpowiednio ze standardowego błędu (wypisywanie pozostałych).
Aby naprawdę to zrozumieć, zdecydowanie zalecamy przeczytanie tej strony w tabelach deskryptorów plików . Zakładając, że zrobiłeś to czytanie, możemy kontynuować. Zauważ, że Bash przetwarza od lewej do prawej; w ten sposób Bash widzi >/dev/nullpierwszy (który jest taki sam jak 1>/dev/null) i ustawia deskryptor pliku 1 tak, aby wskazywał / dev / null zamiast standardowego wyjścia. Po zrobieniu tego Bash następnie porusza się w prawo i widzi 2>&1. Ustawia to deskryptor pliku 2 tak, aby wskazywał na ten sam plik co deskryptor pliku 1 (a nie na sam deskryptor pliku 1 !!!! (zobacz ten zasób na temat wskaźnikówpo więcej informacji)) . Ponieważ deskryptor pliku 1 wskazuje na / dev / null, a deskryptor pliku 2 wskazuje na ten sam plik co deskryptor pliku 1, deskryptor pliku 2 teraz wskazuje również na / dev / null. Zatem 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 zgadnąć dane wyjściowe, gdy zmienimy kolejność przekierowań:
(echo "stdout"; echo "stderror">&2)2>&1>/dev/null
stderror
Powodem jest to, że oceniając od lewej do prawej, Bash widzi 2> i 1, a tym samym ustawia deskryptor pliku 2 tak, aby wskazywał to samo miejsce, co deskryptor pliku 1, tj. Stdout. Następnie ustawia deskryptor pliku 1 (pamiętaj, że> / dev / null = 1> / dev / null) wskazuje na> / dev / null, usuwając w ten sposób wszystko, co zwykle byłoby wysyłane do standardowego wyjścia. Zatem pozostaje nam tylko to, co nie zostało wysłane do standardowego wyjścia w podpowłoce (kod w nawiasach) - tzn. „Stderror”. Interesującą rzeczą do odnotowania jest to, że chociaż 1 jest tylko wskaźnikiem do standardowego wyjścia, przekierowanie wskaźnika 2 na 1 przez2>&1 NIE tworzy łańcucha wskaźników 2 -> 1 -> standardowego. Jeśli tak, w wyniku przekierowania 1 do / dev / null, kod2>&1 >/dev/null dałby łańcuch wskaźnika 2 -> 1 -> / dev / null, a zatem kod nie wygenerowałby nic, w przeciwieństwie do tego, co widzieliśmy powyżej.
Na koniec zauważę, że istnieje prostszy sposób:
W sekcji 3.6.4 tutaj widzimy, że możemy użyć operatora &>do przekierowania zarówno stdout, jak i stderr. Tak więc, aby przekierować zarówno wyjście stderr, jak i wyjście standardowe na dowolne polecenie \dev\null(które usuwa dane wyjściowe), po prostu wpiszemy
$ command &> /dev/null
lub w przypadku mojego przykładu:
$ (echo "stdout"; echo "stderror">&2)&>/dev/null
Najważniejsze dania na wynos:
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” do deskryptora pliku „b”, który wskazuje na plik „f”, powoduje, że deskryptor pliku „a” wskazuje to samo miejsce, co deskryptor pliku b - plik „f”. NIE tworzy łańcucha wskaźników a -> b -> f
Z tego powodu kolejność ma znaczenie, 2>&1 >/dev/nulljest! = >/dev/null 2>&1. Jedno generuje wyjście, a drugie nie!
Deskryptory plików (0, 1, 2) są po prostu przesunięciami do tabeli. Gdy używane są 2> i 1, efektem jest szczelina FD [2] = dup (1), więc gdziekolwiek FD [1] wskazywał FD [2], teraz wskazuje. Kiedy zmienisz FD [1], aby wskazywał na / dev / null, wtedy FD [1] ulega zmianie, ale nie zmienia to gniazda FD [2] (który wskazuje na standardowe wyjście). Używam terminu dup (), ponieważ jest to wywołanie systemowe używane do kopiowania deskryptora pliku.
Zgaduję, że to nie działa z powodu „/ dev / fd / 3 Odmowa dostępu”. Zmiana na> i 3 może pomóc.
quizac
6
Chciałem, aby rozwiązaniem było zapisanie danych wyjściowych ze stdout plus stderr w pliku dziennika, a stderr wciąż na konsoli. Więc musiałem zduplikować wyjście stderr przez tee.
Oto rozwiązanie, które znalazłem:
command 3>&11>&22>&31>>logfile | tee -a logfile
Najpierw zamień stderr i stdout
następnie dołącz stdout do pliku dziennika
potoku rury do trójnika i dołączyć go również do pliku dziennika
BTW, to nie działało dla mnie (plik dziennika jest pusty). | tee nie ma wpływu. Zamiast tego uruchomiłem go za pomocą stackoverflow.com/questions/692000/…
Jarosław Bułatow
4
W sytuacji, gdy konieczne jest „orurowanie”, możesz użyć:
| I
Na przykład:
echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt
lub
TIMEFORMAT=%R;for i in`seq 1 20`;do time kubectl get pods |grep node >>js.log ;done|& sort -h
Te rozwiązania bazujące na bash mogą przesyłać STDOUT i STDERR osobno (od STDERR „sort -c” lub od STDERR do „sort-h”).
Poniższych funkcji można użyć do zautomatyzowania procesu przełączania wyjść między stdout / stderr i plikiem 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 tofunction 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]"fiif[!-f $LOGFILE ];then
touch $LOGFILE
fiif[!-f $LOGFILE ];then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1fi
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 w skrypcie:
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"
kiedy używam twoich funkcji i próbuje przywrócić standardowe dane wyjściowe, pojawia się echo: błąd zapisu: zły numer pliku przekierowanie działa idealnie ... przywracanie nie wydaje się
thom schumacher
aby skrypt mógł działać, musiałem skomentować te wiersze i zmieniłem kolejność: #exec 1> & - #closes FD 1 (plik dziennika) #exec 2> & - #closes FD 2 (plik dziennika); exec 1> i 3 #restore stdout exec 2> & 4
#restore
Przykro mi to słyszeć. Nie otrzymuję żadnych błędów podczas uruchamiania w CentOS 7, bash 4.2.46. Zanotowałem odniesienie, w którym otrzymałem te polecenia. Jest: Ref: logan.tw/posts/2016/02/20/open-and-close-files-in-bash
Fernando Fabreti
Korzystam z tych poleceń w systemie AIX i prawdopodobnie dlatego. Dodałem post dotyczący poprawki, którą zrobiłem.
thom schumacher
1
@ fernando-fabreti
Dodając do tego, co zrobiłeś, nieznacznie zmieniłem funkcje i usunąłem & - zamknięcie i zadziałało to 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 tofunction redirectOutputsToLogfile {if["$OUTPUTS_REDIRECTED"=="false"];then
LOGFILE=$1
if[-z "$LOGFILE"];then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"fiif[!-f $LOGFILE ];then
touch $LOGFILE
fiif[!-f $LOGFILE ];then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1fi
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"
W sytuacjach, gdy rozważasz użycie takich rzeczy exec 2>&1, łatwiej jest odczytać, jeśli to możliwe, przepisywanie kodu za pomocą funkcji bash takich jak to:
Odpowiedzi:
Spójrz tutaj . Powinien być:
(przekierowuje zarówno nazwę pliku, jak
stdout
istderr
nazwę pliku).źródło
#!/bin/bash
od#!/bin/sh
, ponieważ in wymaga bash.Spowoduje to przekierowanie stderr do stdout i stdout do
some_file
i wydrukowanie go do stdout.źródło
do_something &>filename
nie. +1.Ambiguous output redirect.
pomysł, dlaczego?$?
nie odnosi się już do statusu wyjściado_something
, ale do statusu wyjściatee
.Możesz przekierować stderr na stdout, a stdout do pliku:
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 interpretować jako uruchamianie polecenia w tle. Również format jest bardziej czytelny 2 (jest STDERR) przekierowany na 1 (STDOUT).
EDYCJA: zmieniła kolejność, jak wskazano w komentarzach
źródło
Teraz proste echo zapisze się w $ LOG_FILE. Przydatny do demonizacji.
Do autora oryginalnego postu
To zależy od tego, co musisz osiągnąć. Jeśli musisz przekierować polecenie / polecenie z skryptu, odpowiedzi są już podane. Mój dotyczy przekierowania w ramach bieżącego skryptu, który wpływa na wszystkie polecenia / wbudowane (w tym widelce) po wspomnianym fragmencie kodu.
Innym fajnym rozwiązaniem jest przekierowanie zarówno do standardowego wyjścia / wyjścia ORAZ do programu rejestrującego lub pliku dziennika jednocześnie, co obejmuje podzielenie „strumienia” na dwa. Funkcję tę zapewnia komenda „tee”, która może zapisywać / dołączać jednocześnie do kilku deskryptorów plików (plików, gniazd, potoków itp.): Tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...
Tak więc od samego 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.
Wynik uruchomienia skryptu o powyższej linii i dodatkowo tej:
...następująco:
Jeśli chcesz zobaczyć wyraźniejszy obraz, dodaj te 2 wiersze do skryptu:
źródło
1>file.log
instruuje powłokę, aby wysłała STDOUT do plikufile.log
i2>&1
każe przekierować STDERR (deskryptor pliku 2) do STDOUT (deskryptor pliku 1).Uwaga: kolejność ma znaczenie, jak wskazano liw.fi,
2>&1 1>file.log
nie działa.źródło
Co ciekawe, działa to:
Ale to powoduje błąd składniowy:
Musisz użyć:
źródło
&>>
wydaje się działać na BASH 4:$ echo $BASH_VERSION 4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
Krótka odpowiedź:
Command >filename 2>&1
lubCommand &>filename
Wyjaśnienie:
Rozważ następujący kod, który wypisuje słowo „stdout” na stdout, a słowo „stderror” na stderror.
Zauważ, że operator „&” mówi bashowi, że 2 to deskryptor pliku (który wskazuje na stderr), a nie nazwa pliku. Jeśli pominiemy „&”, to polecenie wypisuje
stdout
na standardowe wyjście, tworzy plik o nazwie „2” istderror
tam zapisuje .Eksperymentując z powyższym kodem, możesz przekonać się dokładnie, jak działają operatory przekierowania. Na przykład, zmieniając plik, który z dwóch deskryptorów
1,2
zostanie przekierowany do/dev/null
następujących dwóch wierszy kodu, usuń wszystko odpowiednio ze standardowego wyjścia, a wszystko odpowiednio ze standardowego błędu (wypisywanie pozostałych).Teraz możemy wyjaśnić, dlaczego rozwiązanie, dlaczego poniższy kod nie generuje danych wyjściowych:
Aby naprawdę to zrozumieć, zdecydowanie zalecamy przeczytanie tej strony w tabelach deskryptorów plików . Zakładając, że zrobiłeś to czytanie, możemy kontynuować. Zauważ, że Bash przetwarza od lewej do prawej; w ten sposób Bash widzi
>/dev/null
pierwszy (który jest taki sam jak1>/dev/null
) i ustawia deskryptor pliku 1 tak, aby wskazywał / dev / null zamiast standardowego wyjścia. Po zrobieniu tego Bash następnie porusza się w prawo i widzi2>&1
. Ustawia to deskryptor pliku 2 tak, aby wskazywał na ten sam plik co deskryptor pliku 1 (a nie na sam deskryptor pliku 1 !!!! (zobacz ten zasób na temat wskaźnikówpo więcej informacji)) . Ponieważ deskryptor pliku 1 wskazuje na / dev / null, a deskryptor pliku 2 wskazuje na ten sam plik co deskryptor pliku 1, deskryptor pliku 2 teraz wskazuje również na / dev / null. Zatem 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 zgadnąć dane wyjściowe, gdy zmienimy kolejność przekierowań:
Powodem jest to, że oceniając od lewej do prawej, Bash widzi 2> i 1, a tym samym ustawia deskryptor pliku 2 tak, aby wskazywał to samo miejsce, co deskryptor pliku 1, tj. Stdout. Następnie ustawia deskryptor pliku 1 (pamiętaj, że> / dev / null = 1> / dev / null) wskazuje na> / dev / null, usuwając w ten sposób wszystko, co zwykle byłoby wysyłane do standardowego wyjścia. Zatem pozostaje nam tylko to, co nie zostało wysłane do standardowego wyjścia w podpowłoce (kod w nawiasach) - tzn. „Stderror”. Interesującą rzeczą do odnotowania jest to, że chociaż 1 jest tylko wskaźnikiem do standardowego wyjścia, przekierowanie wskaźnika 2 na 1 przez
2>&1
NIE tworzy łańcucha wskaźników 2 -> 1 -> standardowego. Jeśli tak, w wyniku przekierowania 1 do / dev / null, kod2>&1 >/dev/null
dałby łańcuch wskaźnika 2 -> 1 -> / dev / null, a zatem kod nie wygenerowałby nic, w przeciwieństwie do tego, co widzieliśmy powyżej.Na koniec zauważę, że istnieje prostszy sposób:
W sekcji 3.6.4 tutaj widzimy, że możemy użyć operatora
&>
do przekierowania zarówno stdout, jak i stderr. Tak więc, aby przekierować zarówno wyjście stderr, jak i wyjście standardowe na dowolne polecenie\dev\null
(które usuwa dane wyjściowe), po prostu wpiszemy$ command &> /dev/null
lub w przypadku mojego przykładu:Najważniejsze dania na wynos:
2>&1 >/dev/null
jest! =>/dev/null 2>&1
. Jedno generuje wyjście, a drugie nie!Na koniec spójrz na te wspaniałe zasoby:
Dokumentacja Bash na temat przekierowań , objaśnienie tabel deskryptorów plików , wprowadzenie do wskaźników
źródło
Jest to powiązane: Pisanie stdOut i stderr do syslog.
To prawie działa, ale nie z Xinted; (
źródło
Chciałem, aby rozwiązaniem było zapisanie danych wyjściowych ze stdout plus stderr w pliku dziennika, a stderr wciąż na konsoli. Więc musiałem zduplikować wyjście stderr przez tee.
Oto rozwiązanie, które znalazłem:
źródło
W sytuacji, gdy konieczne jest „orurowanie”, możesz użyć:
Na przykład:
lub
Te rozwiązania bazujące na bash mogą przesyłać STDOUT i STDERR osobno (od STDERR „sort -c” lub od STDERR do „sort-h”).
źródło
„Najprostszym” sposób (tylko bash4)
ls * 2>&- 1>&-
.źródło
Poniższych funkcji można użyć do zautomatyzowania procesu przełączania wyjść między stdout / stderr i plikiem dziennika.
Przykład użycia w skrypcie:
źródło
@ fernando-fabreti
Dodając do tego, co zrobiłeś, nieznacznie zmieniłem funkcje i usunąłem & - zamknięcie i zadziałało to dla mnie.
źródło
W sytuacjach, gdy rozważasz użycie takich rzeczy
exec 2>&1
, łatwiej jest odczytać, jeśli to możliwe, przepisywanie kodu za pomocą funkcji bash takich jak to:źródło
W przypadku tcsh muszę użyć następującego polecenia:
Jeśli zostanie użyty
command &> file
, pojawi się błąd „Niepoprawne polecenie zerowe”.źródło