comment afficher du texte à la fois à l'écran et au fichier dans un script shell?

49

Actuellement, j'ai un script shell qui enregistre les messages dans un fichier journal comme ceci:

log_file="/some/dir/log_file.log"
echo "some text" >> $log_file
do_some_command
echo "more text" >> $log_file
do_other_command

Lors de l'exécution de ce script, il n'y a pas de sortie à l'écran et, comme je me connecte au serveur via putty, je dois ouvrir une autre connexion et faire "tail -f log_file_path.log", car je ne peux pas terminer l'exécution. script et je veux voir la sortie en temps réel.

De toute évidence, ce que je veux, c’est que les messages texte soient imprimés à l’écran et dans un fichier, mais j’aimerais le faire sur une seule ligne et non sur deux lignes, l’une d’elles ne pouvant pas être redirigée.

Comment y parvenir?

jurchiks
la source

Réponses:

71

Cela marche:

command | tee -a "$log_file"

teeenregistre les entrées dans un fichier (utilisez -apour ajouter plutôt que pour remplacer), et copie également l’entrée dans la sortie standard.

l0b0
la source
8

Vous pouvez utiliser un ici-doc et. recherchez-le pour un modèle de collecteur général efficace et convivial pour POSIX.

. 8<<-\IOHERE /proc/self/fd/8

command
 
fn() { declaration ; } <<WHATEVER
# though a nested heredoc might be finicky
# about stdin depending on shell
WHATEVER
cat -u ./stdout | ./works.as >> expect.ed
IOHERE

Lorsque vous ouvrez l'hérédoc, vous indiquez au shell un jeton d'entrée IOHERE lui indiquant qu'il doit rediriger son entrée vers le descripteur de fichier que vous spécifiez jusqu'à ce qu'il rencontre l'autre extrémité de votre jeton limiteur. J'ai regardé autour de moi mais je n'ai pas vu beaucoup d'exemples d'utilisation du numéro fd de redirection, comme indiqué ci-dessus en combinaison avec l'opérateur heredoc, bien que son utilisation soit clairement spécifiée dans les instructions de base relatives aux commandes de shell POSIX. La plupart des gens se contentent de pointer stdin et de filmer, mais je trouve que les scriptlets de sourcing de cette façon peuvent empêcher stdin et les applications qui le constituent de se plaindre des chemins d'E / S bloqués.

Le contenu de heredoc est transmis au descripteur de fichier que vous spécifiez, qui est à son tour interprété comme un code shell et exécuté par le. intégré, mais pas sans spécifier un chemin spécifique pour. . Si le chemin / proc / self vous pose problème, essayez / dev / fd / n ou / proc / $$. Cette même méthode fonctionne sur les tuyaux, au fait:

cat ./*.sh | . /dev/stdin

Est probablement au moins aussi imprudent qu'il n'y paraît. Bien sûr, vous pouvez faire la même chose avec sh, mais. Le but de. avec un tuyau anonyme standard.

Quoi qu'il en soit, comme vous l'avez probablement remarqué, je n'ai toujours pas répondu à votre question. Mais si vous y réfléchissez, de la même manière que l'hérédoc transfère l'intégralité de votre code dans.

. 5<<EOIN /dev/fd/5 |\ 
    tee -a ./log.file | cat -u >$(tty)
script
 
more script
EOIN

Ainsi, toute la stdout terminale de tout code exécuté dans votre heredoc est renvoyée. bien sûr et peut facilement être raccordé à un seul tuyau. J'ai inclus l'appel de chat sans tampon parce que je ne connais pas bien la direction actuelle de la sortie standard, mais il est probablement redondant (il est presque certainement tel qu'il est écrit de toute façon) et le pipeline peut probablement se terminer immédiatement.

Vous pouvez également remettre en question la citation manquante de barre oblique inverse dans le deuxième exemple. Cette partie est importante à comprendre avant de vous lancer et peut vous donner quelques idées sur la façon dont elle peut être utilisée. Un limiteur heredoc cité (jusqu'à présent, nous avons utilisé IOHERE et EOIN, et le premier que j'ai cité avec une barre oblique inversée, bien que des guillemets simples ou doubles serviraient le même but) empêchera le shell d'effectuer une expansion des paramètres sur le contenu, mais un limiteur non cité laissera son contenu ouvert à l’extension. Les conséquences de cela quand votre heredoc est. les sources sont dramatiques:

. 3<<HD ${fdpath}/3
: \${vars=$(printf '${var%s=$((%s*2))},' `seq 1 100`)} 
HD
echo $vars
> 4,8,12 
echo $var1 $var51
> 4 104

Comme je n'ai pas cité le limiteur heredoc, le shell a élargi le contenu au fur et à mesure de sa lecture et avant de servir le descripteur de fichier résultant. éxécuter. Cela a essentiellement pour résultat que les commandes ont été analysées deux fois - les commandes extensibles de toute façon. Étant donné que je citais le paramètre $ vars comme extension de barre oblique inversée, le shell a ignoré sa déclaration lors de la première passe et n'a supprimé que la barre oblique inverse afin que l'ensemble du contenu développé de printf puisse être évalué par null when. source le script sur la deuxième passe.

Cette fonctionnalité est fondamentalement exactement ce que le shell eval intégré peut fournir, même si la citation est beaucoup plus facile à manipuler dans un heredoc que dans eval, et peut être tout aussi dangereuse. À moins que vous ne planifiiez cela soigneusement, il est probablement préférable de citer le limiteur "EOF" par habitude. Je dis juste.

EDIT: Eh, je regarde cela en arrière et je pense que c'est un peu trop exagéré. Si vous devez TOUT concaténer plusieurs sorties dans un même canal, la méthode la plus simple consiste simplement à utiliser un:

{ or ( command ) list ; } | tee -a ea.sy >>pea.sy

Les curlies tenteront d’exécuter le contenu dans le shell actuel, tandis que les parens seront automatiquement sous-sortis. Néanmoins, tout le monde peut vous dire cela et, du moins à mon avis, le. La solution heredoc constitue une information beaucoup plus précieuse, en particulier si vous souhaitez comprendre le fonctionnement réel du shell.

S'amuser!

Mikeserv
la source
3

J'ai trouvé cette réponse en cherchant à modifier un script d'installation pour écrire un journal d'installation.

Mon script est déjà rempli d'instructions d'écho telles que:

echo "Found OLD run script $oldrunscriptname"
echo "Please run OLD tmunsetup script first!"

Et je ne voulais pas d'une instruction tee pour l'exécuter (ni d'un autre script pour appeler l'existant avec un tee), alors j'ai écrit ceci:

#!/bin/bash
# A Shell subroutine to echo to screen and a log file

LOGFILE="junklog"

echolog()
(
echo $1
echo $1 >> $LOGFILE
)


echo "Going"
echolog "tojunk"

# eof

Alors maintenant, dans mon script original, je peux simplement changer "echo" en "echolog" où je veux la sortie dans un fichier journal.

AndruWitta
la source