J'ai exploré presque toutes les questions similaires disponibles , en vain.
Permettez-moi de décrire le problème en détail:
J'exécute quelques scripts sans assistance et ceux-ci peuvent produire une sortie standard et des lignes d'erreur standard, je veux les capturer dans leur ordre précis affiché par un émulateur de terminal , puis leur ajouter un préfixe comme "STDERR:" et "STDOUT:".
J'ai essayé d'utiliser des tuyaux et même une approche basée sur epoll sur eux, en vain. Je pense que la solution est en usage pty, bien que je ne sois pas maître à cela. J'ai également jeté un œil au code source du VTE de Gnome , mais cela n'a pas été très productif.
Idéalement, j'utiliserais Go au lieu de Bash pour y parvenir, mais je n'y suis pas parvenu. Il semble que les canaux interdisent automatiquement de conserver un ordre de lignes correct en raison de la mise en mémoire tampon.
Quelqu'un a-t-il pu faire quelque chose de similaire? Ou c'est tout simplement impossible? Je pense que si un émulateur de terminal peut le faire, ce n'est pas le cas - peut-être en créant un petit programme C qui gère différemment les PTY?
Idéalement, j'utiliserais une entrée asynchrone pour lire ces 2 flux (STDOUT et STDERR) et les réimprimer ensuite en fonction de mes besoins, mais l'ordre d'entrée est crucial!
REMARQUE: je connais stderred, mais cela ne fonctionne pas pour moi avec les scripts Bash et ne peut pas être facilement modifié pour ajouter un préfixe (car il encapsule essentiellement de nombreux appels système).
Mise à jour: ajouté sous deux points essentiels
(des retards aléatoires d'une seconde peuvent être ajoutés dans l'exemple de script que j'ai fourni pour prouver un résultat cohérent)
Mise à jour: une solution à cette question résoudrait également cette autre question , comme l'a souligné @Gilles. Cependant, je suis arrivé à la conclusion qu'il n'est pas possible de faire ce qui a été demandé ici et là. Lorsque vous utilisez les 2>&1
deux flux, ils sont correctement fusionnés au niveau pty / pipe, mais pour utiliser les flux séparément et dans le bon ordre, vous devez en effet utiliser l'approche de stderred qui évoque le raccordement syscall et peut être considérée comme sale de plusieurs manières.
Je serai impatient de mettre à jour cette question si quelqu'un peut réfuter ce qui précède.
la source
Réponses:
Vous pouvez utiliser des coprocessus. Wrapper simple qui alimente les deux sorties d'une commande donnée à deux
sed
instances (l'une pourstderr
l'autre pourstdout
), qui effectuent le balisage.Notez plusieurs choses:
C'est une incantation magique pour de nombreuses personnes (y compris moi) - pour une raison (voir la réponse liée ci-dessous).
Il n'y a aucune garantie qu'il n'échangera pas occasionnellement quelques lignes - tout dépend de la programmation des coprocessus. En fait, il est presque garanti qu'à un moment donné, ce sera le cas. Cela dit, si vous gardez l'ordre strictement le même, vous devez traiter les données à la fois
stderr
etstdin
dans le même processus, sinon le planificateur du noyau peut (et va) en faire un gâchis.Si je comprends bien le problème, cela signifie que vous devrez demander au shell de rediriger les deux flux vers un seul processus (ce qui peut être fait AFAIK). Le problème commence lorsque ce processus commence à décider sur quoi agir en premier - il devrait interroger les deux sources de données et à un moment donné se mettre dans l'état où il traiterait un flux et les données arriveraient aux deux flux avant de se terminer. Et c'est exactement là où ça tombe en panne. Cela signifie également que l'encapsulation des appels système de sortie comme
stderred
est probablement le seul moyen d'atteindre le résultat souhaité (et même alors, vous pourriez avoir un problème une fois que quelque chose devient multithread sur un système multiprocesseur).En ce qui concerne les coprocessions, assurez-vous de lire l'excellente réponse de Stéphane dans Comment utilisez-vous la commande coproc dans Bash? pour un aperçu en profondeur.
la source
./test1.sh: 3: ./test1.sh: Syntax error: "(" unexpected
enbash
avec/bin/sh
(je ne sais pas pourquoi je l'avais là-bas).eval $@
est assez bogué. À utiliser"$@"
si vous souhaitez exécuter vos arguments en tant que ligne de commande exacte - l'ajout d'une couche d'eval
interprétation génère un tas de données difficiles à prévoir (et potentiellement malveillantes, si vous transmettez des noms de fichiers ou d'autres contenus que vous ne contrôlez pas comme arguments), et en omettant de citer même le moreso (divise les noms avec des espaces en plusieurs mots, développe les globes même s'ils ont été précédemment cités pour être littéraux, etc.).eval
de fermer les descripteurs de fichiers nommés dans une variable.exec {SEDo[1]}>&-
fonctionnera tel quel (oui, l'absence d'un$
avant le{
était délibéré).Méthode n ° 1. Utilisation de descripteurs de fichiers et awk
Qu'en est-il de quelque chose comme ça en utilisant les solutions de ce SO Q&A intitulé: Existe - t-il un utilitaire Unix pour ajouter des horodatages aux lignes de texte? et ce SO Q&A intitulé: pipe STDOUT et STDERR à deux processus différents dans un script shell? .
L'approche
Étape 1, nous créons 2 fonctions dans Bash qui exécuteront le message d'horodatage lors de l'appel:
Étape 2, vous utiliseriez les fonctions ci-dessus comme pour obtenir la messagerie souhaitée:
Exemple
Ici, j'ai concocté un exemple qui va écrire
a
dans STDOUT, dort pendant 10 secondes, puis écrit la sortie dans STDERR. Lorsque nous mettons cette séquence de commandes dans notre construction ci-dessus, nous obtenons des messages comme vous l'avez spécifié.Méthode n ° 2. Utilisation d'annoter-sortie
Il existe un outil appelé
annotate-output
faisant partie dudevscripts
package qui fera ce que vous voulez. Sa seule restriction est qu'il doit exécuter les scripts pour vous.Exemple
Si nous mettons notre exemple de séquence de commandes ci-dessus dans un script appelé
mycmds.bash
ainsi:Nous pouvons ensuite l'exécuter comme ceci:
Le format de la sortie peut être contrôlé pour la partie d'horodatage mais pas au-delà. Mais c'est une sortie similaire à ce que vous recherchez, donc cela peut correspondre à la facture.
la source
stderred
vous ne peut pas facilement déterminer les limites des lignes (essayer serait donc hackish). Je voulais voir si quelqu'un pouvait m'aider avec ce problème mais apparemment tout le monde veut renoncer à la seule contrainte ( ordre ) qui est la base de la question