Debuggen eines Bash-Skripts

Übersicht

In diesem Tutorial werden die verschiedenen Techniken zum Debuggen von Bash-Shell-Skripten erläutert. Die Bash-Shell bietet keinen integrierten Debugger. Es gibt jedoch bestimmte Befehle und Konstrukte, die für diesen Zweck verwendet werden können.

Zunächst werden wir die Verwendung des Befehls set zum Debuggen von Skripten besprechen. Danach überprüfen wir einige Debugging-spezifische Anwendungsfälle mit den Befehlen set und trap. Abschließend stellen wir einige Methoden zum Debuggen bereits ausgeführter Skripte vor.

Bash-Debugging-Optionen

Die in der Bash-Shell verfügbaren Debugging-Optionen können auf verschiedene Arten ein- und ausgeschaltet werden. Innerhalb von Skripten können wir entweder den Befehl set verwenden oder der Shebang-Zeile eine Option hinzufügen. Ein anderer Ansatz besteht jedoch darin, die Debugging-Optionen während der Ausführung des Skripts explizit in der Befehlszeile anzugeben. Lassen Sie uns in die Diskussion eintauchen.

2.1. Aktivieren des ausführlichen Modus

Wir können den ausführlichen Modus mit dem Schalter -v aktivieren, mit dem wir jeden Befehl anzeigen können, bevor er ausgeführt wird.

Um dies zu demonstrieren, erstellen wir ein Beispielskript:

#! /bin/bashread -p "Enter the input: " valzero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fi

Dieses Skript prüft, ob die als Eingabe eingegebene Zahl positiv ist.

Als nächstes führen wir unser Skript aus:

$ bash -v ./positive_check.sh#! /bin/bashread -p "Enter the input: " valEnter the input: -10zero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fiThe input value is not positive.

Wie wir feststellen können, druckt es jede Zeile des Skripts auf dem Terminal, bevor es verarbeitet wird.

Wir können auch die Option -v in der Shebang-Zeile hinzufügen:

#! /bin/bash -v

Dies hat den gleichen Effekt wie das explizite Aufrufen eines Skripts mit bash -v. Ein anderes Äquivalent besteht darin, den Modus innerhalb eines Skripts mit dem Befehl set zu aktivieren:

#! /bin/bashset -v

Tatsächlich können wir eine der oben beschriebenen Möglichkeiten verwenden, um die verschiedenen Schalter zu aktivieren, die wir im Folgenden besprechen werden.

2.2. Syntaxprüfung im noexec-Modus

Es kann Situationen geben, in denen wir das Skript vor seiner Ausführung syntaktisch validieren möchten. Wenn ja, können wir den noexec-Modus mit der Option -n verwenden. Infolgedessen liest Bash die Befehle, führt sie jedoch nicht aus.

Führen wir unsere positive_check.sh skript im Noexec-Modus:

$ bash -n ./positive_check.sh

Dies erzeugt eine leere Ausgabe, da keine Syntaxfehler vorliegen. Jetzt ändern wir unser Skript ein wenig und entfernen die then-Anweisung:

#! /bin/bashread -p "Enter the input: " valzero_val=0if echo "Positive number entered."else echo "The input value is not positive."fi

Als nächstes validieren wir es syntaktisch mit der Option -n:

$ bash -n ./positive_check_noexec.sh./positive_check_noexec.sh: line 6: syntax error near unexpected token `else'./positive_check_noexec.sh: line 6: ` else'

Wie erwartet wurde ein Fehler ausgegeben, da wir die then-Anweisung in der if-Bedingung verpasst haben.

2.3. Debuggen im xtrace-Modus

Im vorherigen Abschnitt haben wir das Skript auf Syntaxfehler getestet. Um jedoch logische Fehler zu identifizieren, möchten wir möglicherweise den Status von Variablen und Befehlen während des Ausführungsprozesses verfolgen. In solchen Fällen können wir das Skript im xtrace-Modus (Execution Trace) mit der Option -x ausführen.

Dieser Modus gibt die Ablaufverfolgung von Befehlen für jede Zeile aus, nachdem sie erweitert wurden, aber bevor sie ausgeführt werden.

Führen wir unsere positive_check.sh skript im Ausführungs-Trace-Modus:

$ bash -x ./positive_check.sh+ read -p 'Enter the input: ' valEnter the input: 17+ zero_val=0+ ''+ echo 'Positive number entered.'Positive number entered.

Hier sehen wir die erweiterte Version von Variablen auf stdout vor der Ausführung. Es ist wichtig zu beachten, dass die Zeilen, denen das Vorzeichen + vorangestellt ist, vom xtrace-Modus generiert werden.

2.4. Identifizieren nicht gesetzter Variablen

Lassen Sie uns ein Experiment durchführen, um das Standardverhalten nicht gesetzter Variablen in Bash-Skripten zu verstehen:

#! /bin/bashfive_val=5two_val=2total=$((five_val+tow_val))echo $total

Wir führen nun das obige Skript aus:

$ ./add_values.sh5

Wie wir feststellen können, gibt es ein Problem: Das Skript wurde erfolgreich ausgeführt, aber die Ausgabe ist logisch falsch.

Wir führen das Skript nun mit der Option -u aus:

$ bash -u ./add_values.sh./add_values.sh: line 4: tow_val: unbound variable

Sicherlich gibt es jetzt viel mehr Klarheit!

Das Skript konnte nicht ausgeführt werden, da die Variable tow_val nicht definiert ist. Wir hatten bei der Berechnung der Summe fälschlicherweise two_val als tow_val eingegeben.

Die Option -u behandelt nicht gesetzte Variablen und Parameter als Fehler bei der Parametererweiterung. Folglich erhalten wir eine Fehlerbenachrichtigung, dass eine Variable beim Ausführen des Skripts mit der Option -u nicht an einen Wert gebunden ist

Anwendungsfälle zum Debuggen von Shell-Skripten

Bisher haben wir die verschiedenen Schalter zum Debuggen von Skripten gesehen. Im Folgenden werden wir uns einige Anwendungsfälle und Methoden ansehen, um diese in Shell-Skripten zu implementieren.

3.1. Debugging-Optionen kombinieren

Um bessere Einblicke zu erhalten, können wir die verschiedenen Optionen des Befehls set weiter kombinieren.

Führen wir unsere add_values.sh skript mit aktivierten Optionen -v und -u:

$ bash -uv ./add_values.sh#! /bin/bashfive_val=5two_val=2total=$((five_val+tow_val))./add_values.sh: line 4: tow_val: unbound variable

Wenn wir hier den ausführlichen Modus mit der Option -u aktivieren, können wir die Anweisung, die den Fehler auslöst, leicht identifizieren.

Ebenso können wir den ausführlichen und den xtrace-Modus kombinieren, um genauere Debug-Informationen zu erhalten.

Wie bereits erwähnt, zeigt die Option -v jede Zeile an, bevor sie ausgewertet wird, und die Option -x zeigt jede Zeile an, nachdem sie erweitert wurden. Daher können wir die Optionen -x und -v kombinieren, um zu sehen, wie Anweisungen vor und nach Variablensubstitutionen aussehen.

Jetzt führen wir unsere positive_check.sh skript mit aktiviertem -x- und -v-Modus:

$ bash -xv ./positive_check.sh#! /bin/bashread -p "Enter the input: " val+ read -p 'Enter the input: ' valEnter the input: 5zero_val=0+ zero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fi+ ''+ echo 'Positive number entered.'Positive number entered.

Wir können beobachten, dass die Anweisungen vor und nach der Variablenerweiterung auf stdout gedruckt werden.

3.2. Debuggen bestimmter Teile des Skripts

Debuggen mit -x oder -v Option Shell-Skripte erzeugt eine Ausgabe für jede Anweisung auf stdout. Es kann jedoch Situationen geben, in denen wir Debug-Informationen auf bestimmte Teile des Skripts reduzieren möchten. Wir können dies erreichen, indem wir den Debug-Modus aktivieren, bevor der Codeblock gestartet wird, und ihn später mit dem Befehl set zurücksetzen.

Überprüfen wir es anhand eines Beispiels:

#! /bin/bashread -p "Enter the input: " valzero_val=0set -xif then echo "Positive number entered."else echo "The input value is not positive."fiset +xecho "Script Ended" 

Hier konnten wir nur die if-Bedingung mit der set-Anweisung debuggen, bevor die Bedingung beginnt. Später könnten wir den xtrace-Modus zurücksetzen, nachdem der if-Block mit dem Befehl set + x beendet wurde.

Lassen Sie es uns mit der Ausgabe validieren:

$ ./positive_debug.shEnter the input: 7+ ''+ echo 'Positive number entered.'Positive number entered.+ set +xScript Ended

Sicherlich sieht die Ausgabe weniger überladen aus.

3.3. Nur die Debug-Ausgabe in eine Datei umleiten

Im vorherigen Abschnitt haben wir untersucht, wie wir das Debuggen auf bestimmte Teile des Skripts beschränken können. Folglich könnten wir die Ausgabemenge für stdout einschränken.

Darüber hinaus können wir die Debug-Informationen in eine andere Datei umleiten und die Skriptausgabe auf stdout drucken lassen.

Erstellen wir ein weiteres Skript, um es zu überprüfen:

#! /bin/bashexec 5> debug.log PS4='$LINENO: ' BASH_XTRACEFD="5" read -p "Enter the input: " valzero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fi

Zuerst öffneten wir das Debug.log file on File Descriptor (FD) 5 zum Schreiben mit dem Befehl exec.

Dann haben wir die spezielle Shell-Variable PS4 geändert. Die PS4-Variable definiert die Eingabeaufforderung, die angezeigt wird, wenn wir ein Shell-Skript im xtrace-Modus ausführen. Der Standardwert von PS4 ist +. Wir haben den Wert der PS4-Variablen geändert, um Zeilennummern in der Debug-Eingabeaufforderung anzuzeigen. Um dies zu erreichen, haben wir eine andere spezielle Shell-Variable LINENO .

Später haben wir die FD 5 der Bash-Variablen BASH_XTRACEFD zugewiesen. Tatsächlich schreibt Bash jetzt die xtrace-Ausgabe auf FD5, dh debug.Protokolldatei. Lassen Sie uns das Skript ausführen:

$ bash -x ./debug_logging.sh+ exec+ PS4='$LINENO: '4: BASH_XTRACEFD=5Enter the input: 2Positive number entered.

Wie erwartet wird die Debug-Ausgabe nicht auf das Terminal geschrieben. Zwar wurden die ersten Zeilen, bis FD 5 der Debug-Ausgabe zugeordnet ist, gedruckt.

Zusätzlich erstellt das Skript auch eine Ausgabedatei debug.log, das die Debug-Informationen enthält:

$ cat debug.log5: read -p 'Enter the input: ' val6: zero_val=07: ''9: echo 'Positive number entered.'

Debugging-Skripte mit Trap

Wir können die DEBUG-Trap-Funktion von Bash verwenden, um einen Befehl wiederholt auszuführen. Der in den Argumenten des Befehls angegebene Befehl wird vor jeder nachfolgenden Anweisung im Skript ausgeführt.

Lassen Sie uns dies anhand eines Beispiels veranschaulichen:

#! /bin/bashtrap 'echo "Line- ${LINENO}: five_val=${five_val}, two_val=${two_val}, total=${total}" ' DEBUGfive_val=5two_val=2total=$((five_val+two_val))echo "Total is: $total"total=0 && echo "Resetting Total"

In diesem Beispiel haben wir den Befehl echo angegeben, um die Werte der Variablen five_val, two_val und total zu drucken. Anschließend haben wir diese Echo-Anweisung mit dem DEBUG-Signal an den Trap-Befehl übergeben. Tatsächlich werden vor der Ausführung jedes Befehls im Skript die Werte von Variablen gedruckt.

Überprüfen wir die generierte Ausgabe:

$ ./trap_debug.shLine- 3: five_val=, two_val=, total=Line- 4: five_val=5, two_val=, total=Line- 5: five_val=5, two_val=2, total=Line- 6: five_val=5, two_val=2, total=7Total is: 7Line- 7: five_val=5, two_val=2, total=7Line- 7: five_val=5, two_val=2, total=0Resetting Total

Debuggen Bereits ausgeführter Skripte

Bisher haben wir Methoden vorgestellt, um Shell-Skripte während der Ausführung zu debuggen. Jetzt werden wir nach Möglichkeiten suchen, ein bereits ausgeführtes Skript zu debuggen.

Betrachten Sie ein laufendes Beispielskript, das sleep in einer unendlichen while-Schleife ausführt:

#! /bin/bashwhile :do sleep 10 & echo "Sleeping for 4 seconds.." sleep 4done

Mit Hilfe des Befehls pstree können wir die von unserem Skript gegabelten untergeordneten Prozesse überprüfen sleep.sh:

$ pstree -pinit(1)─┬─init(148)───bash(149)───sleep.sh(372)─┬─sleep(422) │ ├─sleep(424) │ └─sleep(425) ├─init(213)───bash(214)───pstree(426) └─{init}(7)

Wir haben eine zusätzliche Option -p verwendet, um die Prozess-IDs zusammen mit den Prozessnamen zu drucken. Daher können wir erkennen, dass das Skript darauf wartet, dass die untergeordneten Prozesse (Sleep) abgeschlossen werden.

Manchmal möchten wir uns die Vorgänge unserer Prozesse genauer ansehen. In solchen Fällen können wir den Befehl strace verwenden, um die laufenden Linux-Systemaufrufe zu verfolgen:

$ sudo strace -c -fp 372strace: Process 372 attachedstrace: Process 789 attachedstrace: Process 790 attached^Cstrace: Process 372 detachedstrace: Process 789 detachedstrace: Process 790 detached% time seconds usecs/call calls errors syscall------ ----------- ----------- --------- --------- ----------------100.00 0.015625 5208 3 wait4 0.00 0.000000 0 6 read 0.00 0.000000 0 1 write 0.00 0.000000 0 39 close 0.00 0.000000 0 36 fstat 0.00 0.000000 0 38 mmap 0.00 0.000000 0 8 mprotect 0.00 0.000000 0 2 munmap 0.00 0.000000 0 6 brk 0.00 0.000000 0 16 rt_sigaction 0.00 0.000000 0 20 rt_sigprocmask 0.00 0.000000 0 1 rt_sigreturn 0.00 0.000000 0 6 6 access 0.00 0.000000 0 1 dup2 0.00 0.000000 0 2 getpid 0.00 0.000000 0 2 clone 0.00 0.000000 0 2 execve 0.00 0.000000 0 2 arch_prctl 0.00 0.000000 0 37 openat------ ----------- ----------- --------- --------- ----------------100.00 0.015625 228 6 total

Hier haben wir die Option -p verwendet, um an die Prozess-ID (372) anzuhängen, dh an unser Skript in Ausführung. Darüber hinaus haben wir auch die Option -f verwendet, um alle untergeordneten Prozesse anzuhängen. Beachten Sie, dass der Befehl strace für jeden Systemaufruf eine Ausgabe generiert. Daher haben wir die Option -c verwendet, um eine Zusammenfassung der Systemaufrufe am Ende von strace zu drucken.

Fazit

In diesem Tutorial haben wir mehrere Techniken zum Debuggen eines Shell-Skripts untersucht.

Zu Beginn haben wir die verschiedenen Optionen des Befehls set und deren Verwendung zum Debuggen von Skripten besprochen. Danach haben wir mehrere Fallstudien implementiert, um eine Kombination von Debugging-Optionen zu untersuchen. Daneben haben wir auch Möglichkeiten untersucht, die Debug-Ausgabe einzuschränken und in eine andere Datei umzuleiten.

Als nächstes haben wir einen Anwendungsfall des Trap-Befehls und des DEBUG-Signals für Debugging-Szenarien vorgestellt. Schließlich haben wir einige Ansätze zum Debuggen bereits ausgeführter Skripte angeboten.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.

Previous post Seinem König zu trotzen: Wie die Schlacht von Arsuf gewonnen wurde
Next post Birds Eye View Fotografie Tipps