Les rapports d'avancement / informations de journalisation appartiennent-ils à stderr ou stdout?

75

Existe-t-il une directive officielle POSIX, GNU ou autre indiquant l'endroit où les rapports d'avancement et les informations de journalisation (des choses telles que "Doing foo; foo done") doivent être imprimés? Personnellement, j'ai tendance à les écrire sur stderr pour pouvoir rediriger stdout et obtenir uniquement la sortie réelle du programme. On m'a récemment dit que ce n'était pas une bonne pratique puisque les rapports d'avancement ne sont pas réellement des erreurs et que seuls les messages d'erreur doivent être imprimés sur stderr.

Les deux positions ont du sens, et vous pouvez bien sûr choisir l’une ou l’autre en fonction des détails de ce que vous faites, mais je voudrais savoir s’il existe une norme communément acceptée à cet égard. Je n'ai pas pu trouver de règles spécifiques dans POSIX, dans les normes de codage GNU, ni dans aucune autre liste de bonnes pratiques largement acceptée.

Nous avons quelques questions similaires, mais elles ne traitent pas de cette question précise:

Alors, y a-t-il des règles officielles sur l'endroit où les rapports d'avancement et autres messages informatifs (qui ne font pas partie du résultat réel du programme) doivent être imprimés?

terdon
la source
26
Imprimer les fileuses et similaires stdoutavec les résultats est un moyen sûr de rendre les résultats inutiles. Si vous avez besoin de transférer les résultats vers un autre programme, celui-ci devra séparer les résultats des filateurs. De plus, si vous redirigez la sortie vers un fichier, vous ne pourrez pas voir les fileurs. A MON HUMBLE AVIS.
Satō Katsura
2
@SatoKatsura ouais, c'est aussi mon avis et c'est pourquoi j'imprime tel quel sur stderr. Cependant, le CTO de la société pour laquelle je travaille estime que l'impression sur stderr est une indication que quelque chose s'est mal passé. J'ai fait la déclaration, plutôt audacieuse, que le POSIX Way® imprimait sur stderr et il m'a appelé. Étant donné qu'il a une vingtaine d'années d'expérience sur moi, j'aimerais voir si je peux trouver une sorte de directive "officielle".
terdon
Comme Sato Katsura l’a dit, stdoutc’est en fait un moyen sûr et correct de relayer les filateurs et d’autres messages informatifs, mais comme le disent de nombreux programmeurs, «le silence est d’or. Ne rien produire si tout va bien. donc en fait, stderr est toujours utilisé pour faire en raison de la définition vague et StdOut aussi peut briser le tube sequence.for guide officiel
Se ême
6
@ Seven, je pense que vous avez mal compris; Sato dit que l'utilisation de stdout n'est pas sûre ("un moyen sûr de rendre les résultats inutiles").
Stephen Kitt
1
Une solution unique serait d'utiliser uniquement stderrpour signaler les messages d'erreur, sauf lorsqu'un --verboseindicateur est utilisé, auquel cas les rapports d'avancement sont également inclus.
Gaurav

Réponses:

27

Posix définit les flux standards ainsi :

Au démarrage du programme, trois flux doivent être prédéfinis et ne doivent pas être ouverts explicitement: entrée standard (pour lire une entrée conventionnelle), sortie standard (pour écrire une sortie conventionnelle) et erreur standard (pour écrire une sortie de diagnostic). Lorsqu'il est ouvert, le flux d'erreur standard n'est pas entièrement mis en mémoire tampon; les flux d'entrée et de sortie standard sont entièrement mis en mémoire tampon si et seulement s'il est possible de déterminer que le flux ne fait pas référence à un périphérique interactif.

La bibliothèque GNU C décrit les flux standard de la même manière:

Variable: FILE * stdout
Le flux de sortie standard, utilisé pour la sortie normale du programme.

Variable: FILE * stderr
Le flux d'erreur standard, utilisé pour les messages d'erreur et les diagnostics émis par le programme.

Ainsi, les définitions standard donnent peu d'indications sur l'utilisation des flux au-delà des «sorties conventionnelles / normales» et «sorties diagnostiques / d'erreurs». En pratique, il est courant de rediriger l'un ou l'autre de ces flux vers des fichiers et des pipelines, où les indicateurs de progression poseront problème. . Certains systèmes surveillent même lesstderr résultats et le considèrent comme un signe de problèmes. Les informations de progression purement auxiliaires sont donc problématiques sur l'un ou l'autre flux.

Au lieu d'envoyer des indicateurs de progression de manière inconditionnelle à l'un ou l' autre des flux standard, il est important de reconnaître que le résultat des progrès n'est approprié que pour les flux interactifs . Dans cet esprit, je vous recommande d'écrire les compteurs de progression uniquement après avoir vérifié si le flux est interactif (par exemple, avec isatty()) ou s'il est explicitement activé par une option de ligne de commande. Cela est particulièrement important pour les compteurs de progression qui s'appuient sur le comportement de mise à jour du terminal, comme les barres%-complètes.

Pour certains messages de progression très simples («Démarrage de X» ... «Terminé avec X»), il est plus raisonnable d'inclure la sortie même pour les flux non interactifs. Dans ce cas, réfléchissez à la manière dont les utilisateurs pourraient interagir avec les flux, tels que la recherche avec grepou la pagination avec lessou la surveillance avec tail -f. S'il est logique de voir les messages de progrès dans ces contextes, ils seront beaucoup plus faciles à consommer stdout.

Bradd Szonye
la source
Merci, la recommandation GNU est le genre de chose que je recherche. Cependant, je me rends compte que ma question était un peu trompeuse. Quand j'ai parlé de "rapports de progrès", je pensais à la fois à des N% donechoses comme doing fooet à foo done. Ces derniers doivent toujours être conservés car ils sont utilisés pour le débogage lorsqu'ils sont redirigés vers un fichier. Donc, votre suggestion de vérifier si le flux est interactif ne s'applique pas (bien que généralement une très bonne idée).
terdon
Ah, merci pour la clarification, c’est un peu différent des choses telles que «% achevé» et les barres de progression hachurées que vous voyez dans les logiciels comme curlet yum. Dans ce cas, je pense savoir si et comment l'utilisateur voudrait interagir avec la sortie via grepou lessou des outils similaires.
Bradd Szonye
Réponse mise à jour pour intégrer les clarifications ci-dessus.
Bradd Szonye
71

POSIX définit l'erreur standard comme

pour écrire une sortie de diagnostic

Cela ne limite pas son utilisation aux seuls messages d'erreur. Je considérerais les informations de progression comme une sortie de diagnostic, ce qui en fait une erreur standard.

Stephen Kitt
la source
8
FWIW, en Python, stdoutest mis en ligne par défaut, alors qu'il stderrest sans tampon , il en stderrva de même pour l'écriture de texte progressif / barres / filateurs ne contenant pas de nouvelle ligne. (Si vous écrivez un tel texte, stdoutvous devez encombrer vos appels de sortie de progression avec stdout.flush()pour le rendre visible).
PM le 2
12
@ PM2Ring non spécifique à Python, POSIX définit les flux de cette façon.
Stephen Kitt
4
@ PM2Ring: Lors de l' écriture stdout, vous devez vider même si votre texte ne contient une nouvelle ligne si vous voulez que vos rapports d'étape pour montrer réellement implémentés en temps réel redirigé.
@Hurkyl Ah, bon point. Je n'envisageais pas de redirection.
PM 2Ring
1
@ Tour, hein? Ansible swallows stderr text si le statut de sortie est 0. Cette demande est simplement fausse.
Charles Duffy
10

Posix est un peu plus concret sur « des informations de diagnostic » dans Shell et utilitaires , 1.4: Utilitaire Description Par défaut (Souligné par ):

STDERR

La section STDERR décrit la sortie d'erreur standard de l'utilitaire. Seuls les messages envoyés à dessein par l'utilitaire sont décrits. L'utilisation d'un terminal pour l'erreur standard peut entraîner l'arrêt de l'un des utilitaires standard qui écrivent une sortie d'erreur standard lorsqu'il est utilisé en arrière-plan. Pour cette raison, les applications ne doivent pas utiliser les fonctionnalités interactives dans les scripts pour être placées en arrière-plan.

Le format des messages de diagnostic pour la plupart des utilitaires n'est pas spécifié, mais les conventions linguistiques et culturelles des messages de diagnostic et d'information dont le format n'est pas spécifié par ce volume de POSIX.1-2008 devraient être affectées par les paramètres LC_MESSAGES et [XSI] [Option Start ] NLSPATH. [Option Fin]

La sortie d'erreur standard spécifiée des utilitaires standard ne doit pas dépendre de l'existence ou de la valeur des variables d'environnement définies dans ce volume de POSIX.1-2008, sauf dans les cas prévus par ce volume de POSIX.1-2008.

Comportement par défaut : Lorsque cette section est listée comme "L’erreur standard ne doit être utilisée que pour les messages de diagnostic.", Cela signifie que, sauf indication contraire, les messages de diagnostic ne doivent être envoyés à l’erreur standard que lorsque l’état de sortie indique une erreur. s'est produite et que l'utilitaire est utilisé comme décrit dans ce volume de POSIX.1-2008.

Lorsque cette section est répertoriée comme "Non utilisé.", Cela signifie que l'erreur standard ne doit pas être utilisée lorsque l'utilitaire est utilisé comme décrit dans ce volume de POSIX.1-2008.

IANASL, mais j’interprète cela comme signifiant que stderr n’aura de sortie que si l’utilitaire renvoie un code de sortie d’erreur. Etant donné que cela ne devrait pas être le cours normal des événements pour une exécution réussie, aucune information de progression ne doit être imprimée par un utilitaire POSIX sauf en cas d'erreur (sauf indication contraire, bien sûr, etc.).

muru
la source
Je comprends cela comme décrivant la signification de la section "STDERR" donnée dans les spécifications de chaque utilitaire POSIX, et non comme une définition générale de l’utilisation des erreurs standard. Je ne pense pas que terdon écrive un utilitaire POSIX standard ici ;-).
Stephen Kitt
1
@StephenKitt non plus. Mais il discute avec son CTO, qui dit que la sortie stderr indique une anomalie, et que la norme prend en charge sa réclamation en tant que comportement par défaut.
muru
1
Il ne prend en charge que la réclamation en tant que comportement par défaut pour les utilitaires POSIX , et même dans certains cas seulement (utilitaires utilisant la phrase mentionnée). Cette partie de POSIX n'est pas normative (AIUI), c'est un modèle: elle vous explique comment lire les spécifications de l'utilitaire, elle ne spécifie pas le comportement de ces utilitaires. Plus précisément, il définit la signification des phrases "l'erreur type ne doit être utilisée que pour les messages de diagnostic." et "Non utilisé." dans les sections "STDERR" du cahier des charges. Chaque utilitaire spécifie son comportement et peut utiliser ces expressions en abrégé.
Stephen Kitt
@StephenKitt qui ne change pas mon point. Les utilitaires POSIX peuvent soit: a) ne pas sortir vers stderr, b) l’utiliser uniquement pour les messages de diagnostic en cas de problème, c) ne l’utiliser que pour les messages de diagnostic, même si rien ne va pas, et d) l’utiliser pour autre chose. J'ai dit qu'ils ne l'utilisaient que pour les messages de diagnostic en cas de problème (a, b), sauf indication contraire (c, d). Maintenant, je prétends que c'est le défaut, ce qui est clairement le cas, car c'est pourquoi c'est un modèle.
muru
2
Eh bien, je dirais que "sauf indication contraire" est une mise en garde assez importante: un utilitaire POSIX pourrait très bien spécifier qu'il génère des informations de progression sur son erreur standard et qu'il serait conforme aux normes. Vous avez raison, il est intéressant de noter que le "comportement par défaut" (qui nécessite encore une mention spécifique dans la section correspondante) consiste à n'utiliser l'erreur standard que si le code de sortie indique également une erreur; mais ce n'est pas limitatif.
Stephen Kitt
7

En vertu du principe d'exclusion, il ne peut aller que jusqu'à stderr. Oui, je sais que vous avez demandé une spécification officielle, que je ne peux pas vous présenter au-delà du lien vers la spécification POSIX, donnée par Stephen Kitt, qui stipule que stderr est utilisé à des fins de diagnostic.

Le point le plus important est que stdin et stdout ont une fonction qui interdit l'impression de rapports de progression sur stdout - ils forment bien sûr la séquence de canaux qui, dans les commandes de shell Unix, n'est pas qu'un simple effet secondaire, mais constitue le cœur même de la puissante approche de pipeline. .

Alors. Rien sauf la vraie "charge utile" de votre programme n'appartient à stdout. Si votre programme n'a pas de sortie, rien ne devrait aller à stdout. Cela laisse stderr pour tout le reste , y compris les rapports d'avancement.

Certes, cela laisse un trou - il serait probablement bien d’avoir un "stdfluff" ou quelque chose du genre qui n’a pour but ni la sortie ni les erreurs, mais les rapports de progression, le débogage, etc. En fait, rien ne vous empêche de le faire, c’est-à-dire que vous pouvez imprimer votre progression dans le descripteur de fichier 3. Exemple:

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

Cela ne produit aucune sortie. (*)

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

Ceci est imprimé sur fd-3, qui est redirigé sur stdout.

(*) Le premier exemple ne produit aucune sortie mais reste un peu tiré par les cheveux; les openéchoue et $!contiendrait no such file or directory; Prenez ceci comme exemple, il ne faut bien sûr pas en faire un usage sérieux. Dans un programme réel, si vous souhaitez emprunter cette voie, vous pouvez vérifier si vous pouvez l' /dev/fd/3utiliser ou non, et prendre cela comme un indice permettant d'activer ou non vos rapports d'avancement. vous devriez le faire assez tôt pour ne pas vous tromper de openfichiers personnels et autres ...

AnoE
la source
C'est quoi fd-3? La troisième disquette?
Peter Mortensen
descripteur de fichier 3, @PeterMortensen
AnoE
-1

Le message d'utilisation doit aller à stdout si l'utilisateur l'a invoqué avec --helpet à stderr s'il s'est trompé. L'imprimer sur stderr inconditionnellement est odieux, car cela rend plus difficile sa visualisation avec un pager / grep / etc.

Aussi, puis-je suggérer une troisième voie: ouvrir / dev / tty pour les rapports d’avancement / tourneurs / etc.

Au hasard832
la source
6
Désolé, mais cela ne répond pas à la question. J'ai spécifiquement demandé des directives officielles , pas des opinions. En tout cas, même si j'avais demandé un avis, vous répondez à une question différente. Je ne parle pas du tout --helpni de messages d'aide. Veuillez relire la question, s'il vous plaît.
terdon
Vous avez parlé de "message d'usage". Je pensais que cela faisait partie de votre question plutôt que le titre de l'autre question que vous aviez liée. Je ne comprends pas pourquoi vous pensez que des "directives officielles" existent pour cela. Qui aurait le pouvoir de les fabriquer?
Random832
3
Je ne sais pas ce qu'ils font, c'est pourquoi je demande. Et comme pour officiel, la norme POSIX a des spécifications pour divers détails minutieux sur le comportement d’un programme. Y compris, comme Stephen l'a souligné dans sa réponse, une vague suggestion de ce qui devrait aller à stderr. La norme de codage GNU pourrait aussi avoir quelque chose de similaire. Fondamentalement, tout groupe qui est actif et produit du code pour l'écosystème * nix pourrait avoir une norme qui pourrait m'intéresser.
terdon
@terdon seriez-vous d'accord avec des exemples de programmes existants? curl, wget et pv utilisent tous stderr, wget et pv le rendant conditionnel à un tty et curl le rendant conditionnel à ce que stdout ne soit pas le tty.
Random832
4
De plus, il semble que votre CTO pense qu'un programme appelant doit réagir à la présence de données sur stderr, ce qui signifie qu'une erreur s'est produite, plutôt que de regarder le statut de sortie. C'est définitivement une mauvaise pratique (et aussi plus difficile à écrire que de vérifier l'état de la sortie) et pourrait être un meilleur angle pour le convaincre.
Random832