![$BASH_COMMAND 変数は何の役に立つのでしょうか?](https://rvso.com/image/1047576/%24BASH_COMMAND%20%E5%A4%89%E6%95%B0%E3%81%AF%E4%BD%95%E3%81%AE%E5%BD%B9%E3%81%AB%E7%AB%8B%E3%81%A4%E3%81%AE%E3%81%A7%E3%81%97%E3%82%87%E3%81%86%E3%81%8B%3F.png)
による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 する必要があります。適切なファイルを作成しましょう。grep
grep
grep
$ 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_COMMAND
tmp
質問1:私の現在の仮定は正しいでしょうか:
BASH_COMMAND
コマンドが実際に評価しようとしたときにのみ設定されます。- それはない説明ではそう思われるかもしれませんが、コマンドの実行後に設定が解除されるのでしょうか?
質問2:はいの場合、その理由は何ですか? パフォーマンスは? いいえの場合、上記のコマンド シーケンスの動作を他にどのように説明できますか?
質問3:最後に、この変数を実際に有意義に使用できるシナリオはありますか? 実際に、$PROMPT_COMMAND
実行中のコマンドを分析するために (そしてそれに応じていくつかの操作を行うために) 内でこの変数を使用しようとしていましたが、できません。なぜなら、 my 内で$PROMPT_COMMAND
変数 を参照するコマンドを実行するとすぐに、変数 がそのコマンドに設定されてしまうからです。 my の先頭でこれ$BASH_COMMAND
を行う場合でも、割り当てもコマンドであるため、 には文字列 が含まれます。(この質問は、実行内で現在のコマンドを取得する方法に関するものではありません。他の方法があることはわかっています。)MYVARIABLE=$BASH_COMMAND
$PROMPT_COMMAND
MYVARIABLE
MYVARIABLE=$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=set
set
set
一方、そのセクションの最後の段落にはこう書かれています
展開後にコマンド名が残っている場合は、以下のように実行が続行されます。そうでない場合は、指示終了します。
...これはそうではないことを示唆しており、変数の割り当てもコマンドです。
答え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 $