Os relatórios de progresso / informações de registro pertencem ao stderr ou stdout?

75

Existe uma orientação oficial POSIX, GNU ou outra orientação sobre onde os relatórios de progresso e as informações de registro (coisas como "Fazendo o foo; o foo feito") devem ser impressos? Pessoalmente, costumo escrevê-los no stderr para poder redirecionar o stdout e obter apenas a saída real do programa. Recentemente me disseram que isso não é uma boa prática, pois os relatórios de progresso não são realmente erros e apenas as mensagens de erro devem ser impressas no stderr.

Ambas as posições fazem sentido e, é claro, você pode escolher uma ou outra, dependendo dos detalhes do que está fazendo, mas eu gostaria de saber se existe um padrão comumente aceito para isso. Não consegui encontrar nenhuma regra específica no POSIX, nos padrões de codificação GNU ou em qualquer outra lista de boas práticas amplamente aceita.

Temos algumas perguntas semelhantes, mas elas não abordam exatamente esse problema:

Portanto, existem regras oficiais sobre onde os relatórios de progresso e outras mensagens informativas (que não fazem parte da produção real do programa) devem ser impressos?

terdon
fonte
26
A impressão de spinners e similares, stdoutjuntamente com os resultados, é uma maneira segura de tornar os resultados inúteis. Se você precisar canalizar os resultados para outro programa, esse programa precisará separar os resultados dos giradores. Além disso, se você redirecionar a saída para um arquivo, não poderá ver os giradores. NA MINHA HUMILDE OPINIÃO.
Satō Katsura
2
@SatoKatsura sim, essa é minha opinião também e é por isso que imprimo em stderr. O CTO da empresa em que trabalho, no entanto, considera que imprimir em stderr é uma indicação de que algo deu errado. Fiz a afirmação, bastante ousada, de que o POSIX Way® está imprimindo em stderr e ele me chamou. Dado que ele tem 20 anos ímpares de experiência comigo, gostaria de ver se consigo encontrar algum tipo de orientação "oficial".
terdon
O mesmo que Sato Katsura disse, stdouté realmente uma maneira segura e correta de extrair spinners e outras mensagens informativas, mas, como muitos programadores dizem, 'O silêncio é ouro. Não produza nada se estiver tudo bem. Então, na verdade, stderr é sempre usado para fazer isso por causa da definição vaga e também StdOut pode quebrar o tubo sequence.for guia oficial
Se ven
6
@ Seven Acho que você não entendeu; Sato está dizendo que usar stdout não é seguro ("maneira segura de tornar os resultados inúteis").
Stephen Kitt
11
Uma solução possível de tamanho único seria usar apenas stderrpara relatar mensagens de erro, exceto quando um --verbosesinalizador é usado, momento em que os relatórios de progresso também são incluídos.
precisa

Respostas:

27

O Posix define os fluxos padrão assim :

Na inicialização do programa, três fluxos devem ser predefinidos e não precisam ser abertos explicitamente: entrada padrão (para leitura de entrada convencional), saída padrão (para gravação de saída convencional) e erro padrão (para gravação de saída de diagnóstico). Quando aberto, o fluxo de erro padrão não é totalmente armazenado em buffer; os fluxos de entrada e saída padrão são totalmente armazenados em buffer se, e somente se, o fluxo puder ser determinado para não se referir a um dispositivo interativo.

A GNU C Library descreve os fluxos padrão da mesma forma:

Variável: FILE * stdout
O fluxo de saída padrão, usado para saída normal do programa.

Variável: FILE * stderr
O fluxo de erros padrão, usado para mensagens de erro e diagnóstico emitidos pelo programa.

Portanto, as definições padrão têm pouca orientação para o uso do fluxo além da "saída convencional / normal" e da "saída de diagnóstico / erro". Na prática, é comum redirecionar um ou ambos os fluxos para arquivos e pipelines, onde os indicadores de progresso serão um problema . Alguns sistemas até monitoram stderr a saída e a consideram um sinal de problemas. Informações de progresso puramente auxiliares são, portanto, problemáticas em qualquer fluxo.

Em vez de enviar indicadores de progresso incondicionalmente para qualquer fluxo padrão, é importante reconhecer que a saída do progresso é apropriada apenas para fluxos interativos . Com isso em mente, recomendo escrever contadores de progresso somente após verificar se o fluxo é interativo (por exemplo, com isatty()) ou quando ativado explicitamente por uma opção de linha de comando. Isso é especialmente importante para que os medidores de progresso que dependem do comportamento de atualização do terminal façam sentido, como barras com% concluído.

Para certas mensagens de progresso muito simples ("Iniciando X" ... "Concluído com X"), é mais razoável incluir a saída mesmo para fluxos não interativos. Nesse caso, considere como os usuários podem interagir com os fluxos, como pesquisar com grepou paginar com lessou monitorar com tail -f. Se fizer sentido ver as mensagens de progresso nesses contextos, elas serão muito mais fáceis de consumir stdout.

Bradd Szonye
fonte
Obrigado, a diretriz GNU é o tipo de coisa que eu procuro. No entanto, percebo que minha pergunta foi um pouco enganadora. Quando mencionei "relatórios de progresso", pensei em coisas como N% donee como doing fooe foo done. O último deve sempre ser mantido, pois é usado para depuração quando redirecionado para um arquivo. Portanto, sua sugestão de verificar se o fluxo é interativo não é aplicável (embora geralmente seja uma boa ideia).
terdon
Ah, obrigado pelo esclarecimento, que é um pouco diferente de coisas como "% complete" e barras de progresso de marcas de hash que você vê em softwares como curle yum. Nesse caso, eu pensaria se e como o usuário gostaria de interagir com a saída por meio de grepou lessferramentas semelhantes.
Bradd Szonye
Resposta atualizada para incorporar os esclarecimentos acima.
Bradd Szonye
71

POSIX define erro padrão como

para escrever saída de diagnóstico

Isso não limita seu uso apenas a mensagens de erro. Eu consideraria as informações de progresso como saída de diagnóstico, portanto elas pertencem ao erro padrão.

Stephen Kitt
fonte
8
O FWIW, em Python, stdouté buffer de linha por padrão, enquanto não stderré armazenado em buffer, assim stderrcomo a escolha natural para escrever textos / barras / giradores de progresso que não contenham uma nova linha. (Se você escrever esse texto, stdoutprecisará desorganizar as chamadas de saída de progresso stdout.flush()para torná-lo visível).
PM 2Ring
12
@ PM2Ring que não é específico para Python, o POSIX define os fluxos dessa maneira.
Stephen Kitt
4
@ PM2Ring: Ao gravar stdout, você precisa liberar mesmo que seu texto não conter uma nova linha se você quiser que os seus relatórios de progresso para mostrar realmente em tempo real quando redirecionado.
@Hurkyl Ah, bom ponto. Eu não estava pensando em redirecionar.
PM 2Ring
11
@Wtower, eh? Não. Ansible engole texto stderr se o status de saída for 0. Essa afirmação é simplesmente falsa.
Charles Duffy
10

O POSIX é um pouco mais concreto sobre "informações de diagnóstico" no Shell e Utilitários , 1.4: Padrões de Descrição do Utilitário (ênfase minha):

STDERR

A seção STDERR descreve a saída de erro padrão do utilitário. Somente as mensagens enviadas propositadamente pelo utilitário são descritas. O uso de um terminal para erro padrão pode fazer com que qualquer um dos utilitários padrão que grava a saída de erro padrão pare quando usado em segundo plano. Por esse motivo, os aplicativos não devem usar recursos interativos em scripts para serem colocados em segundo plano.

O formato das mensagens de diagnóstico para a maioria dos utilitários não é especificado, mas as convenções de idioma e cultura das mensagens informativas e de diagnóstico cujo formato não é especificado neste volume do POSIX.1-2008 devem ser afetadas pela configuração de LC_MESSAGES e [XSI] [Option Start ] NLSPATH. [Fim da opção]

A saída de erro padrão especificada dos utilitários padrão não deve depender da existência ou do valor das variáveis ​​de ambiente definidas neste volume do POSIX.1-2008, exceto conforme fornecido por este volume do POSIX.1-2008.

Comportamento padrão : Quando esta seção está listada como "O erro padrão deve ser usado apenas para mensagens de diagnóstico.", Significa que, a menos que seja especificado de outra forma, as mensagens de diagnóstico serão enviadas ao erro padrão somente quando o status de saída indicar que um erro ocorreu e o utilitário é usado conforme descrito por este volume do POSIX.1-2008.

Quando esta seção está listada como "Não utilizado", significa que o erro padrão não deve ser usado quando o utilitário for usado conforme descrito neste volume do POSIX.1-2008.

IANASL, mas interpreto que isso signifique que o stderr terá saída apenas se o utilitário retornar um código de saída de erro. Como esse não deve ser o curso normal dos eventos para uma execução bem-sucedida, nenhuma informação de progresso deve ser impressa por um utilitário POSIX, a menos que ocorra um erro (a menos que, é claro, se especificado de outra forma, etc.).

muru
fonte
Entendo isso como descrevendo o significado da seção "STDERR" fornecida nas especificações de cada utilitário POSIX, não como uma definição geral para o uso de erros padrão. Eu não acho que o Terdon esteja escrevendo um utilitário POSIX padrão aqui ;-).
Stephen Kitt
11
@StephenKitt também não. Mas ele está discutindo com seu CTO, que diz que a saída stderr é uma indicação de que algo está errado, e o padrão suporta sua afirmação como comportamento padrão.
Muru
11
Ele suporta apenas a declaração como o comportamento padrão dos utilitários POSIX e, mesmo assim, apenas em casos específicos (utilitários que usam a frase mencionada). Esta parte do POSIX não é normativa (AIUI), é um modelo: mostra como ler as especificações do utilitário, não especifica o comportamento dos utilitários. Especificamente, define os significados das frases "O erro padrão deve ser usado apenas para mensagens de diagnóstico". e "Não usado". nas seções "STDERR" das especificações do utilitário. Cada utilitário especifica seu comportamento e pode usar essas frases como abreviação.
Stephen Kitt
@StephenKitt, que não muda meu ponto de vista. Os utilitários POSIX: a) não enviam para stderr; b) usam-no somente para mensagens de diagnóstico se algo der errado; c) use-o apenas para mensagens de diagnóstico, mesmo que nada dê errado; d) use-o para outra coisa. Eu disse que eles apenas o usam para mensagens de diagnóstico se algo der errado (a, b), a menos que seja indicado de outra forma (c, d). Agora, minha afirmação é que esse é o padrão, o que é claramente, pois é por isso que é modelo.
Muru
2
Bem, eu diria que "a menos que especificado de outra forma" é uma ressalva bastante significativa: um utilitário POSIX poderia muito bem especificar que ele gera informações de progresso em seu erro padrão e que seria compatível com os padrões. Você está certo, é interessante que o "comportamento padrão" (que ainda requer uma menção específica na seção relevante) seja usado apenas erro padrão se o código de saída também indicar um erro; mas não é limitativo.
Stephen Kitt
7

Pelo princípio da exclusão, ele só pode ir para o stderr. Sim, eu sei que você perguntou sobre uma especificação oficial, que não posso apresentar além do link para a especificação POSIX, fornecida por Stephen Kitt, que afirma que stderr é para fins de diagnóstico.

O ponto mais importante é que stdin e stdout têm uma função que desaprova a impressão de relatórios de progresso para stdout - eles naturalmente formam a sequência de pipes que, nos comandos shell do Unix, não é apenas um efeito colateral, mas o cerne da poderosa abordagem de pipelining .

Assim. Nada, exceto a "carga útil" real do seu programa, pertence ao stdout. Se o seu programa não tiver saída, nada deve ser levado ao stdout. Isso deixa o stderr para todo o resto , incluindo relatórios de progresso.

É verdade que isso deixa um buraco - provavelmente seria bom ter um "stdfluff" ou algo parecido que não serve para saída nem erros, mas para relatórios de progresso, depuração e algo assim. De fato, nada impede você de fazer isso, ou seja, você pode imprimir seu progresso no descritor de arquivo 3. Exemplo:

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

Isso não produz saída. (*)

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

Isso imprime em fd-3, que é redirecionado para stdout.

(*) O primeiro exemplo não produz saída, mas ainda é um pouco rebuscado; a openfalha e $!conteria no such file or directory; apenas tome isso como um exemplo, é claro que não deve ser usado dessa maneira a sério. Em um programa real, se você quiser seguir esse caminho, poderá testar se /dev/fd/3é utilizável e usar isso como uma dica para ativar seus relatórios de progresso; você teria que fazer isso bem cedo para não se confundir com seus próprios openarquivos reais e coisas assim ...

AnoE
fonte
O que é fd-3? O terceiro disquete?
Peter Mortensen
descritor de arquivo 3, @PeterMortensen
AnoE 21/12
-1

A mensagem de uso deve ir para stdout se o usuário a tiver chamado --helpe stderr se tiver cometido um erro. Imprimir no stderr incondicionalmente é desagradável, pois torna mais difícil visualizá-lo com um pager / grep / etc.

Além disso, posso sugerir uma terceira maneira: abra / dev / tty para relatórios de progresso / spinners / etc.

Random832
fonte
6
Desculpe, mas isso não está respondendo à pergunta. Pedi especificamente orientações oficiais , não opinião. De qualquer forma, mesmo que eu tenha pedido uma opinião, você está respondendo a uma pergunta diferente. Não estou falando --helpnem ajudando mensagens. Por favor, leia novamente a pergunta.
terdon
Você mencionou "mensagem de uso", pensei que isso fazia parte da sua pergunta e não apenas o título da outra pergunta que você vinculou. Não entendo por que você acha que existem "diretrizes oficiais" para isso. Quem teria autoridade para fazê-los?
Random832
3
Eu não sei o que eles fazem, é por isso que pergunto. E, como oficial, o padrão POSIX possui especificações para vários detalhes minuciosos de como um programa deve se comportar. Incluindo, como Stephen apontou em sua resposta, uma sugestão vaga para o que deveria ir para stderr. O padrão de codificação GNU também pode ter algo semelhante. Basicamente, qualquer grupo que esteja ativo e produza código para o ecossistema * nix pode ter um padrão no qual eu estaria interessado.
terdon
@terdon, você ficaria bem com exemplos do que os programas existentes fazem? curl, wget e pv usam stderr, com wget e pv condicionando-o a ser um tty e curl condicionando-o a stdout não ser o tty.
Random832
4
Além disso, parece que o seu CTO pensa que um programa de chamada deve reagir à presença de dados no stderr, o que significa que ocorreu um erro, em vez de examinar o status de saída. Definitivamente, é uma prática ruim (e também mais difícil de escrever do que verificar o status da saída) e pode ser um ângulo melhor para convencê-lo.
Random832