Bashスクリプトのパイプを含むコマンドライン全体にアクセスできますか?

Bashスクリプトのパイプを含むコマンドライン全体にアクセスできますか?

たとえば、コマンドラインは次のようになります。

test.sh arg1 | grep "xyz"

bashスクリプトtest.shから次のgrepを含む完全なコマンドラインを取得できますか?

答え1

いいえ

bash(またはシェル)は2つの異なるコマンドを分岐します。

  1. test.sh arg1
  2. grep "xyz"

test.shgrepに従う方法がわかりません。

ただし、テストではパイプライン「内部」にあることがわかります。/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
$

ここでは消去された変数はありません。

関連情報