Czy raporty postępu / informacje o logowaniu należą do stderr czy stdout?

75

Czy istnieje oficjalny POSIX, GNU lub inne wytyczne dotyczące tego, gdzie powinny być drukowane raporty z postępu i informacje o logowaniu (takie jak „Doing foo; foo done”)? Osobiście staram się pisać je do stderr, aby móc przekierować standardowe wyjście i uzyskać tylko rzeczywiste wyjście programu. Niedawno powiedziano mi, że nie jest to dobra praktyka, ponieważ raporty postępu nie są tak naprawdę błędami i tylko komunikaty o błędach powinny być drukowane na stderr.

Obie pozycje mają sens i oczywiście możesz wybrać jedną lub drugą w zależności od szczegółów tego, co robisz, ale chciałbym wiedzieć, czy istnieje powszechnie akceptowany standard. Nie byłem w stanie znaleźć żadnych konkretnych reguł w POSIX, standardach kodowania GNU ani żadnej innej tak szeroko akceptowanej listy najlepszych praktyk.

Mamy kilka podobnych pytań, ale nie dotyczą one dokładnie tego problemu:

Czy istnieją więc jakieś oficjalne zasady dotyczące tego, gdzie powinny być drukowane raporty postępu i inne komunikaty informacyjne (które nie są częścią rzeczywistych wyników programu)?

terdon
źródło
26
Drukowanie błystek i tym podobnych stdoutwraz z wynikami to bezpieczny sposób, aby wyniki były bezużyteczne. Jeśli kiedykolwiek będziesz potrzebować przesłać wyniki do innego programu, wspomniany program będzie musiał oddzielić wyniki od pokręteł. Ponadto, jeśli przekierujesz dane wyjściowe do pliku, nie zobaczysz błystek. MOIM ZDANIEM.
Satō Katsura,
2
@SatoKatsura tak, to także moja opinia i dlatego drukuję takie na stderr. CTO firmy, dla której pracuję, uważa jednak, że drukowanie na stderr jest oznaką, że coś poszło nie tak. Zrobiłem dość śmiałe twierdzenie, że POSIX Way® drukuje na stderr, a on mnie wezwał. Biorąc pod uwagę, że ma on dla mnie 20 lat doświadczenia, chciałbym sprawdzić, czy mogę znaleźć jakieś „oficjalne” wytyczne.
terdon
to samo, co powiedziała Sato Katsura, stdoutjest w rzeczywistości bezpiecznym i właściwym sposobem, aby naciskać pokrętła i inne wiadomości informacyjne, ale jak wielu programistów mówi: „Cisza jest złotem. Nie wysyłaj nic, jeśli wszystko jest w porządku. więc w rzeczywistości zawsze używa się stderr ze względu na niejasną definicję, a także stdout może przerwać sekwencję rur. dla oficjalnego przewodnika
Se ven
6
@ Seven Myślę, że źle zrozumiałeś; Sato mówi, że używanie stdout jest niebezpieczne („bezpieczny sposób, aby wyniki były bezużyteczne”).
Stephen Kitt,
1
Jednym z możliwych rozwiązań uniwersalnych byłoby użycie tylko stderrdo zgłaszania komunikatów o błędach, z wyjątkiem sytuacji, gdy --verboseużywana jest flaga, w której również uwzględniane są raporty postępu.
Gaurav,

Odpowiedzi:

27

Posix definiuje standardowe strumienie w ten sposób :

Przy uruchamianiu programu należy wstępnie zdefiniować trzy strumienie i nie trzeba ich jawnie otwierać: standardowe wejście (do odczytu danych konwencjonalnych), standardowe wyjście (do zapisu danych konwencjonalnych) i błąd standardowy (do zapisu danych diagnostycznych). Po otwarciu standardowy strumień błędów nie jest w pełni buforowany; standardowe strumienie wejściowe i wyjściowe są w pełni buforowane wtedy i tylko wtedy, gdy można ustalić, że strumień nie odnosi się do urządzenia interaktywnego.

Biblioteki C GNU opisano standardowe strumienie podobne:

Zmienna: FILE * stdout
Standardowy strumień wyjściowy, który jest używany do normalnego wyjścia z programu.

Zmienna: FILE * stderr
Standardowy strumień błędów używany do komunikatów o błędach i diagnostyki wydawanych przez program.

Zatem standardowe definicje mają niewiele wskazówek dotyczących wykorzystania strumienia poza „wyjściem konwencjonalnym / normalnym” i „wyjściem diagnostycznym / błędem”. W praktyce często przekierowuje się jeden lub oba strumienie do plików i potoków, w których wskaźniki postępu będą stanowić problem . Niektóre systemy nawet monitorują stderr wyjście i uważają to za oznakę problemów. Czysto pomocnicze informacje o postępach są zatem problematyczne w obu strumieniach.

Zamiast bezwarunkowego wysyłania wskaźników postępu do któregokolwiek standardowego strumienia, ważne jest, aby uznać, że wyniki postępu są odpowiednie tylko dla strumieni interaktywnych . Mając to na uwadze, zalecam pisanie liczników postępu dopiero po sprawdzeniu, czy strumień jest interaktywny (np. Z isatty()) lub gdy jest wyraźnie włączony przez opcję wiersza poleceń. Jest to szczególnie ważne w przypadku sensorów postępu, które opierają się na zachowaniu aktualizacji terminala, takich jak słupki z procentem ukończenia.

W przypadku niektórych bardzo prostych komunikatów o postępach („Uruchamianie X” ... „Gotowe z X”) bardziej rozsądne jest włączenie wyniku nawet dla strumieni nieinteraktywnych. W takim przypadku zastanów się, w jaki sposób użytkownicy mogą wchodzić w interakcje ze strumieniami, np. Wyszukiwanie, grepstronicowanie lesslub monitorowanie za pomocą tail -f. Jeśli zrozumienie komunikatów o postępach w tych kontekstach jest sensowne, łatwiej będzie je wykorzystać stdout.

Bradd Szonye
źródło
Dzięki, wytyczne GNU to coś, czego szukam. Zdaję sobie jednak sprawę, że moje pytanie było trochę mylące. Kiedy wspomniałem o „raportach postępu”, myślałem zarówno o rzeczach takich jak N% doneio rzeczach takich jak doing fooi foo done. Te ostatnie należy zawsze zachować, ponieważ są używane do debugowania po przekierowaniu do pliku. Twoja sugestia sprawdzenia, czy strumień jest interaktywny, nie ma zastosowania (choć ogólnie jest to bardzo dobry pomysł).
terdon
Ach, dziękuję za wyjaśnienie, to trochę różni się od takich rzeczy, jak „% complete” i paski postępu znaku haszyszu widoczne w oprogramowaniu, takim jak curli yum. W tym przypadku, chciałbym zastanowić się, czy i w jaki sposób użytkownik będzie chciał współdziałać z wyjściem poprzez greplub lesslub podobnych narzędzi.
Bradd Szonye
Zaktualizowana odpowiedź w celu uwzględnienia powyższych wyjaśnień.
Bradd Szonye
71

POSIX definiuje błąd standardowy jako

do zapisu danych diagnostycznych

Nie ogranicza się to wyłącznie do komunikatów o błędach. Informacje o postępach uważałbym za dane wyjściowe diagnostyczne, więc należy on do standardowego błędu.

Stephen Kitt
źródło
8
FWIW, w Pythonie, stdoutjest domyślnie buforowane w linii, podczas gdy nie stderrjest buforowane , więc stderrjest naturalnym wyborem do pisania tekstu postępu / pasków / spinnerów, które nie zawierają nowej linii. (Jeśli piszesz taki tekst stdout, musisz zaśmiecać swoje wywołania wyjściowe postępów, stdout.flush()aby były widoczne).
PM 2,
12
@ PM2Ring, który nie jest specyficzny dla Pythona, POSIX definiuje strumienie w ten sposób.
Stephen Kitt,
4
@ PM2Ring: Pisząc do stdout, trzeba opróżnić, nawet jeśli tekst nie zawiera nowej linii, jeśli chcesz, aby sprawozdania z postępów rzeczywiście pojawiają się w czasie rzeczywistym, kiedy przekierowany.
@Hurkyl Ah, dobry punkt. Nie zastanawiałem się nad przekierowaniem.
PM 2,
1
@Wower, co? Nie. Ansible połyka tekst stderr, jeśli status wyjścia wynosi 0. To twierdzenie jest po prostu fałszywe.
Charles Duffy,
10

POSIX jest nieco bardziej konkretny na temat „informacji diagnostycznych” w Shell i Utilities , 1.4: Domyślne opis narzędzia (wyróżnienie moje):

STDERR

W sekcji STDERR opisano standardowe wyjście błędu narzędzia. Opisane są tylko te wiadomości, które zostały celowo wysłane przez narzędzie. Użycie terminala dla standardowego błędu może spowodować zatrzymanie dowolnego standardowego narzędzia, które zapisuje standardowe wyjście błędu, gdy jest używane w tle. Z tego powodu aplikacje nie powinny używać interaktywnych funkcji w skryptach umieszczanych w tle.

Format komunikatów diagnostycznych dla większości narzędzi nie jest określony, ale ustawienie LC_MESSAGES i [XSI] [Opcja Start ] NLSPATH. [Koniec opcji]

Określony standardowy błąd wyjściowy standardowych narzędzi nie może zależeć od istnienia lub wartości zmiennych środowiskowych zdefiniowanych w tym tomie POSIX.1-2008, z wyjątkiem przypadków przewidzianych w tym tomie POSIX.1-2008.

Domyślne zachowanie : Gdy sekcja ta jest wymieniona jako „Standardowy błąd należy stosować tylko w przypadku komunikatów diagnostycznych.”, Oznacza to, że o ile nie określono inaczej, komunikaty diagnostyczne są wysyłane do standardowego błędu tylko wtedy, gdy status wyjścia wskazuje, że błąd wystąpił, a narzędzie jest używane zgodnie z opisem w tym tomie POSIX.1-2008.

Gdy ta sekcja jest wymieniona jako „Nieużywane”, oznacza to, że standardowy błąd nie będzie używany, gdy narzędzie jest używane, jak opisano w niniejszym tomie POSIX.1-2008.

IANASL, ale interpretuję to jako oznaczające, że stderr będzie miał dane wyjściowe tylko wtedy, gdy narzędzie zwróci kod wyjścia błędu. Ponieważ nie powinno to być normalnym przebiegiem zdarzeń dla pomyślnego wykonania, narzędzie POSIX nie powinno drukować żadnych informacji o postępach, chyba że wystąpi błąd (chyba że oczywiście określono inaczej itp.).

muru
źródło
Rozumiem to jako opis znaczenia sekcji „STDERR” podanej w specyfikacjach każdego narzędzia POSIX, a nie jako ogólna definicja standardowego użycia błędu. Nie sądzę, żeby terdon pisał tutaj standardowe narzędzie POSIX ;-).
Stephen Kitt
1
@StephenKitt też tego nie robi. Ale debatuje ze swoim CTO, który twierdzi, że dane wyjściowe stderr wskazują, że coś idzie nie tak, a standard potwierdza jego twierdzenie, że jest zachowaniem domyślnym.
muru
1
Obsługuje tylko oświadczenie jako domyślne zachowanie narzędzi POSIX , a nawet wtedy tylko w określonych przypadkach (narzędzia, które używają wspomnianej frazy). Ta część POSIX nie jest normatywna (AIUI), jest to szablon: mówi ci, jak czytać specyfikacje narzędzi, nie określa zachowania narzędzi. W szczególności definiuje znaczenie zwrotów „Standardowy błąd należy stosować tylko w przypadku komunikatów diagnostycznych”. i „Nieużywane”. w sekcjach „STDERR” specyfikacji narzędzi. Każde narzędzie określa swoje zachowanie i może używać tych wyrażeń jako skrótów.
Stephen Kitt
@StephenKitt, co nie zmienia mojego zdania. Narzędzia POSIX albo a) nie wypisują na stderr, b) używają go tylko do komunikatów diagnostycznych, jeśli coś pójdzie nie tak, c) używają go tylko do komunikatów diagnostycznych, nawet jeśli nic nie idzie źle, d) używają go do czegoś innego. Powiedziałem, że używają go tylko do komunikatów diagnostycznych, jeśli coś pójdzie nie tak (a, b), chyba że zaznaczono inaczej (c, d). Moje twierdzenie jest takie, że jest to domyślna, która jest wyraźnie, ponieważ właśnie dlatego jest szablonem.
muru
2
Cóż, powiedziałbym, że „chyba że podano inaczej” jest dość znaczącym zastrzeżeniem: narzędzie POSIX może bardzo dobrze określić, że wysyła informacje o postępie na temat swojego standardowego błędu i będzie zgodne ze standardami. Masz rację, interesujące jest to, że „domyślne zachowanie” (które nadal wymaga szczególnej wzmianki w odpowiedniej sekcji) polega na użyciu standardowego błędu tylko wtedy, gdy kod wyjścia również wskazuje błąd; ale to nie ogranicza.
Stephen Kitt
7

Zgodnie z zasadą wykluczenia może on przejść tylko do stderr. Tak, wiem, że zapytałeś o oficjalną specyfikację, której nie mogę przedstawić poza linkiem do specyfikacji POSIX podanym przez Stephena Kitt'a, który stwierdza, że ​​stderr służy do celów diagnostycznych.

Ważniejsze jest to, że stdin i stdout mają funkcję, która nie zezwala na drukowanie raportów postępu na stdout - one oczywiście tworzą sekwencję potoków, która w komendach powłoki Unixa jest nie tylko efektem ubocznym, ale sam rdzeń potężnego podejścia do potokowania .

Więc. Nic poza prawdziwym „ładunkiem” twojego programu nie należy do standardowego wyjścia. Jeśli twój program nie ma wyjścia, nic nie powinno przejść do standardowego wyjścia. To pozostawia stderr na wszystko inne , w tym raporty postępu.

To prawda, że ​​pozostawia to dziurę - prawdopodobnie dobrze byłoby mieć „stdfluff” lub coś takiego, co nie służy ani do wyjścia, ani do błędów, ale do raportów postępu, debugowania i tak dalej. W rzeczywistości nic Cię nie powstrzymuje, tzn. Możesz wydrukować swoje postępy do deskryptora pliku 3. Przykład:

$ perl -e 'open($fd, ">", "/dev/fd/3"); print $fd "hello\n"'

To nie wytwarza danych wyjściowych. (*)

$ perl -e 'open($fd, ">", "/dev/fd/3"); print $fd "hello\n"'  3>&1
hello

Wypisuje to na fd-3, który jest przekierowywany na standardowe wyjście.

(*) Pierwszy przykład nie generuje danych wyjściowych, ale jest nieco przesadny; openzawiedzie i $!będzie zawierać no such file or directory; po prostu weźmy to za przykład, oczywiście nie należy tego tak na serio używać. W prawdziwym programie, jeśli chciałbyś pójść tą drogą, możesz przetestować, czy /dev/fd/3jest on użyteczny i wziąć to jako wskazówkę, czy aktywować raporty z postępu; musisz to zrobić dość wcześnie, aby nie pomylić własnych openplików dla prawdziwych plików i takie ...

AnoE
źródło
Co to jest fd-3? Trzecia dyskietka?
Peter Mortensen,
deskryptor pliku 3, @PeterMortensen
AnoE
-1

Komunikat użycia powinien przejść do stdout, jeśli użytkownik go wywołał, --helpi do stderr, jeśli popełnił błąd. Bezwarunkowe drukowanie na stderr jest nieznośne, ponieważ utrudnia przeglądanie go za pomocą pager / grep / etc.

Mogę też zasugerować trzeci sposób: open / dev / tty dla raportów postępu / spinnerów / etc.

Losowo 832
źródło
6
Przepraszamy, ale to nie odpowiada na pytanie. W szczególności poprosiłem o oficjalne wytyczne, a nie opinię. W każdym razie, nawet gdybym poprosił o opinię, odpowiadasz na inne pytanie. W ogóle nie mówię --helpani nie pomagam. Przeczytaj ponownie pytanie.
terdon
Wspomniałeś o „komunikacie użytkowania”, myślałem, że była to część twojego pytania, a nie tylko tytuł innego pytania, które podłączyłeś. Nie rozumiem, dlaczego według ciebie istnieją „oficjalne wytyczne” w tym zakresie. Kto miałby uprawnienia do ich tworzenia?
Random832,
3
Nie wiem, że tak, dlatego pytam. A jeśli chodzi o oficjalny, standard POSIX zawiera specyfikacje różnych drobiazgowych szczegółów dotyczących tego, jak program powinien się zachowywać. W tym, jak zauważył Stephen w swojej odpowiedzi, niejasna sugestia dotycząca tego, co powinno iść do stderr. Standard kodowania GNU może również mieć coś podobnego. Zasadniczo każda grupa, która jest aktywna i produkuje kod dla ekosystemu * nix, może mieć standard, który byłbym zainteresowany.
terdon
@terdon byłbyś w porządku z przykładami tego, co robią istniejące programy? curl, wget i pv używają stderr, przy czym wget i pv uzależniają go od bycia tty, a curl od tego, że stdout nie jest tty.
Random832
4
Wygląda na to, że Twój CTO uważa, że ​​program wywołujący powinien reagować na obecność danych na stderr, co oznacza, że ​​wystąpił błąd, zamiast patrzeć na status wyjścia. Jest to zdecydowanie zła praktyka (a także trudniejsza do napisania niż sprawdzanie statusu wyjścia) i może być lepszym kątem do przekonania go.
Random832