Относятся ли отчеты о проделанной работе / журналы к stderr или stdout?

75

Существует ли официальное руководство по POSIX, GNU или другим правилам о том, где следует печатать отчеты о ходе работы и информацию о регистрации (например, «Doing foo; foo done»)? Лично я склонен записывать их в stderr, чтобы я мог перенаправить stdout и получать только фактический вывод программы. Мне недавно сказали, что это нехорошая практика, поскольку отчеты о ходе работы на самом деле не являются ошибками, и в stderr следует печатать только сообщения об ошибках.

Обе позиции имеют смысл, и, конечно, вы можете выбрать одну или другую в зависимости от деталей того, что вы делаете, но я хотел бы знать, есть ли общепринятый стандарт для этого. Мне не удалось найти какие-либо конкретные правила в POSIX, стандартах кодирования GNU или в любом другом широко распространенном списке лучших практик.

У нас есть несколько похожих вопросов, но они не решают эту проблему:

Итак, существуют ли какие-либо официальные правила о том, где следует печатать отчеты о ходе работы и другие информационные сообщения (которые не являются частью фактического результата программы)?

Тердон
источник
26
Печать блесны и тому подобное stdoutвместе с результатами - это безопасный способ сделать результаты бесполезными. Если вам когда-либо понадобится передать результаты в какую-либо другую программу, указанная программа должна будет отделить результаты от счетчиков. Кроме того, если вы перенаправите вывод в файл, вы не сможете увидеть счетчики. ИМХО.
Satō Katsura
2
@SatoKatsura да, это тоже мое мнение, и поэтому я печатаю это на stderr. Однако технический директор компании, в которой я работаю, считает, что печать на stderr свидетельствует о том, что что-то пошло не так. Я сделал довольно смелое утверждение, что POSIX Way® печатает на stderr, и он вызвал меня на это. Учитывая, что он имеет 20 с лишним лет опыта работы со мной, я хотел бы посмотреть, смогу ли я найти какое-то «официальное» руководство.
Тердон
stdoutТо же, что сказал Сато Кацура, на самом деле является безопасным и правильным способом печати блесны и других информативных сообщений, но, как говорят многие программисты: «Молчание - это золото. Ничего не выводить, если все хорошо. так что на самом деле stderr всегда используется для этого из-за расплывчатого определения, а также stdout может нарушить последовательность
каналов.
6
@ Кажется, ты неправильно понял; Сато говорит, что использование stdout небезопасно («безопасный способ сделать результаты бесполезными»).
Стивен Китт
1
Одним из возможных универсальных решений было бы использование только stderrдля сообщения об ошибках, кроме случаев, когда используется --verboseфлаг, и в этот момент также включаются отчеты о ходе работ.
Гаурав

Ответы:

27

Posix определяет стандартные потоки таким образом :

При запуске программы три потока должны быть предварительно определены и не должны открываться явно: стандартный ввод (для чтения обычного ввода), стандартный вывод (для записи обычного вывода) и стандартная ошибка (для записи диагностического вывода). При открытии стандартный поток ошибок не полностью буферизуется; стандартные входные и стандартные выходные потоки полностью буферизуются тогда и только тогда, когда можно определить, что поток не ссылается на интерактивное устройство.

Библиотека GNU C описывает стандартные потоки аналогично:

Переменная: FILE * stdout
Стандартный поток вывода, который используется для нормального вывода из программы.

Переменная: FILE * stderr
Стандартный поток ошибок, который используется для сообщений об ошибках и диагностики, выдаваемых программой.

Таким образом, стандартные определения имеют небольшое руководство для использования потока, помимо «обычного / нормального вывода» и «вывода диагностики / ошибки». На практике часто перенаправляют один или оба этих потока в файлы и конвейеры, где проблемы прогресса будут проблемой , Некоторые системы даже отслеживают stderr вывод и считают это признаком проблем. Поэтому чисто вспомогательная информация о прогрессе проблематична в любом потоке.

Вместо того, чтобы отправлять индикаторы хода выполнения безоговорочно в любой стандартный поток, важно понимать, что вывод хода выполнения подходит только для интерактивных потоков. Имея это в виду, я рекомендую записывать счетчики хода выполнения только после проверки, является ли поток интерактивным (например, с помощью isatty()) или когда он явно включен параметром командной строки. Это особенно важно для индикаторов прогресса, которые имеют смысл в поведении обновления терминала, например, в% -заполненных барах.

Для некоторых очень простых сообщений о ходе выполнения («Начало X» ... «Готово с X») более разумно включить вывод даже для неинтерактивных потоков. В этом случае подумайте о том, как пользователи могут взаимодействовать с потоками, например, выполнять поиск grepили пейджинг lessили мониторинг tail -f. Если имеет смысл видеть сообщения о прогрессе в этих контекстах, их будет намного легче использовать stdout.

Брэд Сзонье
источник
Спасибо, директива GNU - это то, что мне нужно. Однако я понимаю, что мой вопрос немного вводил в заблуждение. Когда я упомянул «отчеты о прогрессе», я думал о таких вещах, как N% doneи о вещах, подобных doing fooи foo done. Последние всегда следует сохранять, поскольку они используются для отладки при перенаправлении в файл. Таким образом, ваше предложение проверить, является ли поток интерактивным, неприменимо (хотя в целом это очень хорошая идея).
Тердон
Ах, спасибо за разъяснение, это немного отличается от таких вещей, как «% complete» и индикаторов выполнения хеш-меток, которые вы видите в программном обеспечении, подобном curlи yum. В этом случае я бы подумал о том, захочет ли пользователь взаимодействовать с выходными данными grepи / lessили с помощью аналогичных инструментов , и каким образом .
Брэд Сзонье
Обновленный ответ для включения разъяснений выше.
Брэд Сзонье
71

POSIX определяет стандартную ошибку как

для записи диагностического вывода

Это не ограничивает его использование только сообщениями об ошибках. Я бы рассматривал информацию о прогрессе как диагностический вывод, поэтому он относится к стандартной ошибке.

Стивен Китт
источник
8
FWIW в Python stdoutпо умолчанию буферизуется по строкам, а не буферизируется stderr, поэтому stderrэто естественный выбор для написания текста прогресса / баров / спиннеров, которые не содержат перевода строки. (Если вы пишете такой текст, stdoutвам нужно загромождать ваши выходные вызовы прогресса, stdout.flush()чтобы сделать его видимым).
PM 2Ring
12
@ PM2Ring, который не является специфичным для Python, POSIX определяет потоки таким образом.
Стивен Китт
4
@ PM2Ring: При записи stdout, вы должны очистить даже если текст действительно содержит символ новой строки , если вы хотите , чтобы ваши отчеты о ходе работ на самом деле показать в режиме реального времени , когда перенаправлены.
@ Хороший вопрос. Я не думал о перенаправлении.
PM 2Ring
1
@ Башня, а? Нет. Ansible глотает текст stderr, если статус выхода равен 0. Это утверждение просто ложно.
Чарльз Даффи
10

POSIX немного конкретнее о «диагностической информации» в Shell и Utilities , 1.4: Описание утилит по умолчанию (выделено мной):

STDERR

В разделе STDERR описывается стандартный вывод ошибок утилиты. Описаны только те сообщения, которые специально отправлены утилитой. Использование терминала для стандартной ошибки может привести к остановке любой из стандартных утилит, которые записывают вывод стандартной ошибки при использовании в фоновом режиме. По этой причине приложения не должны использовать интерактивные функции в сценариях для размещения в фоновом режиме.

Формат диагностических сообщений для большинства утилит не определен, но настройка LC_MESSAGES и [XSI] [Option Start не должна указывать на языковые и культурные соглашения диагностических и информационных сообщений, формат которых не указан в этом томе POSIX.1-2008. ] NLSPATH. [Вариант Конец]

Указанный стандартный вывод ошибок стандартных утилит не должен зависеть от существования или значения переменных среды, определенных в этом томе POSIX.1-2008, за исключением случаев, предусмотренных этим томом POSIX.1-2008.

Поведение по умолчанию : если этот раздел указан как «Стандартная ошибка должна использоваться только для диагностических сообщений», это означает, что, если не указано иное, диагностические сообщения должны отправляться на стандартную ошибку только тогда, когда состояние выхода указывает, что ошибка произошло, и утилита используется, как описано в этом томе POSIX.1-2008.

Если этот раздел указан как «Не используется», это означает, что стандартная ошибка не должна использоваться при использовании утилиты, как описано в этом томе POSIX.1-2008.

IANASL, но я понимаю, что это означает, что stderr будет выводиться только в том случае, если утилита вернет код завершения ошибки. Поскольку это не должно быть нормальным ходом событий для успешного выполнения, утилита POSIX не должна выводить информацию о ходе выполнения, если только не происходит ошибка (если, конечно, не указано иное и т. Д.).

Мур
источник
Я понимаю это как описание значения раздела «STDERR», приведенного в спецификациях каждой утилиты POSIX, а не как общее определение стандартного использования ошибок. Я не думаю, что terdon пишет стандартную утилиту POSIX здесь ;-).
Стивен Китт
1
@StephenKitt и я тоже. Но он спорит со своим техническим директором, который говорит, что вывод stderr свидетельствует о том, что что-то идет не так, и стандарт поддерживает его утверждение как поведение по умолчанию.
Муру
1
Он поддерживает утверждение только в качестве поведения по умолчанию для утилит POSIX , и даже тогда только в определенных случаях (утилиты, которые используют упомянутую фразу). Эта часть POSIX не является нормативной (AIUI), это шаблон: она говорит вам, как читать спецификации утилит, она не определяет поведение утилит. В частности, он определяет значения фраз «Стандартная ошибка должна использоваться только для диагностических сообщений». и "Не используется". в разделах "STDERR" спецификаций утилит. Каждая утилита определяет свое поведение, и она может использовать эти фразы для краткости.
Стивен Китт
@StephenKitt, который не меняет мою точку зрения. Утилиты POSIX либо: а) не выводят в stderr, б) используют его только для диагностических сообщений, если что-то идет не так, в) используют его только для диагностических сообщений, даже если ничего не происходит, и г) используют его для чего-то другого. Я сказал, что они используют его только для диагностических сообщений, если что-то идет не так (a, b), если не указано иное (c, d). Теперь я утверждаю, что это значение по умолчанию, и это, безусловно, так, потому что именно поэтому это шаблон.
Муру
2
Что ж, я бы сказал, что «если не указано иное» является довольно существенным предупреждением: утилита POSIX могла бы очень хорошо указать, что она выводит информацию о прогрессе по своей стандартной ошибке, и она будет соответствовать стандартам. Вы правы, интересно, что «поведение по умолчанию» (которое все еще требует конкретного упоминания в соответствующем разделе) состоит в том, чтобы использовать только стандартную ошибку, если код выхода также указывает на ошибку; но это не ограничение.
Стивен Китт
7

По принципу исключения, это может идти только в stderr. Да, я знаю, что вы спрашивали об официальной спецификации, которую я не могу представить вам помимо ссылки на спецификацию POSIX, предоставленную Стивеном Киттом, в которой говорится, что stderr предназначен для диагностических целей.

Более важным моментом является то, что stdin и stdout имеют функцию, которая запрещает печатать отчеты о ходе работ на stdout - они, конечно, образуют последовательность каналов, которая в командах оболочки Unix является не просто побочным эффектом, а самой основой мощного конвейерного подхода ,

Так. Ничто, кроме реальной полезной нагрузки вашей программы, не принадлежит stdout. Если ваша программа не имеет выходных данных, то ничего не должно идти в стандартный вывод. Это оставляет stderr для всего остального , включая отчеты о проделанной работе.

Конечно, это оставляет дыру - было бы неплохо иметь «stdfluff» или что-то в этом роде, которое не предназначено ни для вывода, ни для ошибок, а для отчетов о проделанной работе, отладки и тому подобного. Фактически, ничто не мешает вам сделать это, то есть вы можете распечатать свой прогресс в файловом дескрипторе 3. Пример:

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

Это не дает никакого выхода. (*)

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

Это печатает на fd-3, который перенаправляется на стандартный вывод.

(*) Первый пример не производит вывода, но все еще немного надуман; openтерпит неудачу и $!будет содержать no such file or directory; просто возьмите это в качестве примера, конечно, не стоит так серьезно использовать. В реальной программе, если вы хотите пойти по этому пути, вы можете проверить, /dev/fd/3пригодно ли это для использования, и принять это как подсказку, активировать ли ваши отчеты о проделанной работе; вам придется сделать это довольно рано, чтобы вас не смущали ваши собственные openфайлы настоящих файлов и тому подобное ...

Anoe
источник
Что такое fd-3? Третья дискета?
Питер Мортенсен
дескриптор файла 3, @PeterMortensen
AnoE
-1

Сообщение об использовании должно идти в stdout, если пользователь вызвал его, --helpи в stderr, если они допустили ошибку. Безусловно, печатать его на stderr неприятно, потому что это затрудняет его просмотр с помощью пейджера / grep / и т.д.

Кроме того, могу я предложить третий способ: открыть / dev / tty для отчетов о проделанной работе / счетчиков / и т.д.

Random832
источник
6
Извините, но это не ответ на вопрос. Я специально попросил официальные рекомендации, а не мнение. В любом случае, даже если бы я спросил мнение, вы отвечаете на другой вопрос. Я не говорю о --helpсообщениях или помощи вообще. Пожалуйста, перечитайте вопрос.
Terdon
Вы упомянули «сообщение об использовании», я подумал, что это часть вашего вопроса, а не просто заголовок другого вопроса, который вы связали. Я не понимаю, почему вы думаете, что для этого существуют «официальные рекомендации». Кто будет иметь полномочия сделать их?
Random832
3
Я не знаю, что они делают, поэтому я спрашиваю. А что касается официального, стандарт POSIX содержит спецификации для различных мелких деталей того, как должна вести себя программа. В том числе, как указал Стивен в своем ответе, смутное предложение о том, что должно идти к stderr. Стандарт кодирования GNU также может иметь нечто подобное. По сути, любая группа, которая активна и производит код для экосистемы * nix, может иметь стандарт, который меня заинтересует.
terdon
@terdon Не могли бы вы привести примеры того, что делают существующие программы? curl, wget и pv все используют stderr, при этом wget и pv делают его условным, если он является tty, а curl - условным, если stdout не является tty.
Random832
4
Кроме того, похоже, что ваш технический директор считает, что вызывающая программа должна реагировать на наличие данных на stderr как на то, что произошла ошибка, а не на состояние выхода. Это определенно плохая практика (и также сложнее написать, чем проверка состояния выхода), и он может быть лучшим способом убедить его в этом.
Random832