Comment faire écho aux commandes du shell lors de leur exécution

913

Dans un script shell, comment puis-je faire écho à toutes les commandes shell appelées et développer les noms de variables?

Par exemple, étant donné la ligne suivante:

ls $DIRNAME

J'aimerais que le script exécute la commande et affiche ce qui suit

ls /full/path/to/some/dir

Le but est de sauvegarder un journal de toutes les commandes shell appelées et de leurs arguments. Existe-t-il peut-être un meilleur moyen de générer un tel journal?

Jack Nock
la source

Réponses:

1044

set -xou set -o xtracedéveloppe les variables et imprime un petit signe + avant la ligne.

set -vou set -o verbosene développe pas les variables avant l'impression.

Utilisez set +xet set +vpour désactiver les paramètres ci-dessus.

Sur la première ligne du script, on peut mettre #!/bin/sh -x(ou -v) pour avoir le même effet que set -x(ou -v) plus tard dans le script.

Ce qui précède fonctionne également avec /bin/sh.

Voir le wiki des bash-hackers sur les setattributs et le débogage .

$ cat shl
#!/bin/bash                                                                     

DIR=/tmp/so
ls $DIR

$ bash -x shl 
+ DIR=/tmp/so
+ ls /tmp/so
$
À M
la source
7
Si vous souhaitez également voir quelle ligne numérotée est exécutée, voir stackoverflow.com/a/17805088/1729501
user13107
3
que faire si je veux colorer la commande lors de l'écho pour différencier la commande et sa sortie de résultats?
Lewis Chan
11
Que se passe-t-il si je veux qu'il chante chaque commande via un synthétiseur vocaloïde lors de l'écho, afin que je puisse entendre ce qu'il fait à distance et profiter de la musique? Peut-être d'une voix différente pour les entrées et les sorties.
ADJenks
(L'ABS a longtemps été insensible aux demandes de correction d'exemples de mauvaises pratiques, au point d'inciter les membres de longue date de la communauté à créer des ressources concurrentes; déplacé des liens vers le wiki des bash-hackers - le wiki Wooledge ou le manuel bash officiel serait également de meilleures ressources).
Charles Duffy
317

set -x vous donnera ce que vous voulez.

Voici un exemple de script shell pour démontrer:

#!/bin/bash
set -x #echo on

ls $PWD

Cela étend toutes les variables et imprime les commandes complètes avant la sortie de la commande.

Production:

+ ls /home/user/
file1.txt file2.txt
radman
la source
21
Utiliser le mot "verbeux" de cette façon ne fait rien. Vous pouvez faire set -o verboseou set -v(uniquement "verbeux") ou set -o xtraceou set -x(uniquement "xtrace") ou set -xv(les deux) ou set -o xtrace -o verbose(les deux).
pause jusqu'à nouvel ordre.
cela fonctionne bien, mais sachez que le "verbeux" écrase 1 $
JasonS
90

J'utilise une fonction pour faire écho et exécuter la commande:

#!/bin/bash
# Function to display commands
exe() { echo "\$ $@" ; "$@" ; }

exe echo hello world

Quelles sorties

$ echo hello world
hello world

Pour des canaux de commandes plus compliqués, etc., vous pouvez utiliser eval:

#!/bin/bash
# Function to display commands
exe() { echo "\$ ${@/eval/}" ; "$@" ; }

exe eval "echo 'Hello, World!' | cut -d ' ' -f1"

Quelles sorties

$  echo 'Hello, World!' | cut -d ' ' -f1
Hello
Soth
la source
Pas beaucoup de votes pour cette réponse. Y a-t-il une raison pour laquelle c'est une mauvaise idée? A fonctionné pour moi, et semble être exactement ce que je recherche ...
fru1tbat
1
C'est la meilleure réponse si vous ne voulez pas que chaque commande soit imprimée. Il évite la ++ set +xsortie lorsqu'il est éteint, ainsi que l'air plus propre. Cependant, pour une seule ou deux déclarations, la réponse de Bhassel à l'aide d'un sous-shell est la plus pratique.
Brent Faust
Voilà ce que je recherche! Non set +x, cela affecte toutes les commandes, ce qui est trop!
Hlung
11
Un inconvénient majeur à cela est que la sortie perd les informations de cotation. Vous ne pouvez pas différencier entre cp "foo bar" bazet cp foo "bar baz", par exemple. C'est donc bon pour afficher les informations de progression à un utilisateur; moins pour le débogage de sortie ou l'enregistrement de commandes reproductibles. Différents cas d'utilisation. Dans zsh, vous pouvez conserver les citations avec le :qmodificateur:exe() { echo '$' "${@:q}" ; "$@" ; }
Andrew Janke
2
Je n'aime pas cette réponse. Il y a beaucoup de cas extrêmes où ce que vous voyez n'est pas ce que vous obtenez (en particulier avec les espaces, les guillemets, les caractères d'échappement, les substitutions de variables / expressions, etc.), alors ne collez pas aveuglément la commande en écho dans un terminal et supposez qu'elle s'exécutera de la même façon. En outre, la deuxième technique n'est qu'un hack et supprimera les autres instances du mot evalde votre commande. Alors ne vous attendez pas à ce qu'il fonctionne correctement exe eval "echo 'eval world'"!
mwfearnley
88

Vous pouvez également basculer cette option pour certaines lignes de votre script en les encapsulant set -xet set +x, par exemple,

#!/bin/bash
...
if [[ ! -e $OUT_FILE ]];
then
   echo "grabbing $URL"
   set -x
   curl --fail --noproxy $SERV -s -S $URL -o $OUT_FILE
   set +x
fi
shuckc
la source
54

La réponse de shuckc pour l'écho des lignes sélectionnées a quelques inconvénients: vous vous retrouvez également avec la set +xcommande suivante en écho et vous perdez la possibilité de tester le code de sortie avec $?car il est écrasé par le set +x.

Une autre option consiste à exécuter la commande dans un sous-shell:

echo "getting URL..."
( set -x ; curl -s --fail $URL -o $OUTFILE )

if [ $? -eq 0 ] ; then
    echo "curl failed"
    exit 1
fi

ce qui vous donnera une sortie comme:

getting URL...
+ curl -s --fail http://example.com/missing -o /tmp/example
curl failed

Cela entraîne cependant la surcharge de la création d'un nouveau sous-shell pour la commande.

bhassel
la source
7
Belle façon d'éviter la ++ set +xsortie.
Brent Faust
3
Encore mieux: remplacez if [ $? -eq 0 ]par if (set -x; COMMAND).
Amedee Van Gasse
35

Une autre option consiste à mettre "-x" en haut de votre script au lieu de sur la ligne de commande:

$ cat ./server
#!/bin/bash -x
ssh user@server

$ ./server
+ ssh user@server
user@server's password: ^C
$
nooj
la source
Notez que cela ne semble pas fonctionner exactement de la même manière entre ./myScriptet bash myScript. Encore une bonne chose à souligner, merci.
altendky
27

Selon TLDP du Guide Bash pour les débutants: Chapitre 2. Rédaction et scripts de débogage :

2.3.1. Débogage sur l'ensemble du script

$ bash -x script1.sh

...

Il existe maintenant un débogueur à part entière pour Bash, disponible sur SourceForge . Ces fonctionnalités de débogage sont disponibles dans la plupart des versions modernes de Bash, à partir de 3.x.

2.3.2. Débogage sur une ou plusieurs parties du script

set -x            # Activate debugging from here
w
set +x            # Stop debugging from here

...

Tableau 2-1. Présentation des options de débogage définies

    Short  | Long notation | Result
    -------+---------------+--------------------------------------------------------------
    set -f | set -o noglob | Disable file name generation using metacharacters (globbing).
    set -v | set -o verbose| Prints shell input lines as they are read.
    set -x | set -o xtrace | Print command traces before executing command.

...

Alternativement, ces modes peuvent être spécifiés dans le script lui-même, en ajoutant les options souhaitées à la première déclaration de shell de ligne. Les options peuvent être combinées, comme c'est généralement le cas avec les commandes UNIX:

#!/bin/bash -xv
Afriza N. Arief
la source
18

Tapez "bash -x" sur la ligne de commande avant le nom du script Bash. Par exemple, pour exécuter foo.sh, tapez:

bash -x foo.sh
Alan
la source
11

Vous pouvez exécuter un script Bash en mode débogage avec l' -xoption .

Cela fera écho à toutes les commandes.

bash -x example_script.sh

# Console output
+ cd /home/user
+ mv text.txt mytext.txt

Vous pouvez également enregistrer l'option -x dans le script . Spécifiez simplement l' -xoption dans le shebang.

######## example_script.sh ###################
#!/bin/bash -x

cd /home/user
mv text.txt mytext.txt

##############################################

./example_script.sh

# Console output
+ cd /home/user
+ mv text.txt mytext.txt
RenRen
la source
2
bash -vx
Fera
4

Pour zsh, echo

 setopt VERBOSE

Et pour le débogage,

 setopt XTRACE
zzapper
la source
2

Pour cshet tcsh, vous pouvez set verboseou set echo(ou vous pouvez même définir les deux, mais cela peut entraîner la duplication la plupart du temps).

L' verboseoption imprime à peu près l'expression shell exacte que vous tapez.

L' echooption indique davantage ce qui sera exécuté par le frai.


http://www.tcsh.org/tcsh.html/Special_shell_variables.html#verbose

http://www.tcsh.org/tcsh.html/Special_shell_variables.html#echo


Special shell variables

verbose If set, causes the words of each command to be printed, after history substitution (if any). Set by the -v command line option.

echo If set, each command with its arguments is echoed just before it is executed. For non-builtin commands all expansions occur before echoing. Builtin commands are echoed before command and filename substitution, because these substitutions are then done selectively. Set by the -x command line option.

cnst
la source
1
$ cat exampleScript.sh
#!/bin/bash
name="karthik";
echo $name;

bash -x exampleScript.sh

La sortie est la suivante:

entrez la description de l'image ici

Karthik Arun
la source
Quelles sont les définitions de couleurs pour votre terminal (RVB, numérique)?
Peter Mortensen
1

Pour permettre l'écho des commandes composées, j'utilise la fonction evalplus Soth pour exefaire écho et exécuter la commande. Ceci est utile pour les commandes redirigées qui, autrement, ne montreraient rien ou seulement la partie initiale de la commande redirigée.

Sans eval:

exe() { echo "\$ $@" ; "$@" ; }
exe ls -F | grep *.txt

Les sorties:

$
file.txt

Avec eval:

exe() { echo "\$ $@" ; "$@" ; }
exe eval 'ls -F | grep *.txt'

Quelles sorties

$ exe eval 'ls -F | grep *.txt'
file.txt
Paul Havens
la source