GNU/Linux >> LINUX-Kenntnisse >  >> Linux

So debuggen Sie Bash-Skripte in Linux und Unix

Debugging hilft Ihnen, die Fehler in Ihrem Programm zu beheben. In diesem Artikel werden wir verschiedene Methoden zum Debuggen von Bash-Skripten in Linux- und Unix-Betriebssystemen diskutieren.

Einführung

In meinen ersten Programmiertagen habe ich Stunden damit verbracht, den Fehler in meinem Code zu finden, und am Ende könnte es etwas Einfaches sein. Vielleicht waren Sie auch schon einmal in der gleichen Situation.

Wenn Sie wissen, wie Sie die richtige Debugging-Technik anwenden, können Sie die Fehler schnell beheben. Im Gegensatz zu anderen Sprachen wie Python, Java usw. gibt es für Bash kein Debugger-Tool, mit dem Sie Haltepunkte setzen, Code überspringen usw.

Es gibt einige integrierte Funktionen, die beim Debuggen der Bash-Shell-Skripte helfen. Wir werden diese Funktionen in den nächsten Abschnitten im Detail sehen.

Drei Möglichkeiten, Debugging-Optionen zu verwenden

Wenn Sie Debugging-Optionen in Ihren Skripten aktivieren möchten, können Sie dies auf drei Arten tun.

1 . Aktivieren Sie beim Aufruf des Skripts die Debugging-Optionen der Terminal-Shell.

$ bash [ debugging flags ] scriptname

2 . Aktivieren Sie die Debugging-Optionen, indem Sie die Debugging-Flags an die Shebang-Zeile im Skript übergeben.

#!/bin/bash [ debugging flags ]

3 . Aktivieren Sie die Debugging-Optionen mit set Befehl aus dem Skript.

set -o nounset
set -u

Wofür wird der Set-Befehl verwendet?

Der set command ist ein in die Shell integrierter Befehl, der verwendet werden kann, um die Bash-Parameter zu steuern und das Bash-Verhalten auf bestimmte Weise zu ändern.

Normalerweise führen Sie keine set-Befehle vom Terminal aus, um Ihr Shell-Verhalten zu ändern. Es wird häufig in Shell-Skripten verwendet, entweder zum Debuggen oder um den strengen Bash-Modus zu aktivieren.

$ type -a set
set is a shell builtin

Sie können auf den Hilfeabschnitt des set-Befehls zugreifen, um zu erfahren, welche Flags er unterstützt und was die einzelnen Flags bewirken.

$ set --help

Teil des Skripts oder vollständiges Skript debuggen

Bevor Sie sich mit den Debugging-Optionen vertraut machen, müssen Sie verstehen, dass Sie entweder das gesamte Skript oder nur einen bestimmten Teil des Codes debuggen können. Sie müssen den set-Befehl verwenden, um die Debugging-Optionen zu aktivieren und zu deaktivieren.

  • set -<debugging-flag> aktiviert den Debug-Modus.
  • set +<debugging-flag> deaktiviert den Debug-Modus.

Sehen Sie sich den folgenden Code an. set -x aktiviert den Xtrace-Modus für das Skript und set +x deaktiviert den Xtrace-Modus. Alles, was zwischen set -x steht und set +x wird im Xtrace-Debugging-Modus ausgeführt.

Im nächsten Abschnitt erfahren Sie mehr über den xtrace-Modus. Das Einzige, was Sie sich also für jedes Debugging-Flag merken müssen, ist:set - aktiviert den Modus und set + deaktiviert den Modus.

#!/bin/bash

set -x
read -p "Pass Dir name : " D_OBJECT
read -p "Pass File name : " F_OBJECT
set +x

touch ${D_OBJECT}/${F_OBJECT}

Fehler, wenn keine Variable definiert ist

Beim Arbeiten mit Variablen in Bash , der Nachteil ist, wenn wir versuchen, eine undefinierte Variable zu verwenden, schlägt das Skript nicht mit einer Fehlermeldung wie "Variable nicht definiert" fehl . Stattdessen wird ein leerer String ausgegeben.

Sehen Sie sich den folgenden Code an, in dem ich Eingaben vom Benutzer erhalte und in der Variablen $OBJECT speichere . Ich habe versucht, den Testoperator auszuführen (-f und -d ) auf $OBJECT1 Variable, die nicht definiert ist.

#!/bin/bash

read -p "Please provide the object name :  " OBJECT
if [[ -f $OBJECT1 ]]
then
    echo "$OBJECT is a file"
elif [[ -d $OBJECT1 ]]
then
    echo "$OBJECT is a directory"
fi

Wenn ich diesen Code ausführe, hätte er mir einen Fehler ausgeben sollen, aber das tat er nicht und sogar das Skript wurde mit dem Rückgabecode Null beendet.

Um dieses Verhalten zu überschreiben, verwenden Sie -u Flag, das einen Fehler auslöst, wenn eine undefinierte Variable verwendet wird.

Ich werde denselben Code noch einmal mit dem falschen Variablennamen ausführen, aber dieses Mal wird eine "Ungebundene Variable" ausgegeben Fehler.

Sie können auch das -u setzen Option mit set Befehl oder als Argument an Shebang übergeben.

set -u
set -o nounset

(oder)

#! /bin/bash -u

Xtrace-Modus zur Rettung

Dies ist der Modus, den ich häufig verwende, wenn ich Bash-Skripte auf logische Fehler debugge. Xtrace Der Modus zeigt den Code Zeile für Zeile an, jedoch mit erweiterten Parametern.

Im vorherigen Abschnitt, als ich den Code ohne -u ausgeführt habe Flag, es wurde erfolgreich abgeschlossen, aber ich hatte die Ausgabe im Terminal erwartet. Jetzt kann ich dasselbe Skript im Xtrace-Modus ausführen und genau sehen, wo das Problem im Skript auftritt.

Sehen Sie sich den folgenden Beispielcode an.

#!/bin/bash

read -p "Please provide the object name :  " OBJECT
if [[ -f $OBJECT1 ]]
then
    echo "$OBJECT is a file"
elif [[ -d $OBJECT1 ]]
then
    echo "$OBJECT is a directory"
fi

Wenn ich den obigen Code ausführe, gibt er mir keine Ausgabe zurück.

Um dieses Problem zu beheben, kann ich das Skript in xtrace ausführen Modus durch Übergeben des -x Flagge.

In der folgenden Ausgabe können Sie sehen, dass die Variablen erweitert und ausgedruckt werden. Dies sagt mir, dass den bedingten Anweisungen -f leere Zeichenfolgen zugewiesen sind und -d . So kann ich die Fehler logisch überprüfen und beheben.

Das Pluszeichen, das Sie in der Ausgabe sehen, kann durch Einstellen des PS4 geändert werden Variable im Skript. Standardmäßig ist PS4 auf (+ ).

$ echo $PS4
+
$ PS4=" ==> " bash -x debugging.sh

Sie können den Xtrace-Modus auch mit dem set-Befehl setzen oder als Argument an den Shebang übergeben.

set -x
set -o xtrace

(oder)

#! /bin/bash -x

In ähnlicher Weise können Sie beim Debuggen die Xtrace-Debug-Protokolle in eine Datei umleiten, anstatt sie auf dem Terminal zu drucken.

Sehen Sie sich den folgenden Code an. Ich weise dem .log einen Dateideskriptor 6 zu Datei und BASH_XTRACEFD="6" leitet die Xtrace-Debug-Protokolle zu Dateideskriptor 6.

um
#!/bin/bash


exec 6> redirected_debug.log 
PS4=' ==> ' 
BASH_XTRACEFD="6" 
read -p "Please provide the object name :  " OBJECT
if [[ -f $OBJECT1 ]]
then
    echo "$OBJECT is a file"
elif [[ -d $OBJECT1 ]]
then
    echo "$OBJECT is a directory"
fi

Wenn ich diesen Code ausführe, anstatt die xtrace-Ausgabe im Terminal zu drucken, wird er an .log umgeleitet Datei.

$ cat redirected_debug.log 
==> read -p 'Please provide the object name :  ' OBJECT
==> [[ -f '' ]]
==> [[ -d '' ]]

PIPE-Exit-Status

Das Standardverhalten bei der Verwendung einer Pipe ist, dass der Exit-Code des letzten Ausführungsbefehls in der Pipe verwendet wird. Selbst wenn die vorherigen Befehle in der Pipe fehlgeschlagen sind, wird der Rest der Pipe ausgeführt.

Schauen Sie sich das folgende Beispiel an. Ich habe versucht, eine Datei zu öffnen, die nicht verfügbar ist, und sie mit einer Wortzahl zu versehen Programm. Obwohl die cat Befehl einen Fehler auslöst, wird das Wortzählprogramm ausgeführt.

Wenn Sie versuchen, den Exit-Code des letzten Run-Pipe-Befehls mit $? zu überprüfen , erhalten Sie Null als Exit-Code, der aus dem Wortzählprogramm stammt.

$ cat nofile.txt | wc -l
cat: nofile.txt: No such file or directory
0
$ echo $?
0

Wenn Pipefail im Skript aktiviert ist und ein Befehl einen Rückgabecode ungleich Null in der Pipe auslöst, wird dieser als Rückgabecode für die gesamte Pipeline betrachtet. Sie können Pipefail aktivieren, indem Sie Ihrem Skript die folgende set-Eigenschaft hinzufügen.

set -o pipefail

Es gibt immer noch ein Problem mit diesem Ansatz. Normalerweise sollte erwartet werden, dass, wenn ein Befehl in der Pipe fehlschlägt, das Skript beendet wird, ohne den Rest des Befehls in der Pipe auszuführen.

Aber leider, selbst wenn irgendein Befehl fehlschlägt, läuft der nachfolgende Befehl in der Pipe. Dies liegt daran, dass jeder Befehl in der Pipe in einer eigenen Subshell ausgeführt wird. Die Shell wartet, bis alle Prozesse in der Pipe abgeschlossen sind, und gibt dann das Ergebnis zurück.

Strikter Bash-Modus

Um alle möglichen Fehler zu beseitigen, die wir in den vorherigen Abschnitten gesehen haben, wird empfohlen, die folgenden Optionen in jedem Skript hinzuzufügen.

All diese Möglichkeiten haben wir bereits im vorigen Abschnitt ausführlich besprochen.

  • -e flag => Beenden Sie das Skript, wenn ein Befehl einen Exit-Code ungleich Null auslöst.
  • -u flag => Lassen Sie das Skript fehlschlagen, wenn ein undefinierter Variablenname verwendet wird.
  • pipefail => Wenn ein Befehl in der Pipeline fehlschlägt, wird der Exit-Code für die gesamte Pipeline berücksichtigt.
  • IFS => Internes Feldtrennzeichen, wenn es auf Zeilenumbruch (\n) und (\t) gesetzt wird, wird die Aufteilung nur in Zeilenumbruch und Tabulator erfolgen.
set -e
set -u
set -o pipefail

Oder

set -euo pipefail
IFS=$'\n\t'

Signale mit TRAP erfassen

Falle ermöglicht es Ihnen, Signale für Ihr Bash-Skript zu erfassen und entsprechende Aktionen auszuführen.

Stellen Sie sich ein Szenario vor, in dem Sie das Skript auslösen, aber das Skript mit CTRL+C abbrechen möchten Tastenanschlag. In diesem Fall SIGINT wird an Ihr Skript gesendet. Sie können dieses Signal erfassen und einige Befehle oder Funktionen ausführen.

Schauen Sie sich den unten angegebenen Pseudocode an. Ich habe eine Funktion namens cleanup erstellt, die ausgeführt wird, wenn SIGINT wird an das Skript übergeben.

trap 'cleanup' TERM INT
function cleanup(){
    echo "Running cleanup since user initiated CTRL + C"
    <some logic>
}

Sie können den Trap "DEBUG" verwenden, mit dem eine Anweisung im Skript wiederholt ausgeführt werden kann. Es verhält sich so, dass jede Anweisung, die im Skript-Trap ausgeführt wird, die zugehörige Funktion oder Anweisung ausführt.

Sie können dies anhand des folgenden Beispiels verstehen.

#!/bin/bash

trap 'printf "${LINENO} ==> DIR_NAME=${D_OBJECT} ; FILE_NAME=${F_OBJECT}; FILE_CREATED=${FILE_C} \n"' DEBUG

read -p "Pass Dir name : " D_OBJECT
read -p "Pass File name : " F_OBJECT

touch ${D_OBJECT}/${F_OBJECT} && FILE_C="Yes"
exit 0

Dies ist ein einfaches Programm, das Benutzereingaben erhält und eine Datei und ein Verzeichnis erstellt. Der Trap-Befehl wird für jede Anweisung im Skript ausgeführt und gibt die übergebenen Argumente und den Status der Dateierstellung aus.

Sehen Sie sich die folgende Ausgabe an. Für jede Zeile im Skript wird der Trap ausgelöst und die Variablen werden entsprechend aktualisiert.

Drucken Sie den Code im ausführlichen Modus

Im ausführlichen Modus wird der Code ausgedruckt, bevor das Ergebnis zurückgegeben wird. Wenn das Programm eine interaktive Eingabe erfordert, wird in diesem Fall nur diese Zeile gedruckt, gefolgt von einem Codeblock.

Schauen Sie sich das folgende Programm an. Es ist ein einfaches Programm, das ein Objekt vom Benutzer erhält und mithilfe einer bedingten Anweisung überprüft, ob das übergebene Objekt eine Datei oder ein Verzeichnis ist .

#!/bin/bash

read -p "Please provide the object name :  " OBJECT
if [[ -f $OBJECT ]]
then
  echo "$OBJECT is a file"
elif [[ -d $OBJECT ]]
then
  echo "$OBJECT is a directory"
fi

Wenn ich den obigen Code ausführe, wird zuerst der Code gedruckt und dann wie unten gezeigt auf Benutzereingaben gewartet.

Sobald ich das Objekt übergeben habe, wird der Rest des Codes gedruckt, gefolgt von der Ausgabe.

Sie können den Verbose-Modus auch mit set einstellen oder in shebang .

set -v
set -o verbose

(oder)

#! /bin/bash -v

Sie können den ausführlichen Modus auch mit anderen Modi kombinieren.

set -vx # Verbose and Xtrace Mode
set -uv # Verbose and Unset Mode

Syntaxvalidierung - noexec-Modus

Bisher haben wir gesehen, wie man mit logischen Fehlern im Skript arbeitet. Lassen Sie uns in diesem Abschnitt über Syntaxfehler sprechen.

Syntaxfehler kommen in Programmen sehr häufig vor. Möglicherweise haben Sie ein Zitat verpasst oder die Schleife nicht verlassen usw. Sie können den Befehl „-n "-Flag, das noexec mode genannt wird um die Syntax zu validieren, bevor das Programm ausgeführt wird.

Ich werde den folgenden Code ausführen und die Syntax validieren.

#!/bin/bash

TOOLS=( htop peek tilix vagrant shutter )
for TOOL in "${TOOLS[@]" 
do
    echo "--------------INSTALLING: ${TOOL}---------------------------"
    apt install ${TOOL} -y
#done

Es gibt zwei Fehler in diesem Programm. Erstens habe ich die geschweiften Klammern in der "for loop nicht geschlossen " und zweitens done Das Schlüsselwort ist auskommentiert, was das Ende der Schleife markieren sollte.

Wenn ich dieses Programm ausführe, erhalte ich die folgenden Fehlermeldungen, die auf eine fehlende geschweifte Klammer hinweisen und fertiges Schlüsselwort . Manchmal enthält die Zeilennummer, auf die in der Fehlermeldung verwiesen wird, keine Fehler, die Sie durchsuchen sollten, um den tatsächlichen Fehler zu finden.

$ bash -n ./debugging.sh 

./debugging.sh: line 6: unexpected EOF while looking for matching `"'
./debugging.sh: line 8: syntax error: unexpected end of file

Es ist zu beachten, dass bash beim Ausführen des Skripts standardmäßig die Syntax validiert und diese Fehler ausgibt, auch ohne den noexec-Modus zu verwenden.

Alternativ können Sie auch das set verwenden Befehl oder Shebang, um noexec zu verwenden Modus.

set -n 
set -o noexec

Oder,

#! /bin/bash -n

Es gibt ein paar externe Tools, die einen Blick wert sind, um die Skripts zu debuggen. Ein solches Tool ist Shellcheck . Shellcheck kann auch in gängige Texteditoren wie vscode, sublime text, Atom integriert werden.

Schlussfolgerung

In diesem Artikel habe ich Ihnen einige Möglichkeiten zum Debuggen von Bash-Skripten gezeigt. Im Gegensatz zu anderen Programmiersprachen verfügt Bash über keine anderen Debug-Tools als einige integrierte Optionen. Manchmal reichen diese integrierten Debugging-Optionen mehr als aus, um die Arbeit zu erledigen.

Bash-Scripting-Leitfäden:

  • Bash-Skripting – Analysieren Sie Argumente in Bash-Skripten mithilfe von getopts
  • Erstellen von GUI-Dialogfeldern in Bash-Skripten mit Zenity unter Linux und Unix
  • Bash-Skripting – Case-Statement
  • Bash-Skripting – Bedingte Anweisungen
  • Bash-Skripting – String-Manipulation
  • Bash-Skripting – Printf-Befehl mit Beispielen erklärt
  • Bash-Skripting – Indiziertes Array mit Beispielen erklärt
  • Bash-Skripting – Assoziatives Array mit Beispielen erklärt
  • Bash-Skripting – For-Schleife mit Beispielen erklärt
  • Bash-Skripting – While- und Until-Schleife mit Beispielen erklärt
  • Bash-Umleitung mit Beispielen erklärt
  • Bash-Scripting – Variablen mit Beispielen erklärt
  • Bash-Scripting – Funktionen mit Beispielen erklärt
  • Bash-Echo-Befehl mit Beispielen in Linux erklärt
  • Bash Heredoc-Tutorial für Anfänger

Linux
  1. So legen Sie eine statische IP-Adresse fest und konfigurieren das Netzwerk unter Linux

  2. So setzen/erstellen Sie Umgebungs- und Shell-Variablen in Linux

  3. So setzen, listen und entfernen Sie Umgebungsvariablen in Linux

  4. UNIX / Linux :Wie man Mutt installiert und konfiguriert

  5. Wie setze ich $PATH dauerhaft unter Linux/Unix?

So richten Sie den öffentlichen und privaten SSH-Schlüssel unter Linux ein

So verwenden Sie den Echo-Befehl in Bash-Skripten unter Linux

So stellen Sie Datum und Uhrzeit unter Linux ein

So setzen und löschen Sie Umgebungsvariablen unter Linux

So löschen Sie den Bash-Verlauf in Linux und Mac

So installieren und richten Sie 1Password auf einem Linux-Desktop ein