Est-il possible de rediriger toute la sortie d'un script Bourne Shell vers quelque part, mais avec des commandes Shell à l'intérieur du script lui-même?
La redirection de 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), enregistrez la sortie de tout dans un fichier. Si elle est exécutée de manière interactive à partir d’un shell, laissez la sortie aller sur stdout comme d’habitude.
Je veux faire cela pour un script normalement exécuté par l'utilitaire périodique FreeBSD. Cela fait partie du courrier quotidien, ce que je ne tiens normalement pas à voir tous les jours dans les courriels, je ne l’ai donc pas envoyé. Cependant, si quelque chose dans ce script échoue, c'est important pour moi et j'aimerais pouvoir capturer et envoyer par courrier électronique la sortie de cette partie des tâches quotidiennes.
Mise à jour: la réponse de Joshua est parfaite, mais je voulais aussi sauvegarder et restaurer stdout et stderr autour du script entier, 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
Répondre à la question 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 d'E/S. Les accolades doivent apparaître à l'endroit 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 précisé; si vous voulez chipoter, faites le moi savoir. )
Vous avez raison de dire que vous pouvez conserver les options stdout et stderr d'origine avec les redirections que vous avez montrées, mais il est généralement plus simple pour les personnes qui doivent gérer le script plus tard de comprendre ce qui se passe si vous portez le code redirigé comme indiqué ci-dessus.
Les sections pertinentes du manuel Bash sont Commandes de regroupement et Redirection E/S . Les sections pertinentes de la spécification POSIX Shell sont commandes composées et redirection E/S . Bash a quelques notations supplémentaires, mais est par ailleurs similaire à la spécification POSIX Shell.
En général, nous placerions l'un de ces éléments en haut du script ou presque. Les scripts qui analysent leurs lignes de commande effectueraient la redirection après l'analyse.
Envoyer la sortie standard vers un fichier
exec > file
avec stderr
exec > file
exec 2>&1
ajoute les fichiers stdout et stderr au fichier
exec >> file
exec 2>&1
Comme Jonathan Lefflermentionné dans son commentaire :
exec
a deux tâches distinctes. La première consiste à remplacer le shell (script) en cours d'exécution par un nouveau programme. L'autre change les redirections d'E/S dans le shell actuel. Cela se distingue en n'ayant aucun argument pour exec
.
Vous pouvez rendre le script entier une fonction comme celle-ci:
main_function() {
do_things_here
}
alors à 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
Sinon, vous pouvez tout enregistrer dans le fichier journal de chaque exécution et toujours 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
Pour sauvegarder les stdout et stderr d'origine, vous pouvez utiliser:
exec [fd number]<&1
exec [fd number]<&2
Par exemple, le code suivant affichera "walla1" et "walla2" dans le fichier journal (a.txt
), "walla3" sur la sortie, "walla4" sur 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
[ -t <&0 ] || exec >> test.log