Dernière commande échouée dans bash

Réponses:

6

Utilisez fcpour obtenir la ligne de commande précédente. Il est normalement utilisé pour éditer la ligne de commande précédente dans votre éditeur préféré, mais il a aussi un mode "liste":

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

janmoesen
la source
malheureusement cela ne fonctionne pas tout à fait comme je m'y attendais s'il y a de grandes déclarations de cas en cours d' utilisation ou fonctions en cours d' utilisation ... :( J'ai fini à l' aide calleret les tableaux de bash BASH_LINENO, BASH_SOURCEet FUNCNAMEde faire une sorte de trace de la pile.
phyatt
6

Si la dernière commande a été exécutée sans arguments, elle sera enregistrée dans la $_variable. Il contient normalement le dernier argument de la commande précédente - donc s'il n'y avait pas d'arguments, la valeur de $_est la dernière commande elle-même.

Une autre option consiste à connaître les détails de la dernière commande d' arrière-plan . Comme l'a écrit l0b0, $!contient son PID - vous pouvez donc analyser la sortie de ps $!(éventuellement avec des options de formatage supplémentaires pour ps).

rozcietrzewiacz
la source
2

Non, mais vous pouvez l'obtenir lors de l'exécution pour le stocker pour d'autres commandes:

  • $0: Chemin du script shell actuel.
  • $FUNCNAME: "Nom de la fonction actuelle."
  • "$@": Tous les paramètres de la commande en cours, cités séparément.
  • $!: "PID (ID de processus) du dernier travail exécuté en arrière-plan."
  • $$: "ID de processus (PID) du script lui-même."

La commande complète du script actuel devrait donc être "$0" "$@". Si c'est une fonction, ça devrait l'être "$FUNCNAME" "$@". Vous souhaiterez peut-être le stocker dans un tableau pour un traitement ultérieur. Par exemple, stockez-le dans test.sh:

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

Lors de l'exécution ./test.sh "first argument" "second argument", il devrait retourner:

./test.sh first\ argument second\ argument

Ce sont des appels équivalents.

l0b0
la source
Dans bash, il y a une BASH_COMMANDvariable, mais ne semble d'aucune façon utile, à part l'utilisation dans les pièges.
enzotib
Merci pour votre contribution. Et si j'exécute some-commandun script shell et qu'il échoue. Je vais avoir un statut différent de zéro $?, est-ce que "non" restera valable pour l'existence d'une détention variable some-command?
Eimantas
Pour autant que je sache, le seul fait qu'une commande ait échoué ne change pas l'ensemble des informations que votre shell stocke à ce sujet. Je dirais donc oui, "non" .
rozcietrzewiacz
2

Le DEBUGpiège vous permet d'exécuter une commande juste avant toute exécution de commande simple. Une version chaîne de la commande à exécuter (avec des mots séparés par des espaces) est disponible dans la BASH_COMMANDvariable.

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

echo "last command is $previous_command"

Notez que previous_commandcela changera à chaque fois que vous exécutez une commande, alors enregistrez-la dans une variable afin de l'utiliser. Si vous souhaitez également connaître l'état de retour de la commande précédente, enregistrez les deux dans une seule commande.

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

Si vous ne souhaitez interrompre qu'une commande ayant échoué, utilisez set -epour faire quitter votre script à la première commande ayant échoué. Vous pouvez afficher la dernière commande du EXITpiège .

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

Une autre approche qui pourrait fonctionner pour certaines utilisations consiste à set -ximprimer une trace de l'exécution du script et à examiner les dernières lignes de la trace.

Gilles 'SO- arrête d'être méchant'
la source
0

Je trouve qu'il est essentiel de trouver la dernière commande ayant échoué lors de l'utilisation des options set -eet set -o pipefail, car sinon bash abandonne simplement sans aucun retour sur pourquoi, c'est donc ce que j'ai trouvé fonctionnant bien:

#!/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"

Si vous exécutez ce qui précède, vous finirez par voir le type de sortie ci-dessous:

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

Le numéro de ligne n'est peut-être pas toujours précis, mais il devrait vous donner quelque chose d'assez proche pour être utile.

haridsv
la source