web-dev-qa-db-fra.com

A quoi sert la variable $ BASH_COMMAND?

Selon le manuel de Bash , la variable d'environnement BASH_COMMAND contient

La commande en cours d'exécution ou sur le point de l'être, à moins que le shell n'exécute une commande à la suite d'une interruption, auquel cas il s'agit de la commande en cours d'exécution au moment de l'interruption.

Si je comprends bien, si je comprends bien, cela signifie que lorsque j'exécute une commande, la variable BASH_COMMAND contient cette commande. Il n'est pas absolument clair si cette variable est désactivée après l'exécution de la commande (c'est-à-dire qu'elle n'est disponible que pendant que la commande est en cours d'exécution, mais pas après), bien que l'on puisse dire que "la commande en cours d'exécution ou sur le point d'être exécutée", ce n'est pas la commande qui était juste exécuté.

Mais vérifions:

$ set | grep BASH_COMMAND=
$ 

Vide. Je me serais attendu à voir BASH_COMMAND='set | grep BASH_COMMAND=' ou peut-être juste BASH_COMMAND='set', mais vide m'a surpris.

Essayons autre chose:

$ echo $BASH_COMMAND
echo $BASH_COMMAND
$ 

Cela a du sens. J'exécute la commande echo $BASH_COMMAND et la variable BASH_COMMAND contient donc la chaîne echo $BASH_COMMAND. Pourquoi cela a-t-il fonctionné cette fois, mais pas avant?

Faisons à nouveau la chose setname__:

$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

Alors attend. Il était défini lorsque j'ai exécuté cette commande echoname__, et il n'était pas non défini par la suite. Mais lorsque j'ai exécuté à nouveau setname__, BASH_COMMAND n'était pas défini sur la commande setname__. Quelle que soit la fréquence d'exécution de la commande setici, le résultat reste le même. Ainsi, la variable est-elle définie lors de l'exécution de echoname__, mais pas lors de l'exécution de setname__? Voyons voir.

$ echo Hello AskUbuntu
Hello AskUbuntu
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

Quoi? La variable a donc été définie lors de l'exécution de echo $BASH_COMMAND, mais et non lors de l'exécution de echo Hello AskUbuntu? Où est la différence maintenant? La variable est-elle définie uniquement lorsque la commande actuelle elle-même oblige le shell à évaluer la variable? Essayons quelque chose de différent. Peut-être une commande externe cette fois, pas une bash intégrée, pour changer.

$ /bin/echo $BASH_COMMAND
/bin/echo $BASH_COMMAND
$ set | grep BASH_COMMAND=
BASH_COMMAND='/bin/echo $BASH_COMMAND'
$

Hmm, ok ... encore une fois, la variable a été définie. Alors, ma proposition actuelle est-elle correcte? La variable est-elle définie uniquement lorsqu'elle doit être évaluée? Pourquoi? Pourquoi? Pour des raisons de performances? Faisons encore un essai. Nous allons essayer de grep pour $BASH_COMMAND dans un fichier et puisque $BASH_COMMAND devrait alors contenir une commande grepname__, grepdevrait grep pour cette commande grep(i.e., pour lui-même). alors faisons un fichier approprié:

$ echo -e "1 foo\n2 grep\n3 bar\n4 grep \$BASH_COMMAND tmp" > tmp
$ grep $BASH_COMMAND tmp
grep: $BASH_COMMAND: No such file or directory
tmp:2 grep                                      <-- here, the Word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the Word "grep" is RED
tmp:2 grep                                      <-- here, the Word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the Word "grep" is RED
$ set | grep BASH_COMMAND=
BASH_COMMAND='grep --color=auto $BASH_COMMAND tmp'
$

Ok, intéressant. La commande grep $BASH_COMMAND tmp a été étendue à grep grep $BASH_COMMAND tmp tmp (la variable est développée une seule fois, bien sûr), et j'ai donc attrapé pour grepname__, une fois dans un fichier $BASH_COMMAND qui n'existe pas et deux fois dans le fichier tmpname__.

Q1: Mon hypothèse actuelle est-elle correcte?

  • BASH_COMMAND n'est défini que lorsqu'une commande tente de l'évaluer réellement; et
  • il est non non défini après l'exécution d'une commande, même si la description peut nous amener à le croire?

Q2: Si oui, pourquoi? Performance? Si non, comment expliquer autrement le comportement dans la séquence de commandes ci-dessus?

Q3: Enfin, existe-t-il un scénario dans lequel cette variable pourrait réellement être utilisée de manière significative? J'essayais en fait de l'utiliser avec $Prompt_COMMAND pour analyser la commande en cours d'exécution (et de faire des choses en fonction de cela), mais je ne peux pas, car dès que, dans mon $Prompt_COMMAND, j'exécute une commande pour examiner la variable $BASH_COMMAND , la variable obtient des ensembles à cette commande. Même lorsque je fais MYVARIABLE=$BASH_COMMAND au début de mon $Prompt_COMMAND, alors MYVARIABLEcontient la chaîne MYVARIABLE=$BASH_COMMAND, car une affectation est aussi une commande. (Cette question ne concerne pas comment je pourrais obtenir la commande en cours dans une exécution de $Prompt_COMMAND. Il existe d'autres moyens, je le sais.)

C'est un peu comme avec le principe d'incertitude de Heisenberg. Juste en observant la variable, je la change.

25
Malte Skoruppa

Répondant à la troisième question: bien sûr, il peut être utilisé de manière significative comme le manuel de Bash indique clairement - dans un piège, e. g.:

$ trap 'echo ‘$BASH_COMMAND’ failed with error code $?' ERR
$ fgfdjsa
fgfdjsa: command not found
‘fgfdjsa’ failed with error code 127
$ cat /etc/fgfdjsa
cat: /etc/fgfdjsa: No such file or directory
‘cat /etc/fgfdjsa’ failed with error code 1
15
Dmitry Alexandrov

Maintenant que Q3 a été répondu (correctement, à mon avis: BASH_COMMAND est utile dans les pièges et presque nulle part ailleurs), passons à Q1 et Q2.

La réponse à la question 1 est la suivante: l’exactitude de votre hypothèse est indécidable. La vérité d'aucun des points de balle ne peut être établie, car ils posent des questions sur un comportement non spécifié. Par sa spécification, la valeur de BASH_COMMAND est définie sur le texte d'une commande pendant la durée de l'exécution de cette commande. La spécification n'indique pas quelle doit être sa valeur dans une autre situation, c'est-à-dire lorsqu'aucune commande n'est en cours d'exécution. Il pourrait avoir n'importe quelle valeur ou pas du tout.

La réponse à la question 2 "Si non, comment expliquer le comportement dans la séquence de commande ci-dessus?" suit ensuite logiquement (quelque peu de façon pédante): cela s'explique par le fait que la valeur de BASH_COMMAND est indéfinie. Comme sa valeur est indéfinie, il peut avoir n'importe quelle valeur, ce qui est exactement ce que montre la séquence.

Postscript

Il y a un point où je pense que vous frappez vraiment un point faible dans la spécification. C'est là que vous dites:

Même lorsque je fais MYVARIABLE = $ BASH_COMMAND au début de mon $ Prompt_COMMAND, MYVARIABLE contient la chaîne MYVARIABLE = $ BASH_COMMAND, , car une affectation est également une commande .

La façon dont j'ai lu la page de manuel bash, le passage en italique n’est pas vrai. La section SIMPLE COMMAND EXPANSION explique comment les affectations de variables sur la ligne de commande sont d'abord réservées, puis

Si aucun nom de commande n'est obtenu (en d'autres termes, il n'y a eu que des affectations de variables), les affectations de variables affectent l'environnement Shell actuel.

Cela me suggère que les affectations de variables ne sont pas des commandes (et donc exemptes d'apparaître dans BASH_COMMAND), comme dans d'autres langages de programmation. Cela expliquerait également pourquoi il n'y a pas de ligne BASH_COMMAND=set dans la sortie de set, set étant essentiellement un "marqueur" syntaxique pour l'affectation de variable.

OTOH, dans le dernier paragraphe de cette section, il est écrit

S'il reste un nom de commande après le développement, l'exécution se poursuit comme décrit ci-dessous. Sinon, la commande quitte.

... ce qui suggère le contraire, et les affectations de variables sont aussi des commandes.

6
zwets

Utilisation inventive pour $ BASH_COMMAND

Récemment trouvé ceci tilisation impressionnante de $ BASH_COMMAND en implémentant une fonctionnalité de type macro.

C'est le truc principal de l'alias qui remplace l'utilisation du piège DEBUG. Si vous avez lu la partie du message précédent relative à l’interruption DEBUG, vous reconnaîtrez la variable $ BASH_COMMAND. Dans cet article, j'ai indiqué que le texte de la commande était défini avant chaque appel à l'interruption DEBUG. Eh bien, il s'avère que cela est défini avant l'exécution de chaque commande, DEBUG ou non (par exemple, exécutez ‘echo“ cette commande = $ BASH_COMMAND ”‘ pour voir de quoi je parle). En lui affectant une variable (uniquement pour cette ligne), nous capturons BASH_COMMAND à la portée la plus externe de la commande, qui contiendra la commande entière.

L'auteur article précédent fournit également un bon arrière-plan lors de l'implémentation d'une technique utilisant un DEBUG trap. trap est éliminé dans la version améliorée.

2
DocSalvager

Une utilisation apparemment courante du piège ... Le débogage consiste à améliorer les titres qui apparaissent dans la liste de fenêtres (^ A ") lorsque vous utilisez" screen ".

J'essayais de rendre "screen", "windowlist" plus utilisable, alors j'ai commencé à trouver des articles faisant référence à "trap ... debug".

J'ai trouvé que la méthode utilisant Prompt_COMMAND pour envoyer la "séquence de titre null" ne fonctionnait pas aussi bien, alors je suis revenu à la méthode trap ... debug.

Voici comment vous le faites:

1 Indiquez à "screen" de rechercher la séquence d'échappement (activez-la) en plaçant "shelltitle '$ | bash:'" dans "$ HOME/.screenrc".

2 Désactivez la propagation du piège de débogage dans les sous-coques. Ceci est important, car s'il est activé, tout se gâche, utilisez: "set + o functrace".

3 Envoyez la séquence d'échappement du titre pour "screen" à interpréter: trap 'printf "\ ek $ (date +% A% m% d% H% M% S) $ (whoami) @ $ (nomhôte): $ (pwd) $ {BASH_COMMAND}\e\"'" DEBUG "

Ce n'est pas parfait, mais ça aide, et vous pouvez utiliser cette méthode pour mettre littéralement tout ce que vous voulez dans le titre.

Voir la "windowlist" suivante (5 écrans):

Drapeaux Nom Num

1 bash: 20161115232035 mcb @ ken007:/home/mcb/ken007 RSYNCCMD = "Sudo rsync" myrsync.sh -r -r "$ {1}" "$ {BACKUPDIR} $ 2" + "/ $ {2}" } "$ 2 bash: 20161115230434 mcb @ ken007:/home/mcb/ken007 ls --color = auto -la 3 $ bash: 20161115230504 mcb @ ken007:/home/mcb/ken007 cat bin/psg.sh 4 bash: 20161115222415 mcb @ ken007:/home/mcb/ken007 ssh ken009 $ 5 bash: 20161115222450 mcb @ ken007:/home/mcb/ken007 mycommoncleanup $

0
Malcolm Boekhoff