$BASH_COMMAND 変数は何の役に立つのでしょうか?

$BASH_COMMAND 変数は何の役に立つのでしょうか?

によるBash マニュアル環境変数BASH_COMMANDには

現在実行中またはこれから実行しようとしているコマンド。ただし、シェルがトラップの結果としてコマンドを実行している場合は、トラップの時点で実行中のコマンドです。

そのトラップコーナーケースを脇に置いておくと、私が正しく理解しているなら、これはコマンドを実行すると、変数にBASH_COMMANDそのコマンドが含まれることを意味します。その変数がコマンド実行後に設定解除されるかどうか(つまり、その間コマンドが実行されている間は、コマンドは実行されているが、その後は実行されていない)が、これは「コマンド」であるため、現在処刑されるかこれから実行される」コマンドではありませんそれはただ処刑された。

しかし、確認してみましょう:

$ set | grep BASH_COMMAND=
$ 

BASH_COMMAND='set | grep BASH_COMMAND='空です。または単に が見えるだろうと予想していましたBASH_COMMAND='set'が、空であることに驚きました。

別のことを試してみましょう:

$ echo $BASH_COMMAND
echo $BASH_COMMAND
$ 

それは理にかなっています。 コマンドを実行するecho $BASH_COMMANDと、変数にBASH_COMMAND文字列が含まれますecho $BASH_COMMAND。 今回は機能したのに、以前は機能しなかったのはなぜでしょうか?

もう一度やりましょうset:

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

待ってください。だったそのechoコマンドを実行したときに設定され、ではなかったその後設定を解除しました。しかし、set再度実行すると、BASH_COMMAND ではなかったコマンドに設定されますset。ここでコマンドを何度実行してもset、結果は同じままです。では、 を実行すると変数は設定されますがecho、 を実行すると設定されないのsetでしょうか? 見てみましょう。

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

何?変数は実行時に設定されましたecho $BASH_COMMANDが、ないを実行したときecho Hello AskUbuntu、どうなりましたか? 違いはどこにあるのでしょうか? 変数は、現在のコマンド自体が実際にシェルに変数の評価を強制したときにのみ設定されますか? 何か違うことを試してみましょう。今回は、bash の組み込みコマンドではなく、外部コマンドを試してみようと思います。

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

うーん、わかりました... もう一度言いますが、変数は設定されました。では、私の現在の推測は正しいでしょうか? 変数は評価する必要がある場合にのみ設定されますか? なぜですか?なぜ?$BASH_COMMANDパフォーマンス上の理由からでしょうか? もう一度試してみましょう。ファイル内でを grep してみます。コマンドが$BASH_COMMAND含まれているはずなので、そのコマンド (つまり、それ自体) を grep する必要があります。適切なファイルを作成しましょう。grepgrepgrep

$ 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'
$

わかりました。興味深いですね。コマンドgrep $BASH_COMMAND tmpは に展開されましたgrep grep $BASH_COMMAND tmp tmp(もちろん、変数は 1 回だけ展開されます)。そのため、 を grep しましたが、存在しないgrepファイルで 1 回、ファイル で 2 回実行されました。$BASH_COMMANDtmp

質問1:私の現在の仮定は正しいでしょうか:

  • BASH_COMMANDコマンドが実際に評価しようとしたときにのみ設定されます。
  • それはない説明ではそう思われるかもしれませんが、コマンドの実行後に設定が解除されるのでしょうか?

質問2:はいの場合、その理由は何ですか? パフォーマンスは? いいえの場合、上記のコマンド シーケンスの動作を他にどのように説明できますか?

質問3:最後に、この変数を実際に有意義に使用できるシナリオはありますか? 実際に、$PROMPT_COMMAND実行中のコマンドを分析するために (そしてそれに応じていくつかの操作を行うために) 内でこの変数を使用しようとしていましたが、できません。なぜなら、 my 内で$PROMPT_COMMAND変数 を参照するコマンドを実行するとすぐに、変数 がそのコマンドに設定されてしまうからです。 my の先頭でこれ$BASH_COMMANDを行う場合でも、割り当てもコマンドであるため、 には文字列 が含まれます。(この質問は、実行内で現在のコマンドを取得する方法に関するものではありません。他の方法があることはわかっています。)MYVARIABLE=$BASH_COMMAND$PROMPT_COMMANDMYVARIABLEMYVARIABLE=$BASH_COMMAND$PROMPT_COMMAND

これはハイゼンベルクの不確定性原理に少し似ています。変数を観察するだけで、それが変化します。

答え1

3 番目の質問に対する回答: もちろん、Bash マニュアルで明確に示唆されている方法で、トラップ内で有意義に使用できます。例:

$ 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

答え2

Q3 の回答が出たので (私の意見では正解です。BASH_COMMANDトラップには役立ちますが、他の場所ではほとんど役に立ちません)、Q1 と Q2 に挑戦してみましょう。

Q1 の答えは、仮定が正しいかどうかは判断できないということです。どちらの箇条書きも、未指定の動作について尋ねているため、真偽は確定できません。仕様により、 の値は、BASH_COMMANDそのコマンドの実行中、コマンドのテキストに設定されます。仕様では、他の状況、つまりコマンドが実行されていない場合にその値が何でなければならないかは規定されていません。任意の値を持つことも、まったく値を持たないこともあります。

Q2「いいえの場合、上記のコマンド シーケンスの動作は他にどのように説明できますか?」に対する答えは、論理的に (多少衒学的ではありますが) 次のようになりますBASH_COMMAND。 の値が未定義であるという事実によって説明されます。 の値は未定義であるため、任意の値を持つことができ、これはまさにシーケンスが示すとおりです。

追記

仕様の弱点を突いていると思う点が 1 つあります。それは、次の点です。

$PROMPT_COMMANDの先頭でMYVARIABLE=$BASH_COMMANDを実行すると、MYVARIABLEにはMYVARIABLE=$BASH_COMMANDという文字列が含まれます。なぜなら、割り当ては命令でもあるからだ

bashのマニュアルページを読む限り、イタリック体の部分は正しくありません。このセクションでは、SIMPLE COMMAND EXPANSIONまずコマンドラインの変数割り当てが保留され、次に

コマンド名が返されない場合(つまり、変数の割り当てのみがあった場合)、変数の割り当ては現在のシェル環境に影響します。

これは、他のプログラミング言語のように、変数の割り当てがコマンドではない(したがって に表示されない)ことを示唆しています。これにより、の出力に、基本的に変数の割り当ての構文上の「マーカー」でBASH_COMMANDある行がない理由も説明できます。BASH_COMMAND=setsetset

一方、そのセクションの最後の段落にはこう書かれています

展開後にコマンド名が残っている場合は、以下のように実行が続行されます。そうでない場合は、指示終了します。

...これはそうではないことを示唆しており、変数の割り当てもコマンドです。

答え3

$BASH_COMMAND の独創的な使い方

最近見つけた$BASH_COMMAND の素晴らしい使い方マクロのような機能を実装する際に。

これはエイリアスのコアトリックであり、DEBUG トラップの使用を置き換えます。前回の投稿の DEBUG トラップに関する部分を読んだ場合は、$BASH_COMMAND 変数に気付くでしょう。その投稿では、DEBUG トラップを呼び出す前にコマンドのテキストに設定されると述べました。実際は、DEBUG トラップの有無に関係なく、すべてのコマンドの実行前に設定されます (たとえば、'echo “this command = $BASH_COMMAND”' を実行して、私が言っていることを確認してください)。変数を割り当てることで (その行のみ)、コマンドの最も外側のスコープで BASH_COMMAND をキャプチャし、コマンド全体を含めます。

作家たち前の記事また、 DEBUG を使用してテクニックを実装する際の優れた背景情報も提供しますtrap。 はtrap改良バージョンでは削除されています。

答え4

トラップ...デバッグの一般的な用途は、"screen" を使用しているときにウィンドウリスト (^A") に表示されるタイトルを改善することです。

私は「画面」、「ウィンドウリスト」をより使いやすくしようとしていたので、「トラップ...デバッグ」を参照する記事を探し始めました。

PROMPT_COMMAND を使用して「null タイトル シーケンス」を送信する方法はあまりうまく機能しないことがわかったので、trap...debug 方法に戻しました。

やり方は次のとおりです:

1 「shelltitle '$|bash:'」を「$HOME/.screenrc」に入れて、「screen」にエスケープ シーケンスを探すように指示します (有効にします)。

2 デバッグ トラップのサブシェルへの伝播をオフにします。これが重要な理由は、有効になっているとすべてが台無しになるため、「set +o functrace」を使用する必要があるからです。

3 「screen」が解釈するためのタイトル エスケープ シーケンスを送信します: trap 'printf "\ek$(date +%Y%m%d%H%M%S) $(whoami)@$(hostname):$(pwd) ${BASH_COMMAND}\e\"' "DEBUG"

完璧ではありませんが、役に立ちます。この方法を使用すると、文字通り好きなものをタイトルに入れることができます。

次の「ウィンドウリスト」(5 つの画面)を参照してください。

名前フラグの数

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 $

関連情報