Wie finde ich die Zeilennummer in Bash, wenn ein Fehler aufgetreten ist?

21

Wie finde ich die Zeilennummer in Bash, in der ein Fehler aufgetreten ist?

Beispiel

Ich erstelle das folgende einfache Skript mit Zeilennummern, um zu erklären, was wir brauchen. Das Skript kopiert Dateien von

cp $file1 $file2
cp $file3 $file4

Wenn einer der cpBefehle fehlschlägt, wird die Funktion mit Exit 1 beendet . Wir möchten der Funktion die Möglichkeit hinzufügen, den Fehler auch mit der Zeilennummer auszudrucken (z. B. 8 oder 12).

Ist das möglich?

Beispielskript

1 #!/bin/bash
2
3
4 function in_case_fail {
5 [[ $1 -ne 0 ]] && echo "fail on $2" && exit 1
6 }
7
8 cp $file1 $file2
9 in_case_fail $? "cp $file1 $file2"
10
11
12 cp $file3 $file4
13 in_case_fail $? "cp $file3 $file4"
14
yael
quelle
Sie können set -xund / oder verwenden, um set -vzu verfolgen, was ausgeführt wurde. Nicht genau das, wonach Sie gefragt haben, aber es wird wahrscheinlich auch hilfreich sein.
Rolf

Antworten:

29

Anstatt Ihre Funktion zu verwenden, würde ich stattdessen diese Methode verwenden:

$ cat yael.bash
#!/bin/bash

set -eE -o functrace

file1=f1
file2=f2
file3=f3
file4=f4

failure() {
  local lineno=$1
  local msg=$2
  echo "Failed at $lineno: $msg"
}
trap 'failure ${LINENO} "$BASH_COMMAND"' ERR

cp -- "$file1" "$file2"
cp -- "$file3" "$file4"

Dies funktioniert, indem Sie ERR abfangen und dann die failure()Funktion mit der aktuellen Zeilennummer + Bash-Befehl aufrufen, der ausgeführt wurde.

Beispiel

Hier habe ich keine Sorge genommen , um die Dateien zu erstellen, f1, f2, f3, oder f4. Wenn ich das obige Skript ausführe:

$ ./yael.bash
cp: cannot stat f1’: No such file or directory
Failed at 17: cp -- "$file1" "$file2"

Es schlägt fehl und gibt die Zeilennummer plus den ausgeführten Befehl an.

slm
quelle
14

Neben LINENOder aktuellen Zeilennummer stehen die Zeichen BASH_LINENOund FUNCNAME(undBASH_SOURCE Arrays ), die die Funktionsnamen und Zeilennummern enthalten, von denen aus sie aufgerufen werden.

Sie könnten also so etwas tun:

#!/bin/bash

error() {
        printf "'%s' failed with exit code %d in function '%s' at line %d.\n" "${1-something}" "$?" "${FUNCNAME[1]}" "${BASH_LINENO[0]}"
}

foo() {
        ( exit   0 ) || error "this thing"
        ( exit 123 ) || error "that thing"
}

foo

Laufen würde das drucken

'that thing' failed with exit code 123 in function 'foo' at line 9.

Wenn Sie set -eoder verwenden trap ... ERR, um Fehler automatisch zu erkennen, beachten Sie, dass sie einige Einschränkungen aufweisen. Es ist auch schwieriger, eine Beschreibung der Funktionsweise des Skripts (wie in Ihrem Beispiel) einzufügen, obwohl dies für einen normalen Benutzer möglicherweise nützlicher ist als nur die Zeilennummer.

Siehe zB diese für die Probleme mit set -eund anderen:

ilkkachu
quelle
13

Bash hat eine eingebaute Variable, $LINENOdie in einer Anweisung durch die aktuelle Zeilennummer ersetzt wird

in_case_fail $? "at $LINENO: cp $file1 $file2"

Sie können auch versuchen, trap ... ERRdie Methode zu verwenden, die ausgeführt wird, wenn ein Befehl fehlschlägt (wenn das Ergebnis nicht getestet wird). Z.B:

trap 'rc=$?; echo "error code $rc at $LINENO"; exit $rc' ERR

Wenn ein Befehl wie cp $file1 $file2fehlschlägt, erhalten Sie die Fehlermeldung mit der Zeilennummer und einem Exit. Sie finden den fehlerhaften Befehl auch in Variable $BASH_COMMAND(obwohl keine Umleitungen usw.).

meuh
quelle