Diferența dintre 2>&1 > output.log și 2>&1 | tee output.log (Unix, Shell, Redirecționare Io, Țeavă)

Chander Shivdasani a intrebat.

Am vrut să știu care este diferența dintre următoarele două comenzi

2>&1 > output.log 

și

2>&1 | tee output.log

Am văzut că unul dintre colegii mei folosește a doua opțiune pentru a redirecționa. Știu ce face 2>&1, singura mea întrebare este care este scopul utilizării tee atunci când se poate utiliza un simplu operator de redirecționare „>”?

7 răspunsuri
Kusalananda

Privind cele două comenzi separat:

utility 2>&1 >output.log 

Aici, deoarece redirecționările sunt procesate într-o manieră de la stânga la dreapta, fluxul de eroare standard ar fi redirecționat mai întâi acolo unde se duce fluxul de ieșire standard (eventual la consolă), iar apoi fluxul de ieșire standard ar fi redirecționat către un fișier. Fluxul de eroare standard ar fi nu nu va fi redirecționat către acel fișier.

Efectul vizibil al acestei situații ar fi acela că veți obține ceea ce se produce la eroarea standard pe ecran și ceea ce se produce la ieșirea standard în fișier.

utility 2>&1 | tee output.log

În acest caz, redirecționați eroarea standard în același loc cu fluxul de ieșire standard. Acest lucru înseamnă că ambele fluxuri vor fi direcționate către tee ca un singur flux de ieșire amestecat și că aceste date de ieșire standard vor fi salvate în fișierul dat de către tee. În plus, datele vor fi reproduse prin tee în consolă (aceasta este ceea ce tee duplică fluxurile de date).

Care dintre acestea este utilizat depinde de ceea ce doriți să obțineți.

Rețineți că nu ați putea reproduce efectul celui de-al doilea pipeline doar cu > (ca în utility >output.log 2>&1, , care ar salva atât ieșirea standard, cât și eroarea în fișier prin redirecționarea mai întâi a ieșirii standard către output.log fișier și apoi redirecționarea erorii standard către locul unde se duce acum ieșirea standard). Ar trebui să utilizați tee pentru a obține datele în consolă, precum și în fișierul de ieșire.


Note suplimentare:

La adresa visible efect al primei comenzi,

utility 2>&1 >output.log 

ar fi același cu

utility >output.log

Adică, ieșirea standard merge în fișier, iar eroarea standard merge în consolă.

Dacă la sfârșitul fiecăreia dintre comenzile de mai sus ar fi adăugată o etapă suplimentară de procesare, ar exista totuși o mare diferență:

utility 2>&1 >output.log | more_stuff

utility >output.log      | more_stuff

În prima linie de comandă, more_stuff ar primi ceea ce este inițial fluxul de eroare standard de la utility ca date de intrare standard, în timp ce în cea de-a doua conductă, întrucât doar fluxul de ieșire standard rezultat este trimis vreodată prin pipe, comanda more_stuff parte a conductei nu ar primi nimic de citit pe intrarea sa standard.

Comentarii

  • Cu comanda „utility 2>&1 | tee output.log, , vreți să spuneți că, din moment ce 1 este direcționat către tee, și 2 este la fel. Din moment ce tee dublează fluxul, ieșirea este atât afișată pe consolă, cât și scrisă în fișier? De aici rezultă diferența dintre utility 2>&1 > output.log și utility 2>&1 | tee output.log este tee prin faptul că acesta dublează fluxul. Ar fi corect? –  > Por Motivat.
  • Cu ajutorul exemplelor de utility 2>&1 > output.log | more_stuff și utility >ouput.log | more_stuff, is the difference that more_stuff` are ca intrare ieșirea de eroare standard în consolă more_stuff? Deoarece în cel de-al doilea exemplu nu există o ieșire în consolă, nu există, în esență, nici o intrare pentru more_stuff? Dacă da, acest lucru nu este clar, deoarece în paragraful anterior observați că ieșirea standard merge în fișier și eroarea standard merge în consolă. –  > Por Motivat.
  • @Motivat Primul dvs. comentariu mi se pare corect, da. În ceea ce privește al doilea comentariu: În prima comandă, more_stuff ar primi ceea ce utility a trimis inițial către aplicația sa fluxul de erori (dar care a fost redirecționat către ieșirea standard). Nu pentru că ar fi ajuns pe consolă dacă more_stuff nu ar fi fost acolo, ci pentru că se duce la fluxul de ieșire standard. În cea de-a doua comandă, more_stuff primește nimic deoarece nu există o ieșire standard din partea stângă a conductei. Fluxul de erori din utility ar ajunge în continuare pe consolă în cea de-a doua comandă. –  > Por Kusalananda.
  • Mulțumesc. Vreți să spuneți că, deoarece comanda utility > output.log | more_stuff nu are ca rezultat o ieșire în fluxul de ieșire standard din punctul de vedere al erorii standard? –  > Por Motivat.
  • @Motivated Deoarece partea stângă nu produce nimic pe ieșirea standard (este redirecționată), nu vor fi trimise date pe țeavă. –  > Por Kusalananda.
user14408

Notă editorială

Vă rugăm să vă asigurați că ați citit comentariile la acest răspuns – derobert.


Răspuns original

2>&1 >output.log înseamnă că mai întâi începeți să trimiteți toate chestiile din file handle 2 (eroare standard) către file handle 1 (ieșire standard) apoi trimiteți-le în fișierul output.log. Cu alte cuvinte, trimiteți eroarea standard și ieșirea standard la fișierul jurnal.

2>&1 | tee output.log este același lucru cu 2>&1 bit, acesta combină ieșirea standard și eroarea standard pe fluxul de ieșire standard. Apoi, le transmite prin intermediul fișierului tee care va trimite intrarea sa standard la ieșirea standard (ca și în cazul programului cat) și de asemenea, către fișier. Astfel, acesta combină cele două fluxuri (eroare și ieșire), apoi le transmite la terminal și la fișier.

Concluzia este că primul trimite stderr/stdout la fișier, în timp ce a doua trimite la ambele atât la fișier, cât și la ieșirea standard (care este probabil terminalul, cu excepția cazului în care vă aflați în interiorul unui alt construct care a redirecționat ieșirea standard).

Menționez această ultimă posibilitate pentru că poți avea lucruri de genul:

(echo hello | tee xyzzy.txt) >plugh.txt

în care nimic nu ajunge pe terminal.

Comentarii

  • -1 Aveți sintaxa corectă, dar nu și semantica. Rulați cat /doesnotexist 2>&1 >output.txt – veți vedea cat: /doesnotexist: No such file or directory afișat pe terminal, iar output.txt este un fișier gol. Ordinea de precedență și închiderea sunt în joc: 2>&1 (dup fd2 din curent fd1), apoi >output.txt (redirecționează fd1 către output.txt, fără a modifica nimic altceva). Motivul pentru care 2>&1 | este diferit este din cauza ordinii de precedență: | înainte de >. –  > Por Arcegeș.
  • Acest răspuns este fundamental greșit în esență din toate punctele de vedere. Multe dintre răspunsurile de mai jos sunt mai bune, dar cred că acesta al lui Kusalananda este cel mai clar. –  > Por Michael Homer.
  • @user14408: În cazul în care vă faceți vreodată un cont pe Unix & Linux și revendicați acest răspuns, vă rog să nu ezitați să eliminați nota mea editorială după ce ați abordat comentariile. –  > Por derobert.
osgx

Prima comandă va face o altă sarcină:

După

2>&1 > output.log 

vechiul STDOUT va fi salvat (copiat) în STDERR și apoi STDOUT va fi redirecționat către un fișier.

Astfel, stdout va merge în fișier, iar stderr va merge în consolă.

Și în

 2>&1 | tee output.log

ambele fluxuri vor fi redirecționate către tee. Tee va duplica orice intrare în stdout-ul său (consola în cazul dvs.) și în fișier (output.log).

Și mai există o altă formă de prima:

    > output.log  2>&1

aceasta va redirecționa atât STDOUT cât și STDERR către fișier.

André Caron

Primul dă ieșiri doar către fișier. A doua dă ieșire atât către fișier și pe ecran.

Arcege

Motivul pentru care 2>&1 | tee este acela de a putea captura atât stdout cât și stderr într-un fișier jurnal și de a le vedea pe ecran în același timp. Acest lucru ar putea fi realizat ca >output.txt 2>&1 & tail -f la fel de bine, dar nu ați ști când se termină comanda în fundal – este programul terminat sau rulează fără ieșire. Site-ul 2>&1 | tee era un idiom comun pentru programatori.

Comentarii

  • Vreți să spuneți că 2>&1 > file.txt, de exemplu, nu ar captura atât stdout, cât și stderr în file.txt? –  > Por Motivat.
Hari Perev

Să vedem mai întâi câteva exemple de cod:

#include <stdio.h>
main() 
{
// message 1, on stdout (using  printf)
printf("%s",          "message 1, on stdout (using  printf)
");

// message 2, on stdout (using fprintf)
fprintf(stdout, "%s", "message 2, on stdout (using fprintf)
");

// message 3, on stderr (using fprintf)
fprintf(stderr, "%s", "message 3, on stderr (using fprintf)
");
}

Să comparăm rezultatele:
./helloerror
+ fișier: niciun mesaj; consolă: mesaj 1,2,3;

./helloerror >error.txt
+ fișier: mesaj 1,2; consolă: mesaj 3;

./helloerror 2>&1 >error.txt
+ fișier: mesaj 1,2; consolă: mesaj 3;
+ la fel ca ./helloerror >error.txt

./helloerror >error.txt 2>&1
+ fișier: mesaj 3,1,2; consolă: niciun mesaj;
+ rețineți ordinea în care 3 este primul, apoi 1, apoi 2

./helloerror | tee error.txt 2>&1
+ file: message 1,2; console: message 3,1,2;
+ rețineți că ordinea 3 este prima, apoi 1, apoi 2

./helloerror 2>&1 | tee error.txt
+ file: message 3,1,2; console: message 3,1,2;

Pentru utilizare:
./helloerror >error.txt 2>&1
-> dacă se dorește ca toate mesajele (stdout+stderr) să fie în fișier, dar nu se afișează pe consolă

./helloerror 2>&1 | tee error.txt
-> dacă se doresc toate mesajele (stdout+stderr) în fișier și imprimate pe consolă

Abhishek Jain

Iată o postare care rezumă fluxurile de ieșire Unix: http://www.devcodenote.com/2015/04/unix-output-streams.html

Un fragment din această postare:

Există 3 fluxuri de ieșire standard:

STDIN - Standard Input - Writes from an input device to the program
STDOUT - Standard Output - Writes program output to screen unless specified otherwise.
STDERR - Standard Error Output - Writes error messages. Also printed to the screen unless specified otherwise.