Dlaczego dane wyjściowe niektórych programów systemu Linux nie są wysyłane do STDOUT ani STDERR?

21

Dlaczego dane wyjściowe niektórych programów systemu Linux nie są wysyłane do STDOUT ani STDERR?

Właściwie chcę wiedzieć, jak niezawodnie przechwytywać wszystkie dane wyjściowe programu, bez względu na to, jakiego „strumienia” używa. Problemem jest to, że niektóre programy nie pozwalają na przechwycenie ich wyników.

Przykładem jest polecenie „czas”:

time sleep 1 2>&1 > /dev/null

real        0m1.003s
user        0m0.000s
sys         0m0.000s

lub

time sleep 1 &> /dev/null

real        0m1.003s
user        0m0.000s
sys         0m0.000s

Dlaczego widzę dane wyjściowe za każdym razem? Spodziewałem się, że wszystko to zostanie przekazane do / dev / null .

Jakiego strumienia wyjściowego używa czas i jak mogę go potokować do pliku?

Jednym ze sposobów obejścia tego problemu jest utworzenie skryptu Bash , na przykład combine.shzawierającego to polecenie:

$@ 2>&1

Następnie wynik „czasu” można zarejestrować w prawidłowy sposób:

combine.sh time sleep 1 &> /dev/null

(nie widać danych wyjściowych - poprawnie)

Czy istnieje sposób na osiągnięcie tego, co chcę bez użycia osobnego skryptu łączenia?

Will Sheppard
źródło
3
najpierw należy odwrócić kolejność: 2>&1 > /dev/nullśrodki „2 teraz idzie gdzie 1 idzie (czyli terminal domyślnie), a następnie 1 teraz jedzie do / dev / null (ale 2 wciąż idzie do zacisku!) używać. >/dev/null 2>&1powiedzieć „1 idzie teraz do / dev / null, a następnie 2 idzie tam, gdzie 1 idzie (tj. Również do / dev / null). To nadal nie działa tutaj, ponieważ wbudowany „czas” nie zostanie przekierowany, ale ogólnie jest poprawny (na przykład działałoby, jeśli użyjesz / usr / bin / time). Pomyśl o „2> & 1” jako kierunek kopiowania „na” 1 za 2, a nie 2 zamiar 1
Olivier Dulac

Odpowiedzi:

38

To pytanie zostało rozwiązane w BashFAQ / 032 . W twoim przykładzie:

{ time sleep 1; } 2> /dev/null

Powód dlaczego

time sleep 1 2>/dev/null

nie zachowuje się tak, jak się spodziewasz, ponieważ dzięki tej składni będziesz chciał timepolecenia sleep 1 2>/dev/null(tak, polecenie sleep 1z przekierowaniem do stderr/dev/null ). Wbudowane timedziała w ten sposób, aby to faktycznie było możliwe.

bash Wbudowane w rzeczywistości może to zrobić, ponieważ ... dobrze, to wbudowane. Takie zachowanie byłoby niemożliwe w przypadku timezwykle umieszczonego polecenia zewnętrznego /usr/bin. W rzeczy samej:

$ /usr/bin/time sleep 1 2>/dev/null
$

Teraz odpowiedź na twoje pytanie

Dlaczego dane wyjściowe niektórych programów linuksowych nie są wysyłane do STDOUT ani STDERR?

jest: tak, wyjście przechodzi do stdout lub stderr .

Mam nadzieję że to pomoże!

gniourf_gniourf
źródło
2
można utworzyć inne fd i mieć polecenia jawnie przejść do tych (ex: w bash skryptu: exec 3>/some/file ; ls >&3 ;)
Olivier Dulac
@OlivierDulac Pewnie, a nawet prościej z coprocwbudowanym. Ale nie jest tak w przypadku timewbudowanego.
gniourf_gniourf
@ gniourf-gniourf: Komentowałem z powodu twojego zdania „wyjście przechodzi na stdout lub stderr” ^^
Olivier Dulac
14

Odpowiedzi na twoje szczególne pytanie dotyczące timewbudowanego, ale pewne polecenia, które nie piszą ani do, stdoutani do stderr. Klasycznym przykładem jest polecenie Unix crypt. cryptbez argumentów szyfruje standardowe wejście stdini zapisuje je na standardowe wyjście stdout. Monituje użytkownika o podanie hasła getpass(), które domyślnie wyświetla monit o hasło /dev/tty. /dev/ttyjest bieżącym urządzeniem końcowym. Zapisywanie do /dev/ttypowoduje zapisywanie do bieżącego terminala (jeśli taki istnieje, patrz isatty()).

Powodem, dla cryptktórego nie można pisać, stdoutjest to, że zapisuje zaszyfrowane dane wyjściowe stdout. Ponadto lepiej jest pytać /dev/ttyzamiast pisać, aby stderrużytkownik przekierował stdouti stderrmonit nadal był widoczny. (Z tego samego powodu cryptnie można odczytać hasła stdin, ponieważ jest ono używane do odczytu danych do zaszyfrowania).

Alok
źródło
2
+1. Mniej istotne dla PO, ale bardziej odpowiednie dla każdego, kto natknie się na „Dlaczego dane wyjściowe niektórych programów linuksowych nie trafiają ani do STDOUT, ani do STDERR?” przez Google. :-)
ruakh
0

time sleep 1 > /dev/null 2>&1# przekierowuje wyjście „sleep” do null. Następnie „czas” zapisuje własne wyjście, bez przekierowań. To jest jak „ time (sleep 1 > /dev/null 2>&1)”.

(time sleep 1) > /dev/null 2>&1 # uruchamia „czas uśpienia 1”, a następnie przekierowuje dane wyjściowe na zero.

[] s

Márcio
źródło
-1

Problem w twoim przypadku polega na tym, że przekierowanie działa w inny sposób. Napisałeś

time sleep 1 2>&1 > /dev/null

To przekierowuje standardowe wyjście, /dev/nulla następnie przekierowuje standardowy błąd na standardowe wyjście.

Aby przekierować wszystkie dane wyjściowe, musisz napisać

time sleep 1 > /dev/null 2>&1 

Następnie błąd standardowy zostanie przekierowany na standardowe wyjście, a następnie wszystkie standardowe wyjście (zawierające błąd standardowy) zostanie przekierowane na /dev/null.

Uwe Plonus
źródło
To nie działa z wbudowanym bash time. Zobacz moją odpowiedź na kilka wyjaśnień.
gniourf_gniourf
+1, ponieważ jest to przydatna odpowiedź na podobne pytanie. Chociaż @Olivier wyjaśnia to lepiej w komentarzu do powyższego pytania.
Will Sheppard,