たとえば、コマンドラインは次のようになります。
test.sh arg1 | grep "xyz"
bashスクリプトtest.shから次のgrepを含む完全なコマンドラインを取得できますか?
答え1
いいえ
bash(またはシェル)は2つの異なるコマンドを分岐します。
test.sh arg1
grep "xyz"
test.sh
grepに従う方法がわかりません。
ただし、テストではパイプライン「内部」にあることがわかります。/proc/self/fd/1
テストファイル
#!/bin/bash
file /proc/self/fd/1
動作モードは次のとおりです。
> ./test.sh
/proc/self/fd/1: symbolic link to /dev/pts/0
> ./test.sh | cat
/proc/self/fd/1: broken symbolic link to pipe:[25544239]
(編集者)参照Muluのコメントあなたがパイプラインにいるかどうかを知っていることについて。
あなたがそのような問題に直面しているかどうかを知る必要はありません。出力がTTYであることを確認してください。
[ -t 1 ]
https://unix.stackexchange.com/a/401938/70524
答え2
これを行う方法はありません一般的に言えば。
ただし、インタラクティブbash
シェルは、履歴メカニズムとトラップを利用して、DEBUG
環境変数を介して実行するようにコマンドライン全体を「通知」できます。
$ trap 'export LC=$(fc -nl -0); LC=${LC#? }' DEBUG
$ sh -c 'printf "last_command={%s}\n" "$LC"' | cat; true
last_command={sh -c 'printf "last_command={%s}\n" "$LC"' | cat; true}
答え3
を使用すると、 /proc/self/fd
パイプラインに存在するかどうか、およびパイプラインのIDを確認できます。一致するパイプを繰り返すと、/proc/\*/fd
パイプの反対側の端にあるPIDを見つけることができます。 PID を使用すると、/proc/$PID/cmdline
ファイル記述子からプロセスを読み込み、繰り返してパイプターゲットを検索します。
$ cat | cat | cat &
$ ps
PID TTY TIME CMD
6942 pts/16 00:00:00 cat
6943 pts/16 00:00:00 cat
6944 pts/16 00:00:00 cat
7201 pts/16 00:00:00 ps
20925 pts/16 00:00:00 bash
$ ls -l /proc/6942/fd
lrwx------. 1 tim tim 64 Jul 24 19:59 0 -> /dev/pts/16
l-wx------. 1 tim tim 64 Jul 24 19:59 1 -> 'pipe:[49581130]'
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
$ ls -l /proc/6943/fd
lr-x------. 1 tim tim 64 Jul 24 19:59 0 -> 'pipe:[49581130]'
l-wx------. 1 tim tim 64 Jul 24 19:59 1 -> 'pipe:[49581132]'
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
$ ls -l /proc/6944/fd
lr-x------. 1 tim tim 64 Jul 24 19:59 0 -> 'pipe:[49581132]'
lrwx------. 1 tim tim 64 Jul 24 19:59 1 -> /dev/pts/16
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
また、運が良ければ、パイプラインのさまざまなコマンドが連続したPIDを取得するので、作業が少し簡単になります。
実際にこれを行うスクリプトはありませんが、概念を実証しました。
答え4
別のアプローチは、自動変数にアクセスすることであり得るが、$BASH_COMMAND
これは本質的に不安定であり、所望の値を捕捉することが困難である。
eval
私の考えでは、次のような特別な方法でコマンドラインを呼び出すだけでこれをキャッチできると思います。
CMD="${BASH_COMMAND##* eval }" eval './test.sh arg1 | grep "xyz"'
ここでは$BASH_COMMAND
拡張され、eval
文字列のビットもクリアされるため、結果の文字列は補助変数$CMD
に「スナップショット」されます。
小さな例:
$ cat test.sh
#!/bin/sh
printf 'you are running %s\n' "$CMD"
sleep 1
echo bye bye
$
$ CMD="${BASH_COMMAND##* eval }" eval './test.sh | { grep -nH "."; }'
(standard input):1:you are running './test.sh | { grep -nH "."; }'
(standard input):2:bye bye
$
もちろん、sh -c
次の方法でスクリプトを呼び出すときにも機能します(実際にはより良い)bash -c
。
$
$ CMD="${BASH_COMMAND}" sh -c './test.sh | { grep -nH "."; }'
(standard input):1:you are running CMD="${BASH_COMMAND}" sh -c './test.sh | { grep -nH "."; }'
(standard input):2:bye bye
$
ここでは消去された変数はありません。