J'ai un script qui sera exécuté de manière interactive par des utilisateurs non techniques. Le script écrit les mises à jour d'état dans STDOUT afin que l'utilisateur puisse être sûr que le script s'exécute correctement.
Je veux que STDOUT et STDERR soient redirigés vers le terminal (afin que l'utilisateur puisse voir que le script fonctionne ainsi que voir s'il y avait un problème). Je veux également que les deux flux soient redirigés vers un fichier journal.
J'ai vu un tas de solutions sur le net. Certains ne fonctionnent pas et d'autres sont horriblement compliqués. J'ai développé une solution viable (que je vais entrer comme réponse), mais c'est kludgy.
La solution parfaite serait une seule ligne de code qui pourrait être incorporée au début de tout script qui envoie les deux flux à la fois au terminal et à un fichier journal.
EDIT: Rediriger STDERR vers STDOUT et envoyer le résultat vers le tee fonctionne, mais cela dépend du fait que les utilisateurs se souviennent de rediriger et de diriger la sortie. Je veux que la journalisation soit infaillible et automatique (c'est pourquoi j'aimerais pouvoir intégrer la solution dans le script lui-même.)
Réponses:
Utilisez "tee" pour rediriger vers un fichier et l'écran. Selon le shell que vous utilisez, vous devez d'abord rediriger stderr vers stdout en utilisant
ou
Dans csh, il existe une commande intégrée appelée "script" qui capturera tout ce qui va à l'écran dans un fichier. Vous le démarrez en tapant "script", puis en faisant ce que vous voulez capturer, puis appuyez sur control-D pour fermer le fichier de script. Je ne connais pas d'équivalent pour sh / bash / ksh.
De plus, puisque vous avez indiqué qu'il s'agit de vos propres scripts sh que vous pouvez modifier, vous pouvez effectuer la redirection en interne en entourant tout le script d'accolades ou de crochets, comme
la source
2>&1 | tee -a filename
je n'ai pas enregistré stderr dans le fichier à partir de mon script, mais cela a bien fonctionné lorsque j'ai copié la commande et l'ai collée dans le terminal! L'astuce du support fonctionne bien, cependant.Près d'une demi-décennie plus tard ...
Je pense que c'est la «solution parfaite» recherchée par le PO.
Voici une seule ligne que vous pouvez ajouter en haut de votre script Bash:
Voici un petit script démontrant son utilisation:
(Remarque: cela ne fonctionne qu'avec Bash. Cela ne fonctionnera pas avec / bin / sh.)
Adapté d' ici ; l'original n'a pas, d'après ce que je peux dire, attraper STDERR dans le fichier journal. Corrigé avec une note d' ici .
la source
Le motif
Cela redirige à la fois stdout et stderr séparément, et il envoie des copies séparées de stdout et stderr à l'appelant (qui pourrait être votre terminal).
Dans zsh, il ne passera pas à l'instruction suivante tant que les
tee
s ne seront pas terminés.Dans bash, vous constaterez peut-être que les dernières lignes de sortie apparaissent après la déclaration suivante.
Dans les deux cas, les bons bits vont aux bons endroits.
Explication
Voici un script (stocké dans ./example):
Voici une session:
Voici comment ça fonctionne:
tee
processus sont lancés, leurs stdins sont affectés à des descripteurs de fichiers. Comme ils sont inclus dans des substitutions de processus , les chemins vers ces descripteurs de fichiers sont substitués dans la commande appelante, donc maintenant cela ressemble à ceci:the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14
the_cmd
s'exécute, écrivant stdout dans le premier descripteur de fichier et stderr dans le second.Dans le cas bash, une fois
the_cmd
terminé, l'instruction suivante se produit immédiatement (si votre terminal est l'appelant, alors vous verrez votre invite apparaître).Dans le cas de zsh, une fois
the_cmd
terminé, le shell attend la fin des deuxtee
processus avant de continuer. Plus d'informations ici .Le premier
tee
processus, qui lit depuisthe_cmd
le stdout de, écrit une copie de ce stdout vers l'appelant parce que c'est ce quetee
fait. Ses sorties ne sont pas redirigées, donc elles le ramènent à l'appelant inchangéLe deuxième
tee
processus eststdout
redirigé vers l'appelantstderr
(ce qui est bien, car c'est stdin qui lit depuisthe_cmd
le stderr de). Ainsi, quand il écrit dans sa sortie stdout, ces bits vont dans le stderr de l'appelant.Cela permet de séparer stderr de stdout à la fois dans les fichiers et dans la sortie de la commande.
Si le premier tee écrit des erreurs, elles apparaîtront à la fois dans le fichier stderr et dans le stderr de la commande, si le second tee écrit des erreurs, elles n'apparaîtront que dans le stderr du terminal.
la source
tee
est disponible sur le système en question.) L'erreur que j'obtiens est "Le processus ne peut pas accéder au fichier car il est utilisé par un autre processus."le pour rediriger stderr vers stdout, ajoutez ceci à votre commande:
2>&1
Pour la sortie vers le terminal et la connexion au fichier, vous devez utilisertee
Les deux ensemble ressembleraient à ceci:
EDIT: Pour l'intégration dans votre script, vous feriez de même. Donc votre script
finirait comme
la source
EDIT: Je vois que j'ai déraillé et j'ai fini par répondre à une question différente de celle posée. La réponse à la vraie question se trouve au bas de la réponse de Paul Tomblin. (Si vous souhaitez améliorer cette solution pour rediriger stdout et stderr séparément pour une raison quelconque, vous pouvez utiliser la technique que je décris ici.)
Je voulais une réponse qui préserve la distinction entre stdout et stderr. Malheureusement, toutes les réponses données jusqu'à présent qui préservent cette distinction sont sujettes à la race: elles risquent que les programmes voient des contributions incomplètes, comme je l'ai souligné dans mes commentaires.
Je pense que j'ai finalement trouvé une réponse qui préserve la distinction, n'est pas sujette à la race et n'est pas non plus terriblement délicate.
Premier bloc de construction: pour permuter stdout et stderr:
Deuxième bloc de construction: si nous voulions filtrer (par exemple tee) uniquement stderr, nous pourrions le faire en échangeant stdout et stderr, filtrer, puis inverser:
Maintenant, le reste est simple: nous pouvons ajouter un filtre stdout, soit au début:
ou à la fin:
Pour me convaincre que les deux commandes ci-dessus fonctionnent, j'ai utilisé ce qui suit:
La sortie est:
et mon invite revient immédiatement après le "
teed stderr: to stderr
", comme prévu.Note de bas de page sur zsh :
La solution ci-dessus fonctionne dans bash (et peut-être dans d'autres shells, je ne suis pas sûr), mais cela ne fonctionne pas dans zsh. Il y a deux raisons pour lesquelles il échoue dans zsh:
2>&3-
n'est pas comprise par zsh; qui doit être réécrit comme2>&3 3>&-
Ainsi, par exemple, ma deuxième solution doit être réécrite pour zsh comme
{my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(qui fonctionne aussi dans bash, mais est terriblement verbeuse).D'un autre côté, vous pouvez profiter du mystérieux départ implicite intégré de zsh pour obtenir une solution beaucoup plus courte pour zsh, qui ne fonctionne pas du tout:
(Je n'aurais pas deviné d'après les documents que j'ai trouvé que le
>&1
et2>&2
sont la chose qui déclenche le départ implicite de zsh; je l'ai découvert par essais et erreurs.)la source
my_function
) dans le filtrage stdout / stderr imbriqué? Je l'ai fait{ { my_function || touch failed;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
mais ça fait bizarre de créer un fichier comme indicateur d'échec ...Utilisez la
script
commande dans votre script (script man 1)Créez un shellscript wrapper (2 lignes) qui configure script (), puis appelle exit.
Partie 1: wrap.sh
Partie 2: realscript.sh
Résultat:
la source
Utilisez le programme tee et dup stderr pour stdout.
la source
J'ai créé un script appelé "RunScript.sh". Le contenu de ce script est:
Je l'appelle comme ça:
Cela fonctionne, mais cela nécessite que les scripts de l'application soient exécutés via un script externe. C'est un peu kludgy.
la source
Un an plus tard, voici un ancien script bash pour enregistrer quoi que ce soit. Par exemple,
teelog make ...
enregistre sous un nom de journal généré (et voyez aussi l'astuce pour journaliser lesmake
s imbriqués .)la source