Letzter fehlgeschlagener Befehl in Bash

Antworten:

6

Verwenden Sie fcdiese Option , um die vorherige Befehlszeile abzurufen. Es wird normalerweise verwendet, um die vorherige Befehlszeile in Ihrem bevorzugten Editor zu bearbeiten, hat aber auch einen "Listen" -Modus:

last_command="$(fc -nl -1)"

janmoesen
quelle
Das funktioniert leider nicht gut recht , wie ich erwartet habe , wenn es große Case - Anweisungen in Gebrauch oder Funktionen im Einsatz ist ... :( Ich landete mit callerund der Bash - Arrays BASH_LINENO, BASH_SOURCEund FUNCNAMEeine Art von Stack - Trace zu tun.
phyatt
6

Wenn der letzte Befehl ohne Argumente ausgeführt wurde, wird er in der $_Variablen gespeichert . Dies enthält normalerweise das letzte Argument des vorherigen Befehls. Wenn also keine Argumente vorhanden waren, ist der Wert von $_der letzte Befehl selbst.

Eine andere Möglichkeit besteht darin, die Details des letzten Hintergrundbefehls zu erfahren . Wie l0b0 schrieb, $!hält seine PID - so können Sie die Ausgabe von analysieren ps $!(möglicherweise mit zusätzlichen Formatierungsoptionen zu ps).

rozcietrzewiacz
quelle
2

Nein, aber Sie können es während der Ausführung zum Speichern für andere Befehle erhalten:

  • $0: Pfad des aktuellen Shell-Skripts.
  • $FUNCNAME: "Name der aktuellen Funktion."
  • "$@": Alle Parameter des aktuellen Befehls werden separat angegeben.
  • $!: "PID (Prozess-ID) des letzten im Hintergrund ausgeführten Jobs."
  • $$: "Prozess-ID (PID) des Skripts selbst."

Der vollständige Befehl des aktuellen Skripts sollte daher lauten "$0" "$@". Wenn es eine Funktion ist, sollte es sein "$FUNCNAME" "$@". Möglicherweise möchten Sie dies für die zukünftige Verarbeitung in einem Array speichern. Speichern Sie dies beispielsweise in test.sh:

#!/usr/bin/env bash
foo()
{
    declare -a command=("$0")
    for param in "$@"
    do
        command+=("$(printf %q "$param")")
    done
    echo "${command[@]}"
}
foo "$@"

Beim Ausführen ./test.sh "first argument" "second argument"sollte Folgendes zurückgegeben werden:

./test.sh first\ argument second\ argument

Welches sind gleichwertige Anrufe.

l0b0
quelle
In Bash gibt es eine BASH_COMMANDVariable, die aber abgesehen von der Verwendung in Fallen in keiner Weise nützlich zu sein scheint.
Enzotib
Danke für deinen Beitrag. Was ist, wenn ich some-commandin einem Shell-Skript ausgeführt werde und es fehlschlägt ? Ich habe einen Status ungleich Null in $?. Wird "Nein" immer noch für das Vorhandensein von variablem Halten gelten some-command?
Eimantas
Soweit ich weiß, ändert die einzige Tatsache, dass ein Befehl fehlgeschlagen ist, nichts an den Informationen, die Ihre Shell darüber speichert. Also würde ich ja sagen , "nein" .
Rozcietrzewiacz
2

Mit dem DEBUGTrap können Sie einen Befehl unmittelbar vor einer einfachen Befehlsausführung ausführen. In der BASH_COMMANDVariablen ist eine Zeichenfolgenversion des auszuführenden Befehls (mit durch Leerzeichen getrennten Wörtern) verfügbar .

trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG

echo "last command is $previous_command"

Beachten Sie, dass previous_commandsich dies jedes Mal ändert, wenn Sie einen Befehl ausführen. Speichern Sie ihn daher in einer Variablen, um ihn zu verwenden. Wenn Sie auch den Rückgabestatus des vorherigen Befehls wissen möchten, speichern Sie beide in einem einzigen Befehl.

cmd=$previous_command ret=$?
if [ $ret -ne 0 ]; then echo "$cmd failed with error code $ret"; fi

Wenn Sie nur bei fehlgeschlagenen Befehlen abbrechen möchten, verwenden Sie diese Option set -e, um das Skript beim ersten fehlgeschlagenen Befehl zu beenden. Sie können den letzten Befehl aus der EXITTrap anzeigen .

set -e
trap 'echo "exit $? due to $previous_command"' EXIT

Ein alternativer Ansatz, der für einige Verwendungszwecke geeignet sein könnte, besteht darin set -x, eine Ablaufverfolgung der Skriptausführung zu drucken und die letzten Zeilen der Ablaufverfolgung zu untersuchen.

Gilles 'SO - hör auf böse zu sein'
quelle
0

Ich finde es wichtig, den letzten fehlgeschlagenen Befehl zu finden, wenn ich set -eund set -o pipefailOptionen habe, da sonst bash einfach abgebrochen wird, ohne Rückmeldung darüber, warum. Deshalb habe ich festgestellt, dass dies gut funktioniert:

#!/usr/bin/env bash
set -eu
set -o pipefail

cur_command=
first_err_command=
first_err_lineno=
# This trap is executed in exactly the same conditions in which the `set -e` results in an exit.
trap 'cur_command=$BASH_COMMAND;
      if [[ -z "$first_err_command" ]]; then
          first_err_command=$cur_command;
          first_err_lineno=$LINENO;
      fi' ERR
trap 'if [[ ! -z "$first_err_command" ]]; then
          echo "ERROR: Aborting at line: $first_err_lineno on command: $first_err_command";
      fi' EXIT

echo "The following command causes bash to abort, but it should also result in a nice message"
false
echo "This message is not expected"

Wenn Sie die oben genannten Schritte ausführen, wird am Ende die folgende Ausgabe angezeigt:

The following command causes bash to abort, but it should also result in a nice message
ERROR: Aborting at line: 22 on command: false

Die Zeilennummer ist möglicherweise nicht immer genau, sollte Ihnen jedoch nahe genug sein, um nützlich zu sein.

haridsv
quelle