Jak stwierdzić, czy wyjście polecenia lub skryptu powłoki jest standardowe lub standardowe


32

Powiedzmy, że uruchamiam skrypt poleceń lub powłoki, co daje mi wynik. Nie znając wewnętrznych elementów tego polecenia lub skryptu powłoki, w jaki sposób można ustalić, czy dane wyjściowe pochodzą z stderrlub stdout?

Na przykład

$ ls -ld /
drwxrwxr-t  35 root  admin  1258 Dec 11 19:16 /

vs

ls -ld /test
ls: /test: No such file or directory

Jak sprawdzić, czy pierwsze polecenie zostało wydrukowane, stdouta drugie stderr(czy to zrobiło?)?


1
Jaki problem próbujesz tutaj rozwiązać?
Kenster

3
Bał się, że ktoś o to zapyta; nikt tak naprawdę, głównie ciekawy i mający nadzieję na lepsze zrozumienie przekierowania.
KM.

7
Możesz umieścić stderredw środowisku powłoki, LD_PRELOADaby uzyskać stdouti stderrw różnych kolorach. Oto powiązane pytanie w tym duchu.
Anko

Odpowiedzi:


18

Nie ma sposobu, aby stwierdzić, kiedy wydruk został już wydrukowany. W tym przypadku oba stdouti stderrsą podłączone do terminala, więc informacje, o których strumień został napisany został już utracone w momencie tekst pojawił się na terminalu; zostały połączone przez program, zanim dotarły do ​​terminalu.

Co można zrobić w takim przypadku jak wyżej, byłoby uruchomić komendę stdouti stderrprzekierowany do różnych miejsc i zobaczyć, co się dzieje. Lub uruchom go dwukrotnie, raz z stdoutprzekierowaniem do /dev/nulli raz z stderrprzekierowaniem do /dev/null, i sprawdź, który z tych przypadków powoduje wyświetlenie tekstu.

Można przekierować stdoutdo /dev/nullprzez sklejaniu >/dev/nullsię na końcu wiersza polecenia i można przekierować stderrdo /dev/nulldodając 2>/dev/null.


9

Możesz przekierować stdout za pomocą > filei przekierować stderr za pomocą 2> file. Wiele nowoczesnych powłok obsługuje przekierowanie do poleceń, dzięki czemu można użyć seddo podświetlenia, które dane wyjściowe pochodzą z którego strumienia:

$ ls 2> >(sed 's/^/2: /') > >(sed 's/^/1: /')
1: unity_support_test.0
1: vmwareDnD

$ ls foo 2> >(sed 's/^/2: /') > >(sed 's/^/1: /')
2: ls: cannot access foo: No such file or directory

Bardzo dobrze! Byłoby wspaniale, gdyby można było wyróżnić linie kolorami niż przedrostkami.
dotancohen

3
@dotancohen: One can! Np.(echo "this is stdout"; echo "this is stderr" >&2) > >(sed 's/.*/\x1b[32m&\x1b[0m/') 2> >(sed 's/.*/\x1b[31m&\x1b[0m/')
PM 2Ring

Zauważ, że jeśli istnieje wyjście mieszane z stdout i stderr, najpierw zostanie wydrukowane stdout, a następnie stderr.
nyuszika7h

5

annotate-outputSkrypt z Debianie devscriptspozwala to zrobić wybiórczo:

$ annotate-output ls -ld /test
14:54:22 -: Started ls -ld /test
14:54:22 E: ls: cannot access /test: No such file or directory
14:54:22 -: Finished with exitcode 2

Druga kolumna wskazuje stdout i stderr odpowiednio za pomocą Oi E.

Jest kilka zastrzeżeń, z których głównym jest to, co zaznaczono w innych odpowiedziach: po fakcie nie można tego zrobić. Ani powłoka, ani terminal nie są świadomi tego, w jaki sposób dowolny program używa swoich deskryptorów plików, chociaż powłoka jest odpowiedzialna za ich początkowe ustawienie.

Ta metoda korzysta z fifos, pisanie do fifo może zachowywać się inaczej niż pisanie do tty, a pisanie do dwóch różnych fifo jest zdecydowanie inne (potencjalne problemy z synchronizacją / przeplotem). Ponadto nie nadaje się do użytku interaktywnego, np. annotate-output bashNie jest świetnym planem, ale jest przydatny do wielu innych celów. Istnieje wiele przykładów skryptów i funkcji powłoki w odpowiedziach na pokrewne pytania dotyczące kolorowania stdin / stdout / stderr, najbardziej niezawodny jest stderrd, który wykorzystuje modyfikację (większość) programów uruchomieniowych do modyfikacji danych zapisanych do stderr.

To pytanie, do którego prowadzi Anko, ma dobre odpowiedzi na ten powiązany temat: pokolorowanie wyjścia stdout / stderr: Czy mogę skonfigurować moją powłokę do drukowania STDERR i STDOUT w różnych kolorach?


1
Zauważ, że jest to bashskrypt wykorzystujący while readpętle i uruchamiający jedno datepolecenie dla każdej linii stdout lub stderr, więc będzie o wiele mniej wydajny niż jego cmd > >(ts '%T O:') 2> >(ts '%T E:')odpowiednik.
Stéphane Chazelas,

1

Oprócz innych odpowiedzi wskazane jest /proc/$PID/fd(choć nie odpowiada na pytanie):

$ cat > /dev/null 2> /tmp/blablah &
[1] 3073

[1]+  Stopped                 cat > /dev/null 2> /tmp/blablah
$ ls -l /proc/3073/fd
total 0
lrwx------ 1 kampde kampde 64 Feb 24 11:43 0 -> /dev/pts/33
l-wx------ 1 kampde kampde 64 Feb 24 11:43 1 -> /dev/null
l-wx------ 1 kampde kampde 64 Feb 24 11:43 2 -> /tmp/blablah

Jak widać, tutaj można zobaczyć deskryptory plików otwarte dla procesu. 0jest STDIN, 1jest STDOUTi 2jest STDERR. Jeśli nie przekierowano STDOUT lub STDERR, zobaczysz /dev/pts/33(przynajmniej w tym przykładzie), ponieważ wskazywałyby na terminal.

uwagi : /proc/$PIDistnieje tylko dla uruchomionych procesów. W tym przypadku użyłem catbez argumentów, więc nie kończy się, dopóki nie zamknę STDIN. Wykonałem go również w tle, więc mam PID natychmiast ze względu na ten przykład.


0

Nie jest do końca jasne, o co pytasz, ale to może pomóc

ls -ld /
echo $?    # Exit status 0 returned because command executed successfully.

ls -ld /test
echo $?    # Non-zero exit status returned -- command failed to execute

Źródło

Jeśli kod zakończenia wynosi 0, oznacza to po prostu, że polecenie zostało wykonane poprawnie (standardowe wyjście), tutaj możesz znaleźć znaczenie, jeśli kod wyjścia jest inny niż 0 (standardowe)


Bardzo interesujące! Nigdy nie łączyłbym stanów wyjścia z przekierowaniem. Czy zero oznacza stdouti niezerowy stderr?
KM.

7
@ KM., Nr. Powiedziałbym, że silnie koreluje, ale nic nie stoi na przeszkodzie, aby program pisał stderri zwracał dobry kod błędu lub pisał stdouti zwracał zły kod błędu. W rzeczywistości spróbuj find /root- zakładając, że nie działasz jako root, powinieneś wydrukować 2 linie - zostanie wydrukowane polecenie „/ root” stdout, a polecenie „find: / root: odmowa uprawnień” zostanie wydrukowane stderr. I find zwraca zły kod powrotu.
godlygeek

Problemem op jest jasne, jak rozpoznać, które stdout lub stderr.
it_is_a_literature

0

Zwykle STDERR będzie miał nazwę programu dodaną do wiadomości dwukropkiem.

Przykład:

rpm -zq some_utils 
rpm: -zq: unknown option

Vs

rpm -ql some_utils 
package some_utils is not installed

-1

Aby przechwycić i przetestować dane wyjściowe błędu:

ls -l test 2>errors
if [ -s errors ]; then echo "There were errors:" && cat errors; fi
rm errors
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.