2008-12-12 6 views
57

J'ai un script qui sera exécuté interactivement par des utilisateurs non-techniques. Le script écrit les mises à jour d'état sur STDOUT afin que l'utilisateur puisse être sûr que le script s'exécute correctement.Comment puis-je obtenir à la fois STDOUT et STDERR pour accéder au terminal et à un fichier journal?

Je souhaite que STDOUT et STDERR soient redirigés vers le terminal (pour que l'utilisateur puisse voir que le script fonctionne et voir s'il y a eu un problème). Je veux également que les deux flux soient redirigés vers un fichier journal.

J'ai vu un tas de solutions sur le net. Certains ne fonctionnent pas et d'autres sont horriblement compliqués. J'ai développé une solution réalisable (que je vais entrer comme réponse), mais c'est kludgy.

La solution idéale serait une ligne de code unique qui pourrait être incorporée au début de tout script qui envoie les deux flux au terminal et à un fichier journal.

EDIT: Rediriger STDERR à STDOUT et la tuyauterie du résultat tee travaux, mais cela dépend des utilisateurs de rediriger et se souvenir redirigez la sortie. Je veux que la journalisation soit infaillible et automatique (c'est pourquoi je voudrais pouvoir incorporer la solution dans le script lui-même.)

+0

Pour les autres lecteurs: question similaire: http://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe – pevik

Répondre

92

Utilisez "tee" pour rediriger vers un fichier et l'écran. En fonction du shell que vous utilisez, vous devez d'abord rediriger stderr vers stdout en utilisant

./a.out 2>&1 | tee output 

ou

./a.out |& tee output 

En csh, il y a une commande intégrée appelée « script » qui va capturer tout ce qui va à l'écran dans un fichier. Vous commencez en tapant "script", puis en faisant ce que vous voulez capturer, puis appuyez sur control-D pour fermer le fichier de script. Je ne connais pas d'équivalent pour sh/bash/ksh.

De plus, puisque vous avez maintenant indiqué que ce sont vos propres scripts sh que vous pouvez modifier, vous pouvez faire la redirection interne en entourant le script entier avec des accolades ou des crochets, comme

#!/bin/sh 
    { 
    ... whatever you had in your script before 
    } 2>&1 | tee output.file 
+3

Je ne savais pas que vous pouviez placer des commandes entre les scripts shell. Intéressant. – Jamie

+1

J'apprécie aussi le raccourci de parenthèse! Pour une raison quelconque, '2> & 1 | tee -a nom de fichier ne sauvegardait pas stderr dans le fichier de mon script, mais ça marchait bien quand j'ai copié la commande et que je l'ai collée dans le terminal! Le tour de support fonctionne bien, cependant. –

+3

Notez que la distinction entre stdout et stderr sera perdue, car tee imprime tout sur stdout. – Flimm

1

Utilisez le tee programme et dup stderr à stdout.

program 2>&1 | tee > logfile 
2

J'ai créé un script appelé "RunScript.sh". Le contenu de ce script est:

${APP_HOME}/${1}.sh ${2} ${3} ${4} ${5} ${6} 2>&1 | tee -a ${APP_HOME}/${1}.log 

Je l'appelle comme ceci:

./RunScript.sh ScriptToRun Param1 Param2 Param3 ... 

Cela fonctionne, mais il faut à exécuter les scripts de l'application via un script externe. C'est un peu kludgy.

+6

Vous perdrez le groupement d'arguments contenant des espaces avec ** $ 1 $ 2 $ 3 ... **, vous devriez utiliser (w/quotes): ** "$ @" ** – NVRAM

3

le à rediriger stderr vers stdout append ceci à votre commande: 2>&1 Pour la sortie à la borne et la connexion dans le fichier, vous devez utiliser tee

Les deux ensemble ressemblerait à ceci:

mycommand 2>&1 | tee mylogfile.log 

EDIT: Pour l'intégration dans votre script, vous feriez la même chose.Donc, votre script

#!/bin/sh 
whatever1 
whatever2 
... 
whatever3 

finirait comme

#!/bin/sh 
(whatever1 
whatever2 
... 
whatever3) 2>&1 | tee mylogfile.log 
+0

Notez que la distinction entre stdout et stderr sera perdu, comme tee écrit tout à stdout. – Flimm

0

Utilisez la commande script dans votre script (man 1 script)

Créer une shellscript wrapper (2 lignes) qui met en place le script () puis appelle exit.

Partie 1: wrap.sh

#!/bin/sh 
script -c './realscript.sh' 
exit 

Partie 2: realscript.sh

#!/bin/sh 
echo 'Output' 

Résultat:

~: sh wrap.sh 
Script started, file is typescript 
Output 
Script done, file is typescript 
~: cat typescript 
Script started on fr. 12. des. 2008 kl. 18.07 +0100 
Output 

Script done on fr. 12. des. 2008 kl. 18.07 +0100 
~: 
1

Un an plus tard, voici un vieux script bash pour l'enregistrement n'importe quoi. Par exemple,
teelog make ... journaux à un nom de journal généré (et voir l'astuce pour l'enregistrement imbriqué make s aussi.)

#!/bin/bash 
me=teelog 
Version="2008-10-9 oct denis-bz" 

Help() { 
cat <<! 

    $me anycommand args ... 

logs the output of "anycommand ..." as well as displaying it on the screen, 
by running 
    anycommand args ... 2>&1 | tee `day`-command-args.log 

That is, stdout and stderr go to both the screen, and to a log file. 
(The Unix "tee" command is named after "T" pipe fittings, 1 in -> 2 out; 
see http://en.wikipedia.org/wiki/Tee_(command)). 

The default log file name is made up from "command" and all the "args": 
    $me cmd -opt dir/file logs to `day`-cmd--opt-file.log . 
To log to xx.log instead, either export log=xx.log or 
    $me log=xx.log cmd ... 
If "logdir" is set, logs are put in that directory, which must exist. 
An old xx.log is moved to /tmp/\$USER-xx.log . 

The log file has a header like 
    # from: command args ... 
    # run: date pwd etc. 
to show what was run; see "From" in this file. 

Called as "Log" (ln -s $me Log), Log anycommand ... logs to a file: 
    command args ... > `day`-command-args.log 
and tees stderr to both the log file and the terminal -- bash only. 

Some commands that prompt for input from the console, such as a password, 
don't prompt if they "| tee"; you can only type ahead, carefully. 

To log all "make" s, including nested ones like 
    cd dir1; \$(MAKE) 
    cd dir2; \$(MAKE) 
    ... 
export MAKE="$me make" 

! 
    # See also: output logging in screen(1). 
    exit 1 
} 


#------------------------------------------------------------------------------- 
# bzutil.sh denisbz may2008 -- 

day() { # 30mar, 3mar 
    /bin/date +%e%h | tr '[A-Z]' '[a-z]' | tr -d ' ' 
} 

edate() { # 19 May 2008 15:56 
    echo `/bin/date "+%e %h %Y %H:%M"` 
} 

From() { # header # from: $* # run: date pwd ... 
    case `uname` in Darwin) 
     mac=" mac `sw_vers -productVersion`" 
    esac 
    cut -c -200 <<! 
${comment-#} from: [email protected] 
${comment-#} run: `edate` in $PWD `uname -n` $mac `arch` 

! 
    # mac $PWD is pwd -L not -P real 
} 

    # log name: day-args*.log, change this if you like -- 
logfilename() { 
    log=`day` 
    [[ $1 == "sudo" ]] && shift 
    for arg 
    do 
     log="$log-${arg##*/}" # basename 
     ((${#log} >= 100)) && break # max len 100 
    done 
      # no blanks etc in logfilename please, tr them to "-" 
    echo $logdir/` echo "$log".log | tr -C '.:+=[:alnum:]_\n' - ` 
} 

#------------------------------------------------------------------------------- 
case "$1" in 
-v* | --v*) 
    echo "$0 version: $Version" 
    exit 1 ;; 
"" | -*) 
    Help 
esac 

    # scan log= etc -- 
while [[ $1 == [a-zA-Z_]*=* ]]; do 
    export "$1" 
    shift 
done 

: ${logdir=.} 
[[ -w $logdir ]] || { 
    echo >&2 "error: $me: can't write in logdir $logdir" 
    exit 1 
    } 
: ${log=` logfilename "[email protected]" `} 
[[ -f $log ]] && 
    /bin/mv "$log" "/tmp/$USER-${log##*/}" 


case ${0##*/} in # basename 
log | Log) # both to log, stderr to caller's stderr too -- 
{ 
    From "[email protected]" 
    "[email protected]" 
} > $log 2> >(tee /dev/stderr) # bash only 
    # see http://wooledge.org:8000/BashFAQ 47, stderr to a pipe 
;; 

*) 
#------------------------------------------------------------------------------- 
{ 
    From "[email protected]" # header: from ... date pwd etc. 

    "[email protected]" 2>&1 # run the cmd with stderr and stdout both to the log 

} | tee $log 
    # mac tee buffers stdout ? 

esac 
+0

Je sais que c'est très tard pour ajouter un commentaire mais je devais juste dire merci pour ce script. Très utile et bien documenté! – stephenmm

+0

Merci @stephenmm; c'est * jamais * trop tard pour dire "utile" ou "pourrait être amélioré". – denis

10

l'approche d'une demi-décennie plus tard ...

je crois que c'est le " solution parfaite "recherchée par le PO.

est ici une seule ligne, vous pouvez ajouter en haut de votre script Bash:

exec > >(tee -a $HOME/logfile) 2>&1 

Voici un petit script qui démontre son utilisation:

#!/usr/bin/env bash 

exec > >(tee -a $HOME/logfile) 2>&1 

# Test redirection of STDOUT 
echo test_stdout 

# Test redirection of STDERR 
ls test_stderr___this_file_does_not_exist 

(Note: Cela fonctionne uniquement avec Bash. il sera pas travail avec/bin/sh)

Adapté de here. l'original n'a pas, d'après ce que je peux dire, attraper STDERR dans le fichier journal. Fixé avec une note de here.

+0

Notez que la distinction entre stdout et stderr sera perdue, car tee imprime tout sur stdout. – Flimm

+0

@Flimm stderr pourrait être redirigé vers un autre processus tee qui pourrait à nouveau être redirigé vers stderr. – jarno

Questions connexes