web-dev-qa-db-fra.com

Comment rediriger la sortie d'un script Shell entier dans le script lui-même?

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
175
Steve Madsen

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.

137
Jonathan Leffler

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.

155
Joshua

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
28
dbguy

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
6
Eyal leshem
[ -t <&0 ] || exec >> test.log
3
Dimitar