Ich schreibe ein Bash-Skript für mich, um das Schreiben von Skripten zu lernen. Irgendwann muss ich Trap hinzufügen, um unerwünschte Verzeichnisse und Dateien zu bereinigen, wenn das Skript beendet wird. Aus irgendeinem Grund, den ich nicht verstehe, ruft Trap die Reinigungsfunktion auf clean_a()
- wenn das Skript beendet wird, zeigt aber $LINENO
auf eine Zeile in der Reinigungsfunktion selbst, nicht in der Funktion archieve_it()
- wenn das Skript beendet wird.
Erwartetes Verhalten:
- Skript ausführen
- Drücken Sie Ctrl+C
- Trap Caches Ctrl+ Cund ruft
clean_a()
Funktion auf clean_a()
Die Funktion gibt die Zeilennummer wieder, die Ctrl+ Cgedrückt wird. Lassen Sie es Zeile 10 in seinarchieve_it()
.
Was passiert eigentlich:
- Skript ausführen
- Drücken Sie Ctrl+C
- Trap Caches Ctrl+ Cund ruft
clean_a()
Funktion auf clean_a()
gibt eine irrelevante Zeilennummer wieder. Sagen wir, Zeile 25 inclean_a()
Funktion.
Hier ist ein Beispiel als Teil meines Skripts:
archieve_it () {
trap 'clean_a $LINENO $BASH_COMMAND'\
SIGHUP SIGINT SIGTERM SIGQUIT
for src in ${sources}; do
mkdir -p "${dest}${today}${src}"
if [[ "$?" -ne 0 ]] ; then
error "Something!"
fi
rsync "${options}" \
--stats -i \
--log-file="${dest}${rsync_log}" \
"${excludes}" "${src}" "${dest}${today}${src}"
done
}
clean_a () {
error "something!
line: $LINENO
command: $BASH_COMMAND
removing ${dest}${today}..."
cd "${dest}"
rm -rdf "${today}"
exit "$1"
}
PS: Original - Skript zu sehen ist hier . Definitionen und Variablennamen sind auf Türkisch. Wenn es benötigt wird, kann ich alles ins Englische übersetzen.
BEARBEITEN: Ich ändere das Skript so gut ich kann gemäß der Erklärung von @ mikeserv wie folgt:
#!/bin/bash
PS4='DEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO $BASH_COMMAND'\
SIGHUP SIGINT SIGTERM SIGQUIT
..
}
clean_a () {
error " ...
line: $LINENO $LASTNO
..."
}
Wenn ich nun ein Skript mit ausführe set -x
und es mit Ctrl+ Cbeende, wird die richtige Zeilennummer ausgegeben, wie unten zu sehen ist:
DDEBUG: 1 : clean_a 1 336 rsync '"${options}"' ...
In der clean_a()
Funktion wird der Wert von $LASTNO
jedoch als 1 gedruckt.
line: 462 1
Hat es etwas mit dem Fehler zu tun, der von @Arkadiusz Drabczyk angezeigt wird?
EDIT2 : Ich habe das Skript genau so geändert, wie es mir @mikesrv empfohlen hat. Aber $ LASTNO hat 1 als Wert der Zeile zurückgegeben, als das Skript beendet wurde (es hätte 337 sein sollen).
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO "$BASH_COMMAND"' \
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
clean_a () {
error " ...
line: $LASTNO $LINENO
..."
} 2>&1
Wenn ich ein Skript ausführe und es mit Ctrl+ Cbeende, während rsync ausgeführt wurde, erhalte ich folgende Ausgabe:
^^MDEBUG: 1 : clean_a '337 1 rsync "${options}" --delete-during ...
...
line: 1 465
Wie Sie sehen können, ist der Wert von $ LASTNO 1.
Während ich versuchte herauszufinden, wo das Problem liegt, schrieb ich eine andere Funktion - testing
- unter Verwendung des Parametersubstitutionsformats ${parameter:-default}
. Das Skript stellte sich also wie folgt heraus:
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'testing "$LASTNO $LINENO $BASH_COMMAND"'\
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
testing() {
echo -e "${1:-Unknown error!}"
exit 1
} 2>&1
Wenn ich nun ein Skript ausführe und Ctrl+ drücke C, erhalte ich folgende Ausgabe:
^^MDEBUG: 1 : testing '337 1 rsync "${options}" --delete-during ...
337 1 rsync "${options}" --delete-during ...
337 zeigt auf die Linie, als ich Ctrl+ drückte C, während rsync lief.
Für einen weiteren Test habe ich versucht, eine clear_a
Funktion wie diese zu schreiben :
clear_a () {
echo -e " $LASTNO $LINENO"
}
und $ LASTNO gab immer noch 1 zurück.
Das bedeutet also, dass wir beim Beenden des Skripts die korrekte Zeilennummer erhalten können, wenn wir die Parametersubstitution verwenden?
EDIT3 Es scheint, dass ich die Erklärung von @ mikeserv in EDIT2 falsch angewendet habe. Ich habe meinen Fehler korrigiert. Der Positionsparameter "$1
sollte in der Funktion durch $ LASTNO clear_a
ersetzt werden.
Hier ist das Skript, das so funktioniert, wie ich es haben möchte:
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO "$BASH_COMMAND"' \
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
clean_a () {
error " ...
line: $1
..."
} 2>&1
Wenn das Skript beendet wird, werden trap
das $LASTNO
erste Argument, das $LINENO
zweite Argument und das dritte Argument ausgewertet und $BASH_COMMAND
die Werte an die clear_a
Funktion übergeben. Schließlich drucken wir $ LASTNO mit $1
der Zeilennummer, mit der das Skript beendet wird.
BASH_LINENO
Array, das wahrscheinlich genau das ist, was Sie wollen, anstatt nurLINENO
.Antworten:
Ich denke, das Problem ist, dass Sie erwarten
"$LINENO"
, Ihnen die Ausführungszeile für den letzten Befehl zu geben, die fast funktioniert, aberclean_a()
auch eine eigene bekommt$LINENO
und die Sie stattdessen tun sollten:Aber selbst das würde wahrscheinlich nicht funktionieren, da ich davon ausgehe, dass nur die Zeile gedruckt wird, in der Sie die Einstellung vorgenommen haben
trap
.Hier ist eine kleine Demo:
AUSGABE
Also wird das
trap
gesetzt, dannfn()
definiert und dannecho
ausgeführt. Wenn die Shell die Ausführung ihrer Eingabe abgeschlossen hat, wird derEXIT
Trap ausgeführt undfn
aufgerufen. Es wird ein Argument übergeben - das ist das dertrap
Zeile$LINENO
.fn
druckt zuerst sein eigenes,$LINENO
dann sein erstes Argument.Ich kann mir einen Weg vorstellen, wie Sie das Verhalten erreichen können, das Sie erwarten, aber es bringt die Schale irgendwie durcheinander
stderr
:AUSGABE
Es verwendet die
$PS4
Debug-Eingabeaufforderung der Shell , um$LASTNO
in jeder ausgeführten Zeile zu definieren . Es ist eine aktuelle Shell-Variable, auf die Sie überall im Skript zugreifen können. Das bedeutet, dass Sie unabhängig von der Zeile, auf die gerade zugegriffen wird, auf die letzte Zeile des Skripts verweisen können, in dem ausgeführt wird$LASTNO
. Wie Sie sehen, wird es natürlich mit einer Debug-Ausgabe geliefert. Sie können das2>/dev/null
für den Großteil der Ausführung des Skripts verschieben, und dann einfach2>&1
inclean_a()
oder so.Der Grund , warum Sie bekommen
1
in$LASTNO
ist , denn das ist der letzte Wert , auf dem$LASTNO
gesetzt wurde , weil das der letzte war$LINENO
Wert. Sie haben Ihretrap
in derarchieve_it()
Funktion und so bekommt es seine eigene,$LINENO
wie in der Spezifikation unten angegeben. Obwohl esbash
dort sowieso nicht so aussieht, als ob es das Richtige tut, kann es auch daran liegen, dasstrap
das Shell-INT
Signal erneut ausgeführt werden muss und$LINENO
daher zurückgesetzt wird. In diesem Fall bin ich ein wenig verwirrt - wie esbash
anscheinend ist.Sie wollen nicht zu bewerten ,
$LASTNO
inclean_a()
, glaube ich. Besser wäre es in der zu bewertentrap
und den Wert übergebentrap
erhält in$LASTNO
durchclean_a()
als Argument. Vielleicht so:Versuchen Sie das - es sollte tun, was Sie wollen, denke ich. Oh - und beachten Sie, dass in
PS4=^M
der^M
eine wörtliche Rückkehr ist - wie STRG + V ENTER.Aus der POSIX-Shell-Spezifikation :
quelle
Die Lösung von mikeserv ist gut, aber er sagt zu Unrecht, dass
fn
dietrap
Linie überschritten wird,$LINENO
wenn die Falle ausgeführt wird. Fügentrap ...
Sie vorher eine Zeile ein und Sie werden sehen, dass diesefn
tatsächlich immer übergeben wird1
, unabhängig davon, wo die Falle deklariert wurde.AUSGABE
Da das erste Argument, das abgefangen werden
fn "$LINENO"
soll, in einfache Anführungszeichen gesetzt wird,$LINENO
wird es genau dann erweitert , wenn EXIT ausgelöst wird, und sollte daher auf erweitert werdenfn 5
. Warum nicht? Tatsächlich war dies bis zu Bash-4.0 der Fall, als es absichtlich geändert wurde, sodass $ LINENO beim Auslösen des Traps auf 1 zurückgesetzt wird und daher auf erweitert wirdfn 1
. [Quelle] Das ursprüngliche Verhalten wird jedoch für ERR-Traps beibehalten, wahrscheinlich weil wie oft so etwastrap 'echo "Error at line $LINENO"' ERR
verwendet wird.AUSGABE
getestet mit GNU Bash, Version 4.3.42 (1) -Veröffentlichung (x86_64-pc-linux-gnu)
quelle