Selon le manuel de Bash , la variable d'environnement BASH_COMMAND
contient
La commande en cours d'exécution ou sur le point d'être exécutée, sauf si le shell 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.
En prenant ce cas de coin de piège de côté, 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 n'est pas définie après l'exécution de la commande (c'est-à-dire qu'elle n'est disponible que lorsque la commande est en cours d'exécution, mais pas après), bien que l'on puisse dire que puisque c'est "la commande en cours d'exécution ou sur le point d'être exécutée" , ce n'est pas la commande qui vient d'être exécutée.
Mais vérifions:
$ set | grep BASH_COMMAND=
$
Vide. Je m'attendais à voir BASH_COMMAND='set | grep BASH_COMMAND='
ou peut-être juste BASH_COMMAND='set'
, mais le vide m'a surpris.
Essayons autre chose:
$ echo $BASH_COMMAND
echo $BASH_COMMAND
$
Eh bien, cela a du sens. J'exécute la commande echo $BASH_COMMAND
et donc la variable BASH_COMMAND
contient la chaîne echo $BASH_COMMAND
. Pourquoi cela a-t-il fonctionné cette fois, mais pas avant?
Faisons à set
nouveau la chose:
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$
Alors attend. Il a été défini lorsque j'ai exécuté cette echo
commande, et il n'a pas été annulé par la suite. Mais lorsque j'ai exécuté à set
nouveau, la commande BASH_COMMAND
n'a pas été définie set
. Quelle que soit la fréquence d'exécution de la set
commande ici, le résultat reste le même. Donc, la variable est-elle définie lors de l'exécution echo
, mais pas lors de l'exécution set
? Voyons voir.
$ echo Hello AskUbuntu
Hello AskUbuntu
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$
Quelle? La variable a donc été définie lors de mon exécution echo $BASH_COMMAND
, mais pas lors de mon exécution echo Hello AskUbuntu
? Où est la différence maintenant? La variable n'est-elle définie que lorsque la commande actuelle elle-même force réellement le shell à évaluer la variable? Essayons quelque chose de différent. Peut-être une commande externe cette fois, pas une commande bash intégrée, pour un changement.
$ /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 supposition actuelle est-elle correcte? La variable n'est-elle définie que 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 grep
commande, grep
devrait grep pour cette grep
commande (c'est-à-dire pour lui-même). faisons donc 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 a grep $BASH_COMMAND tmp
été étendue à grep grep $BASH_COMMAND tmp tmp
(la variable n'est développée qu'une seule fois, bien sûr), et j'ai donc recherché grep
une fois dans un fichier $BASH_COMMAND
qui n'existe pas et deux fois dans le fichier tmp
.
Q1: Mon hypothèse actuelle est-elle correcte:
BASH_COMMAND
n'est définie que lorsqu'une commande essaie de l'évaluer; et- il n'est pas instable après exécution d'une commande, même si la description peut nous le faire 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 à l'intérieur $PROMPT_COMMAND
pour analyser la commande en cours d'exécution (et 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 regarder la variable $BASH_COMMAND
, la variable obtient des ensembles à cette commande. Même lorsque je fais MYVARIABLE=$BASH_COMMAND
juste au début de mon $PROMPT_COMMAND
, MYVARIABLE
contient alors la chaîne MYVARIABLE=$BASH_COMMAND
, car une affectation est aussi une commande. (Cette question ne porte pas sur la façon dont je pourrais obtenir la commande actuelle dans une $PROMPT_COMMAND
exécution. Il existe d'autres moyens, je sais.)
C'est un peu comme avec le principe d'incertitude de Heisenberg. Juste en observant la variable, je la change.
la source
bash
über-gourous là-bas.Réponses:
Répondre à la troisième question: bien sûr, il peut être utilisé de manière significative dans la façon dont le manuel Bash laisse clairement entendre - dans un piège, par exemple:
la source
$BASH_COMMAND
peut en effet être utilisé de manière significative même dans des contextes autres qu'un piège. Pourtant, l'utilisation que vous décrivez est probablement la plus typique et la plus simple. Donc, votre réponse, et celle de DocSalvager, répond le mieux à mon Q3 , et c'était la plus importante pour moi. En ce qui concerne Q1 et Q2, comme indiqué par @zwets, ces choses ne sont pas définies. Il se peut qu'on$BASH_COMMAND
ne donne une valeur que si vous y accédez, pour des performances - ou bien des conditions de programme internes étranges peuvent conduire à ce comportement. Qui sait?$BASH_COMMAND
à être toujours défini, en forçant l'évaluation d'$BASH_COMMAND
avant chaque commande. Pour ce faire, nous pouvons utiliser le piège DEBUG, qui est exécuté avant chaque commande unique (tous em). Si nous émettons, par exemple,trap 'echo "$BASH_COMMAND" > /dev/null' DEBUG
alors$BASH_COMMAND
sera toujours évalué avant chaque commande (et assigné la valeur de cela$COMMAND
). Donc, si j'exécuteset | grep BASH_COMMAND=
alors que ce piège est actif ... que savez-vous? Maintenant, la sortie estBASH_COMMAND=set
, comme prévu :)Maintenant que Q3 a été répondu (correctement, à mon avis:
BASH_COMMAND
est utile dans les pièges et presque nulle part ailleurs), essayons Q1 et Q2.La réponse à Q1 est: l'exactitude de votre hypothèse est indécidable. La vérité de aucun des points 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 sa valeur dans toute autre situation, c'est-à-dire lorsqu'aucune commande n'est en cours d'exécution. Il pourrait avoir n'importe quelle valeur ou aucune.La réponse à Q2 "Si non, comment expliquer autrement le comportement dans la séquence de commandes ci-dessus?" suit alors logiquement (si quelque peu pédantiquement): il s'explique par le fait que la valeur de
BASH_COMMAND
n'est pas définie. Comme sa valeur n'est pas définie, elle peut avoir n'importe quelle valeur, ce qui est exactement ce que la séquence montre.Postscript
Il y a un point où je pense que vous avez effectivement un faible dans la spécification. C'est là que vous dites:
La façon dont je lis la page de manuel bash, le bit 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 mises de côté, puisCela me suggère que les affectations de variables ne sont pas des commandes (et donc exemptées de s'afficher dans
BASH_COMMAND
), comme dans d'autres langages de programmation. Cela expliquerait également pourquoi il n'y a pas de ligneBASH_COMMAND=set
dans la sortie deset
,set
étant essentiellement un «marqueur» syntaxique pour l'affectation des variables.OTOH, dans le dernier paragraphe de cette section, il est dit
... ce qui suggère le contraire, et les affectations de variables sont également des commandes.
la source
set
je devrais voirBASH_COMMAND='set'
quelque part dans cette sortie, quiset
commande . Par conséquent, selon les spécifications, cela devrait fonctionner. Est-ce donc que l'implémentation n'obéit pas fidèlement aux spécifications, ou est-ce que je comprends mal les spécifications? Trouver la réponse à cette question est en quelque sorte l'objectif du premier trimestre. +1 pour votre postscript :)set
.set
ne soit pas une "commande". Tout comme ce=
n'est pas une "commande". Le traitement du texte suivant / entourant ces jetons pourrait avoir une logique entièrement différente du traitement d'une "commande".set
cela ne compte pas comme une "commande". Je n'aurais pas pensé que$BASH_COMMAND
cela se révélerait si peu spécifié dans de nombreuses circonstances. Je suppose que nous avons au moins établi que la page de manuel pourrait être plus claire à ce sujet;)Une utilisation inventive de $ BASH_COMMAND
Récemment trouvé cette utilisation impressionnante de $ BASH_COMMAND dans l'implémentation d'une fonctionnalité de type macro.
L' article précédent de l'auteur fournit également un bon contexte lors de la mise en œuvre d'une technique utilisant un DEBUG
trap
. Letrap
est éliminé dans la version améliorée.la source
$BASH_COMMAND
peut être utilisée de manière significative dans un contexte autre que atrap
. Et quelle utilité! L'utiliser pour implémenter des macro-commandes dans bash - qui l'aurait cru possible? Merci pour le pointeur.Une utilisation apparemment courante du piège ... le débogage consiste à améliorer les titres qui apparaissent dans la liste des fenêtres (^ A ") lorsque vous utilisez" screen ".
J'essayais de rendre "l'écran", "la liste des fenêtres" 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 titres nulle" ne fonctionnait pas si bien, donc je suis revenu à la méthode trap ... debug.
Voici comment procéder:
1 Dites à "screen" de rechercher la séquence d'échappement (activez-la) en mettant "shelltitle '$ | bash:'" dans "$ HOME / .screenrc".
2 Désactivez la propagation du piège de débogage en sous-shells. Ceci est important, car s'il est activé, tout est bloqué, utilisez: "set + o functrace".
3 Envoyez la séquence d'échappement du titre pour "screen" à interpréter: trap 'printf "\ ek $ (date +% Y% m% d% H% M% S) $ (whoami) @ $ (hostname): $ (pwd) $ {BASH_COMMAND} \ e \ "'" DEBUG "
Ce n'est pas parfait, mais cela aide, et vous pouvez utiliser cette méthode pour mettre littéralement tout ce que vous aimez dans le titre
Voir la "liste des fenêtres" suivante (5 écrans):
Indicateurs de 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 poubelle / psg.sh 4 $ bash: 20161115222415 mcb @ ken007: / home / mcb / ken007 ssh ken009 5 $ bash: 20161115222450 mcb @ ken007: / home / mcb / ken007 mycommoncleanup $
la source