Je sais comment rediriger stdout vers un fichier:
exec > foo.log
echo test
cela mettra le «test» dans le fichier foo.log.
Maintenant, je veux rediriger la sortie dans le fichier journal ET le garder sur stdout
c'est-à-dire que cela peut être fait trivialement en dehors du script:
script | tee foo.log
mais je veux le déclarer dans le script lui-même
j'ai essayé
exec | tee foo.log
mais ça n'a pas marché.
Réponses:
Notez que ce n'est
bash
pas le cassh
. Si vous appelez le script avecsh myscript.sh
, vous obtiendrez une erreur dans le sens desyntax error near unexpected token '>'
.Si vous travaillez avec des interruptions de signal, vous souhaiterez peut-être utiliser l'
tee -i
option pour éviter de perturber la sortie si un signal se produit. (Merci à JamesThomasMoon1979 pour le commentaire.)Les outils qui modifient leur sortie selon qu'ils écrivent dans un tuyau ou un terminal (en
ls
utilisant des couleurs et une sortie en colonnes, par exemple) détecteront la construction ci-dessus comme signifiant qu'ils sortent vers un tuyau.Il existe des options pour appliquer la colorisation / la mise en colonnes (par exemple
ls -C --color=always
). Notez que cela entraînera également l'écriture des codes couleur dans le fichier journal, ce qui le rendra moins lisible.la source
tee
ne doit pas mettre en tampon sa sortie. S'il fait un tampon sur la plupart des systèmes, il est cassé sur la plupart des systèmes. C'est un problème d'tee
implémentations, pas de ma solution.exec
est très puissant, mais aussi très impliqué. Vous pouvez "sauvegarder" la sortie standard actuelle vers un autre descripteur de fichier, puis la récupérer plus tard. Google "bash exec tutorial", il y a beaucoup de choses avancées là-bas.exec
est documenté pour ne pas démarrer de nouveaux processus,>(tee ...)
est une substitution standard nommée pipe / process, et&
la redirection n'a bien sûr rien à voir avec l'arrière-plan ...? :-)-i
àtee
. Sinon, les interruptions de signal (interruptions) perturberont la sortie standard du script principal. Par exemple, si vous en avez untrap 'echo foo' EXIT
puis appuyez surctrl+c
, vous ne verrez pas " foo ". Je modifierais donc la réponse àexec &> >(tee -ia file)
.La réponse acceptée ne conserve pas STDERR en tant que descripteur de fichier distinct. Cela signifie
ne sortira pas
bar
sur le terminal, uniquement sur le fichier journal, etaffichera à la fois
foo
etbar
vers le terminal. De toute évidence, ce n'est pas le comportement auquel un utilisateur normal est susceptible de s'attendre. Cela peut être résolu en utilisant deux processus de départ distincts, tous deux ajoutés au même fichier journal:(Notez que ce qui précède ne tronque pas initialement le fichier journal - si vous voulez ce comportement, vous devez ajouter
en haut du script.)
La spécification POSIX.1-2008 de
tee(1)
requiert que la sortie ne soit pas mise en mémoire tampon, c'est-à-dire même pas mise en mémoire tampon de ligne, donc dans ce cas, il est possible que STDOUT et STDERR puissent se retrouver sur la même ligne defoo.log
; cependant, cela pourrait également se produire sur le terminal, de sorte que le fichier journal sera un reflet fidèle de ce qui pourrait être vu sur le terminal, sinon un miroir exact de celui-ci. Si vous souhaitez que les lignes STDOUT soient clairement séparées des lignes STDERR, envisagez d'utiliser deux fichiers journaux, éventuellement avec des préfixes de date sur chaque ligne pour permettre un réassemblage chronologique plus tard.la source
exec > >(tee -a $LOG)
trap "kill -9 $! 2>/dev/null" EXIT
exec 2> >(tee -a $LOG >&2)
trap "kill -9 $! 2>/dev/null" EXIT
-i
àtee
. Sinon, les interruptions de signal (interruptions) perturberont la sortie standard du script. Par exemple, si voustrap 'echo foo' EXIT
appuyez surctrl+c
, vous ne verrez pas " foo ". Je modifierais donc la réponse àexec > >(tee -ia foo.log)
.. log
ou. log foo.log
: sam.nipl.net/sh/log sam.nipl.net/sh/log-aSTDOUT
apparaître en premier comme un lot, puis les messages vontSTDERR
apparaître. Ils ne sont pas entrelacés comme prévu.Solution pour les boîtiers busybox, macOS bash et non-bash
La réponse acceptée est certainement le meilleur choix pour bash. Je travaille dans un environnement Busybox sans accès à bash, et il ne comprend pas la
exec > >(tee log.txt)
syntaxe. Il ne fonctionne pas non plusexec >$PIPE
correctement, essayant de créer un fichier ordinaire avec le même nom que le canal nommé, qui échoue et se bloque.J'espère que cela serait utile à quelqu'un d'autre qui n'a pas bash.
En outre, pour toute personne utilisant un canal nommé, cela est sûr
rm $PIPE
, car cela dissocie le canal du VFS, mais les processus qui l'utilisent conservent toujours un compte de référence jusqu'à ce qu'ils soient terminés.Notez que l'utilisation de $ * n'est pas nécessairement sûre.
la source
Dans votre fichier de script, mettez toutes les commandes entre parenthèses, comme ceci:
la source
{}
)Un moyen facile de créer un journal de script bash dans syslog. La sortie du script est disponible à la fois via
/var/log/syslog
et via stderr. syslog ajoutera des métadonnées utiles, notamment des horodatages.Ajoutez cette ligne en haut:
Vous pouvez également envoyer le journal dans un fichier distinct:
Cela nécessite
moreutils
(pour lats
commande, qui ajoute des horodatages).la source
En utilisant la réponse acceptée, mon script revenait exceptionnellement tôt (juste après 'exec>> (tee ...)'), laissant le reste de mon script en arrière-plan. Comme je n'ai pas réussi à faire fonctionner cette solution à ma façon, j'ai trouvé une autre solution / solution au problème:
Cela fait que la sortie du script passe du processus, par le canal, au sous-processus en arrière-plan de «tee» qui enregistre tout sur le disque et sur la sortie standard du script.
Notez que 'exec &>' redirige à la fois stdout et stderr, nous pouvons les rediriger séparément si nous le souhaitons, ou changer en 'exec>' si nous voulons juste stdout.
Même si le canal est supprimé du système de fichiers au début du script, il continuera de fonctionner jusqu'à la fin des processus. Nous ne pouvons tout simplement pas le référencer en utilisant le nom de fichier après la ligne rm.
la source
$logfile
partie detee < ${logfile}.pipe $logfile &
. Plus précisément, j'ai essayé de modifier cela pour capturer les lignes de journal de commande développées complètes (deset -x
) dans un fichier tout en affichant uniquement les lignes sans diriger «+» dans stdout en modifiant(tee | grep -v '^+.*$') < ${logfile}.pipe $logfile &
mais en recevant un message d'erreur concernant$logfile
. Pouvez-vous expliquer latee
ligne un peu plus en détail?Bash 4 possède une
coproc
commande qui établit un canal nommé vers une commande et vous permet de communiquer à travers elle.la source
Je ne peux pas dire que je suis à l'aise avec l'une des solutions basées sur exec. Je préfère utiliser directement tee, donc je fais appeler le script avec tee lorsque demandé:
Cela vous permet de faire ceci:
Vous pouvez personnaliser cela, par exemple faire de tee = false la valeur par défaut à la place, faire en sorte que TEE conserve le fichier journal, etc. Je suppose que cette solution est similaire à celle de jbarlow, mais plus simple, peut-être que la mienne a des limites que je n'ai pas encore rencontrées.
la source
Aucun de ces deux n'est une solution parfaite, mais voici quelques choses que vous pouvez essayer:
ou
Le second laisserait un fichier pipe assis en cas de problème avec votre script, ce qui pourrait ou non être un problème (c'est-à-dire que vous pourriez le
rm
faire dans le shell parent par la suite).la source
tee
- j'ai édité. Comme je l'ai dit, ni l'un ni l'autre n'est une solution parfaite, mais les processus d'arrière-plan seront tués lorsque leur shell parent se terminera, vous n'avez donc pas à vous soucier qu'ils monopolisent les ressources pour toujours.