So geben Sie Shell-Befehle wieder, wenn sie ausgeführt werden

911

Wie kann ich in einem Shell-Skript alle aufgerufenen Shell-Befehle wiedergeben und Variablennamen erweitern?

Zum Beispiel in der folgenden Zeile:

ls $DIRNAME

Ich möchte, dass das Skript den Befehl ausführt und Folgendes anzeigt

ls /full/path/to/some/dir

Der Zweck besteht darin, ein Protokoll aller aufgerufenen Shell-Befehle und ihrer Argumente zu speichern. Gibt es vielleicht eine bessere Möglichkeit, ein solches Protokoll zu erstellen?

Jack Nock
quelle

Antworten:

1042

set -xoder set -o xtraceerweitert Variablen und druckt ein kleines + Zeichen vor der Zeile.

set -voder set -o verboseerweitert die Variablen vor dem Drucken nicht.

Verwenden Sie set +xund set +v, um die obigen Einstellungen zu deaktivieren.

In der ersten Zeile des Skripts kann man #!/bin/sh -x(oder -v) setzen, um den gleichen Effekt wie set -x(oder -v) später im Skript zu erzielen .

Das obige funktioniert auch mit /bin/sh.

Informationen zu setAttributen und zum Debuggen finden Sie im Wiki der Bash-Hacker .

$ cat shl
#!/bin/bash                                                                     

DIR=/tmp/so
ls $DIR

$ bash -x shl 
+ DIR=/tmp/so
+ ls /tmp/so
$
Tom
quelle
7
Wenn Sie auch sehen möchten, welche nummerierte Zeile ausgeführt wird, sehen Sie stackoverflow.com/a/17805088/1729501
user13107
3
Was ist, wenn ich den Befehl beim Echo färben möchte, um den Befehl und seine Ergebnisausgabe zu unterscheiden?
Lewis Chan
10
Was ist, wenn ich möchte, dass jeder Befehl beim Echo durch einen vokaloidartigen Sprachsynthesizer gesungen wird, damit ich aus der Ferne hören kann, was er tut, und auch die Musik genießen kann? Vielleicht mit einer anderen Stimme für Ein- und Ausgänge.
ADJenks
(Das ABS reagiert seit langem nicht mehr auf Anfragen, Korrekturbeispiele zu korrigieren, bis hin zu langjährigen Community-Mitgliedern, um konkurrierende Ressourcen zu schaffen. Es wurden Links zum Wiki der Bash-Hacker verschoben - dem Wooledge-Wiki oder dem offizielles Bash-Handbuch wäre auch bessere Ressourcen).
Charles Duffy
317

set -x wird dir geben, was du willst.

Hier ist ein Beispiel für ein Shell-Skript, das Folgendes demonstriert:

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

ls $PWD

Dies erweitert alle Variablen und druckt die vollständigen Befehle vor der Ausgabe des Befehls.

Ausgabe:

+ ls /home/user/
file1.txt file2.txt
Radman
quelle
21
Wenn Sie das Wort "ausführlich" auf diese Weise verwenden, wird nichts erreicht. Sie können set -o verboseoder set -v(nur "ausführlich") oder set -o xtraceoder set -x(nur "xtrace") oder set -xv(beide) oder set -o xtrace -o verbose(beide) tun .
Bis auf weiteres angehalten.
Dies funktioniert gut, aber beachten Sie, dass die "ausführliche" $ 1 überschreibt
JasonS
90

Ich benutze eine Funktion, um den Befehl zu wiederholen und auszuführen:

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

exe echo hello world

Welche Ausgänge

$ echo hello world
hello world

Für kompliziertere Befehlspipes usw. können Sie eval verwenden:

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

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

Welche Ausgänge

$  echo 'Hello, World!' | cut -d ' ' -f1
Hello
Soth
quelle
Nicht viele Stimmen für diese Antwort. Gibt es einen Grund, warum es eine schlechte Idee ist? Hat für mich gearbeitet und scheint genau das zu sein, wonach ich suche ...
fru1tbat
1
Dies ist die beste Antwort, wenn nicht jeder Befehl gedruckt werden soll. Es vermeidet die ++ set +xAusgabe im ausgeschalteten Zustand und sieht sauberer aus. Für nur ein oder zwei Aussagen ist die Antwort von bhassel mit einer Unterschale am bequemsten.
Brent Faust
Das suche ich! Nicht set +x, es betrifft alle Befehle, was zu viel ist!
Hlung
11
Ein wesentlicher Nachteil dabei ist, dass die Ausgabe die Angebotsinformationen verliert. Sie können zum Beispiel nicht zwischen cp "foo bar" bazund cp foo "bar baz"unterscheiden. Es ist also gut, um einem Benutzer Fortschrittsinformationen anzuzeigen. weniger für das Debuggen der Ausgabe oder das Aufzeichnen reproduzierbarer Befehle. Verschiedene Anwendungsfälle. In zshkönnen Sie das Zitieren mit dem :qModifikator beibehalten :exe() { echo '$' "${@:q}" ; "$@" ; }
Andrew Janke
2
Diese Antwort gefällt mir nicht. Es gibt viele Randfälle, in denen das, was Sie sehen, nicht das ist, was Sie erhalten (insbesondere bei Leerzeichen, Anführungszeichen, maskierten Zeichen, Variablen- / Ausdrucksersetzungen usw.). Fügen Sie den Befehl echoed also nicht blind in ein Terminal ein und gehen Sie davon aus, dass er ausgeführt wird in der gleichen Weise. Außerdem ist die zweite Technik nur ein Hack und entfernt andere Instanzen des Wortes evalaus Ihrem Befehl. Erwarten Sie also nicht, dass es richtig funktioniert exe eval "echo 'eval world'"!
mwfearnley
88

Sie können dies auch für ausgewählte Zeilen in Ihrem Skript umschalten, indem Sie sie einschließen set -xund set +xbeispielsweise

#!/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
quelle
54

Die Antwort von shuckc für das Echo ausgewählter Zeilen hat einige Nachteile: Am Ende wird auch der folgende set +xBefehl wiederholt, und Sie verlieren die Möglichkeit, den Exit-Code zu testen, $?da er von der überschrieben wird set +x.

Eine andere Möglichkeit besteht darin, den Befehl in einer Subshell auszuführen:

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

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

Das gibt Ihnen Ausgabe wie:

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

Dies verursacht jedoch den Aufwand für das Erstellen einer neuen Subshell für den Befehl.

bhassel
quelle
7
Gute Möglichkeit, die ++ set +xAusgabe zu vermeiden .
Brent Faust
3
Noch besser: ersetzen if [ $? -eq 0 ]durch if (set -x; COMMAND).
Amedee Van Gasse
35

Eine andere Möglichkeit besteht darin, "-x" am Anfang Ihres Skripts anstatt in der Befehlszeile einzufügen:

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

$ ./server
+ ssh user@server
user@server's password: ^C
$
nooj
quelle
Beachten Sie, dass dies zwischen ./myScriptund nicht genau gleich zu funktionieren scheint bash myScript. Trotzdem eine gute Sache, danke.
Altendky
27

Nach TLDP ‚s Bash Leitfaden für Anfänger: Kapitel 2. Schreiben und Debuggen von Skripten :

2.3.1. Debuggen des gesamten Skripts

$ bash -x script1.sh

...

Es gibt jetzt einen vollwertigen Debugger für Bash, der bei SourceForge erhältlich ist . Diese Debugging-Funktionen sind in den meisten modernen Versionen von Bash ab 3.x verfügbar.

2.3.2. Debuggen von Teilen des Skripts

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

...

Tabelle 2-1. Übersicht über die festgelegten Debugging-Optionen

    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.

...

Alternativ können diese Modi im Skript selbst angegeben werden, indem die gewünschten Optionen zur Shell-Deklaration in der ersten Zeile hinzugefügt werden. Optionen können kombiniert werden, wie dies normalerweise bei UNIX-Befehlen der Fall ist:

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

Geben Sie "bash -x" in die Befehlszeile vor dem Namen des Bash-Skripts ein. Geben Sie zum Ausführen von foo.sh beispielsweise Folgendes ein:

bash -x foo.sh
Alan
quelle
11

Mit der Option können Sie ein Bash-Skript im Debug-Modus ausführen .-x

Dadurch werden alle Befehle wiedergegeben.

bash -x example_script.sh

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

Sie können die Option -x auch im Skript speichern . -xGeben Sie einfach die Option im Shebang an.

######## 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
quelle
2
Auch bash -vxwird das gleiche tun, aber ohne variable Interpolation
loa_in_
4

Für zsh echo

 setopt VERBOSE

Und zum Debuggen,

 setopt XTRACE
zzapper
quelle
2

Für cshund tcshkönnen Sie set verboseoder set echo(oder Sie können sogar beide festlegen, dies kann jedoch die meiste Zeit zu Duplikaten führen).

Die verboseOption gibt so ziemlich den genauen Shell-Ausdruck aus, den Sie eingeben.

Die echoOption zeigt eher an, was durch das Laichen ausgeführt wird.


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
quelle
1
$ cat exampleScript.sh
#!/bin/bash
name="karthik";
echo $name;

bash -x exampleScript.sh

Die Ausgabe ist wie folgt:

Geben Sie hier die Bildbeschreibung ein

Karthik Arun
quelle
Was sind die Farbdefinitionen für Ihr Terminal (RGB, numerisch)?
Peter Mortensen
1

Damit zusammengesetzte Befehle wiedergegeben werden können, verwende ich evaldie exeFunktion von plus Soth, um den Befehl wiederzugeben und auszuführen. Dies ist nützlich für Piped-Befehle, bei denen sonst nur keiner oder nur der erste Teil des Piped-Befehls angezeigt wird.

Ohne Bewertung:

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

Ausgänge:

$
file.txt

Mit eval:

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

Welche Ausgänge

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