283 votes

Comment rediriger la sortie d'un script entier du shell à l'intérieur du script lui-même ?

Est-il possible de rediriger toute la sortie d'un shell Bourne script vers quelque part, mais avec des commandes shell à l'intérieur du script lui-même ?

Rediriger la sortie d'une seule commande est facile, mais je veux quelque chose de plus semblable à ceci :

#!/bin/sh
if [ ! -t 0 ]; then
    # redirect all of my output to a file here
fi

# rest of script...

Signification : si le script est exécuté de manière non interactive (par exemple, cron), enregistre la sortie de tout dans un fichier. S'il est exécuté de manière interactive depuis un shell, laissez la sortie aller vers stdout comme d'habitude.

Je veux faire cela pour un script normalement exécuté par l'utilitaire périodique de FreeBSD. Il fait partie de l'exécution quotidienne, que je n'ai pas l'habitude de voir tous les jours dans un courrier électronique, donc je ne l'ai pas envoyé. Cependant, si quelque chose à l'intérieur de ce script particulier échoue, c'est important pour moi et j'aimerais pouvoir capturer et envoyer par courrier électronique la sortie de cette partie des travaux quotidiens.

Mise à jour : La réponse de Joshua est exacte, mais je voulais également sauvegarder et restaurer stdout et stderr autour de l'ensemble du script, ce qui se fait comme suit :

# save stdout and stderr to file 
# descriptors 3 and 4, 
# then redirect them to "foo"
exec 3>&1 4>&2 >foo 2>&1

# ...

# restore stdout and stderr
exec 1>&3 2>&4

2 votes

Tester pour $TERM n'est pas la meilleure façon de tester le mode interactif. A la place, testez si stdin est un tty (test -t 0).

2 votes

En d'autres termes : if [ ! -t 0 ] ; then exec >somefile 2>&1 ; fi

1 votes

Voir ici pour toutes les bonnes choses : http://tldp.org/LDP/abs/html/io-redirection.html En gros, ce qui a été dit par Joshua. exec > file redirige stdout vers un fichier spécifique, exec < file remplace stdin par file, etc. C'est la même chose que d'habitude mais en utilisant exec (voir man exec pour plus de détails).

263voto

Jonathan Leffler Points 299946

Répondre à la question telle qu'elle a été mise à jour.

#...part of script without redirection...

{
    #...part of script with redirection...
} > file1 2>file2 # ...and others as appropriate...

#...residue of script without redirection...

Les accolades '{ ... }' fournissent une unité de redirection des entrées/sorties. Les accolades doivent apparaître là où une commande pourrait apparaître - de manière simpliste, au début d'une ligne ou après un point-virgule. ( Oui, cela peut être rendu plus précis ; si vous voulez chipoter, faites-le moi savoir. )

Vous avez raison de dire que vous pouvez préserver le stdout et le stderr originaux avec les redirections que vous avez montrées, mais il est généralement plus simple pour les personnes qui doivent maintenir le script plus tard de comprendre ce qui se passe si vous scopez le code redirigé comme indiqué ci-dessus.

Les sections pertinentes du manuel Bash sont les suivantes Commandes de regroupement y Redirection des E/S . Les sections pertinentes de la spécification de l'interpréteur de commandes POSIX sont les suivantes Commandes composées y Redirection des E/S . Bash a quelques notations supplémentaires, mais est autrement similaire à la spécification POSIX shell.

5 votes

C'est beaucoup plus clair que de sauvegarder les descripteurs originaux et de les restaurer plus tard.

44 votes

J'ai dû faire quelques recherches sur Google pour comprendre ce que cela fait vraiment, alors je voulais partager. Les accolades deviennent un "bloc de code" ce qui, en fait, crée un fonction anonyme . La sortie tout dans le bloc de code peut alors être redirigée (Voir Exemple 3-2 de ce lien). Notez également que les accolades ne pas lancer un Sous-coque mais similaire Redirections d'E/S puede être fait avec des sous-ensembles en utilisant des parenthèses.

4 votes

J'aime mieux cette solution que les autres. Même une personne n'ayant que des notions de base sur la redirection des E/S peut comprendre ce qui se passe. De plus, elle est plus verbeuse. Et, en tant que Pythonien, j'aime le verbeux.

240voto

Joshua Points 13231

En général, nous en plaçons un en haut ou près du haut du script. Les scripts qui analysent leurs lignes de commande effectueraient la redirection après l'analyse.

Envoyer stdout vers un fichier

exec > file

avec stderr

exec > file                                                                      
exec 2>&1

ajouter les deux stdout et stderr au fichier

exec >> file
exec 2>&1

Comme Jonathan Leffler mentionné dans son commentaire :

exec a deux emplois distincts. La première consiste à remplacer le shell en cours d'exécution (script) par un nouveau programme. L'autre consiste à modifier les redirections d'entrée/sortie dans le shell actuel. Elle se distingue par le fait qu'elle n'a pas d'argument pour exec .

8 votes

Je dirais qu'il faut aussi ajouter 2>&1 à la fin, juste pour que stderr soit pris aussi :-)

1 votes

Un badge éclairé pour l'arme la plus rapide de l'ouest il y a des années.

0 votes

Vous n'avez pas idée du nombre de badges d'illumination que j'ai obtenu de cette façon. :-P

44voto

dbguy Points 66

Vous pouvez faire de l'ensemble du script une fonction comme ceci :

main_function() {
  do_things_here
}

puis à la fin du script avoir ceci :

if [ -z $TERM ]; then
  # if not run via terminal, log everything into a log file
  main_function 2>&1 >> /var/log/my_uber_script.log
else
  # run via terminal, only output to screen
  main_function
fi

Alternativement, vous pouvez tout enregistrer dans le fichier journal à chaque exécution et le sortir sur stdout en faisant simplement :

# log everything, but also output to stdout
main_function 2>&1 | tee -a /var/log/my_uber_script.log

1 votes

Tu voulais dire main_function >> /var/log/my_uber_script.log 2>&1

0 votes

J'aime utiliser la fonction main_function dans ce genre de tuyau. Mais dans ce cas, votre script ne renvoie pas la valeur de retour originale. Dans le cas de bash, vous devriez sortir en utilisant 'exit ${PIPESTATUS[0]}'.

9voto

Eyal leshem Points 329

Pour sauvegarder les stdout et stderr originaux, vous pouvez utiliser :

exec [fd number]<&1 
exec [fd number]<&2

Par exemple, le code suivant imprimera "walla1" et "walla2" dans le fichier journal ( a.txt ), "walla3" vers stdout, "walla4" vers stderr.

#!/bin/bash

exec 5<&1
exec 6<&2

exec 1> ~/a.txt 2>&1

echo "walla1"
echo "walla2" >&2
echo "walla3" >&5
echo "walla4" >&6

3 votes

Normalement, il serait préférable d'utiliser exec 5>&1 y exec 6>&2 en utilisant la notation de redirection des sorties plutôt que la notation de redirection des entrées pour les sorties. Vous vous en tirez parce que lorsque le script est exécuté à partir d'un terminal, l'entrée standard est également accessible en écriture et la sortie standard et l'erreur standard sont lisibles en vertu (ou est-ce un 'vice' ?) d'une bizarrerie historique : le terminal est ouvert pour la lecture et l'écriture et le même description du fichier ouvert est utilisé pour les trois descripteurs de fichiers d'E/S standard.

4voto

Dimitar Points 21
[ -t <&0 ] || exec >> test.log

2 votes

Qu'est-ce que ça fait ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X