Forcer le script bash à utiliser tee sans tuyauterie depuis la ligne de commande

20

J'ai un script bash avec beaucoup de sortie et je voudrais changer ce script (pas en créer un nouveau) pour copier toute la sortie dans un fichier, tout comme cela se produirait si je le canalisais via tee.

J'ai un fichier script.sh :

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini

et je voudrais faire une copie de STDOUT dans un fichier script.log sans avoir à taper à ./script.sh | tee script.logchaque fois.

Merci Tom

À M
la source
Je suis désolé de ne pas l'avoir dit plus tôt, mais le script est assez long, c'est pourquoi je cherche une solution simple. Ajout | tee à chaque ligne est un peu trop.
Tom

Réponses:

15

Je ne pouvais pas faire fonctionner le très simple one-liner de Dennis, alors voici une méthode beaucoup plus compliquée. J'essaierais le premier.

Comme mentionné, vous pouvez utiliser exec pour rediriger l'erreur standard et la sortie standard pour l'ensemble du script. Comme ceci:
exec > $LOGFILE 2>&1 Cela affichera tous les stderr et stdout dans $ LOGFILE.

Maintenant, puisque vous voulez que cela soit affiché sur la console ainsi qu'un fichier journal, vous devrez également utiliser un canal nommé pour que exec puisse écrire et té pour lire.
(La doublure de Dennis fait également cela techniquement, bien que de manière évidemment différente) La pipe elle-même est créée avec mkfifo $PIPEFILE. Procédez ensuite comme suit.

# Commencez à écrire dans un fichier journal, mais en tirant son entrée de notre pipe nommée.
tee $ LOGFILE <$ PIPEFILE &

# capture l'ID de processus de tee pour la commande d'attente.
TEEPID = $!

# redirige le reste du stderr et du stdout vers notre pipe nommée.
exec> $ PIPEFILE 2> & 1

echo "Faites vos commandes ici"
echo "Tous leurs standards sortiront."
echo "Il en sera de même de leur erreur standard"> & 2

# fermez les descripteurs de fichiers stderr et stdout.
exec 1> & - 2> & -

# Attendez que le tee se termine depuis que l'autre extrémité du tuyau est fermée.
attendre $ TEEPID

Si vous voulez être minutieux, vous pouvez créer et détruire le fichier de canal nommé au début et à la fin de votre script.

Pour mémoire, j'ai glané la plupart de cela dans un article de blog très informatif d'un gars au hasard: ( Version archivée )

Christopher Karel
la source
Cela ne semble pas fonctionner dans cygwin. :-(
docwhat
Ce poste fait un bon travail d'éducation. Merci beaucoup.
Felipe Alvarez
27

Ajoutez simplement ceci au début de votre script:

exec > >(tee -ia script.log)

Cela ajoutera toutes les sorties envoyées à stdout au fichier script.log, en laissant le contenu précédent en place. Si vous souhaitez recommencer chaque fois que le script est exécuté, ajoutez-le rm script.logjuste avant cette execcommande ou supprimez-le -ade la teecommande.

L' -ioption fait teeignorer les signaux d'interruption et peut permettre teed'attraper un jeu de sortie plus complet.

Ajoutez cette ligne pour également détecter toutes les erreurs (dans un fichier séparé):

exec 2> >(tee -ia scripterr.out)

Les espaces entre les multiples >symboles sont importants.

En pause jusqu'à nouvel ordre.
la source
Solution très intéressante.
ℝaphink
2
C'est une façon vraiment élégante de le faire, par rapport à toutes les conneries que j'ai publiées. Mais lorsque je crée un script avec ces deux lignes, il se bloque et ne se ferme jamais correctement.
Christopher Karel,
Quelle version de Bash?
pause jusqu'à nouvel ordre.
GNU bash, version 3.2.25 (1) dans le cadre d'une installation RHEL5.3. Il semble que ce soit la dernière version des dépôts officiels.
Christopher Karel
Je viens de l'essayer sous cygwin dans Bash 3.2.49 (23) -release et j'ai des erreurs de descripteur de fichier. Je travaille dans Bash 4.0.33 (1) -release dans Ubuntu 9.10, cependant, il peut donc s'agir de Bash 4.
pause jusqu'à nouvel ordre.
6

Ceci est une version combinée de la réponse publiée par Dennis Williamson plus tôt. Ajoute à la fois err & std à script.log dans le bon ordre.

Ajoutez cette ligne au début de votre script:

exec > >(tee -a script.log) 2>&1

Notez l'espace entre les >panneaux. Fonctionne pour moi sur GNU bash, version 3.2.25 (1) -release (x86_64-redhat-linux-gnu)

ttt
la source
5

Je suppose qu'une autre approche ressemble à ceci:

#!/bin/bash

# start grouping at the top
{ 
    # your script goes here
    do_something_that_outputs_stuff_to_STDOUT
    launch_applications_that_output_to_STDOUT

# and at the bottom, redirect everything from the grouped commands
} 2>&1 | tee script.log 
glenn jackman
la source
3

Si vous souhaitez une copie de la sortie, vous pouvez utiliser teecette méthode:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT | tee script.log
  launch_applications_that_output_to_STDOUT | tee -a script.log
  fini

Cela n'enregistrera cependant stdout que dans script.log. Si vous voulez vous assurer que les deux stderr et stdout sont redirigés, utilisez:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT 2>&1 | tee script.log
  launch_applications_that_output_to_STDOUT 2>&1 | tee -a script.log
  fini

Vous pourriez même le rendre un peu plus agréable avec une petite fonction:

  #!/bin/bash

  LOGFILE="script.log"
  # Wipe LOGFILE if you don't want to add to it at each run
  rm -f "$LOGFILE"

  function logcmd() {
        local cmd="$1" logfile=${LOGFILE:-script.log} # Use default value if unset earlier

        # Launch command, redirect stderr to stdout and append to $logfile
        eval '$cmd' 2>&1 | tee -a "$logfile"
  }

  init
  logcmd "do_something_that_outputs_stuff_to_STDOUT"
  logcmd "launch_applications_that_output_to_STDOUT"
  fini
ℝaphink
la source
Bonne réponse. S'il ne se soucie vraiment que de la 2>&1sortie standard, je laisserais juste le dehors et je le mettrais juste au tee.
DaveParillo
C'est vrai, j'ai seulement inclus stderr parce que la réponse précédente d'Igor tenait à le rediriger et je pensais que c'était une bonne idée.
ℝaphink
1

Vous utiliseriez simplement ceci:

script logfile.txt
your script or command here

Cela renvoie tout dans ce journal, tout, ainsi que l'afficher à l'écran. Si vous voulez la sortie FULL, voici comment procéder.

Et vous pouvez ensuite rejouer le script si vous êtes paresseux.

Phishy
la source
Pourrait être bon de noter qu'il scripts'agit d'un package, par exemple. sudo apt-get install scriptpour l'installer sur des distributions à saveur Debian , il script -ac 'command opts' ~/Documents/some_log.scriptpeut en outre être examiné dans un terminal via quelque chose comme strings ~/Documents/some_log.script | more; stringsêtre un moyen simple de pré-désinfecter certaines des choses qui script injectent pour permettre des méthodes plus sophistiquées de relecture / visualisation. Sinon, une réponse solide @Phishy, ​​car scriptelle préserve également les choses interactives.
S0AndS0
0
#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT > script.log 2>&1
launch_applications_that_output_to_STDOUT >> script.log 2>&1
fini

Vous pouvez en savoir plus sur son fonctionnement ici: http://www.xaprb.com/blog/2006/06/06/what-does-devnull-21-mean/

Igor Zinov'yev
la source
1
J'ai ajouté un deuxième> à la deuxième ligne dans la réponse d'Igor afin qu'il n'efface pas le journal écrit dans la première ligne.
Doug Harris
1
Il veut cependant une copie, il doit donc l'utiliser tee.
ℝaphink
@Raphink: C'est vrai :)
Tom