例えばコマンドライン:
test.sh arg1 | grep "xyz"
bashスクリプトtest.shに次のgrepを含む完全なコマンドラインを取得することは可能ですか?
[ -t 1 ] unix.stackexchange.com/a/401938/70524
例えばコマンドライン:
test.sh arg1 | grep "xyz"
bashスクリプトtest.shに次のgrepを含む完全なコマンドラインを取得することは可能ですか?
[ -t 1 ] unix.stackexchange.com/a/401938/70524
回答:
番号
bash(またはシェル)は2つの異なるコマンドをフォークします。
test.sh arg1grep "xyz"test.sh 次のgrepについて知ることができませんでした。
ただし、テストによってパイプの「内側」にいることを知っているかもしれません。 /proc/self/fd/1
test.sh
#!/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]
(編集)パイプに接続しているかどうかを知ることに関するmuruのコメントを参照してください。
あなたはそのためのパイプにいるかどうかを知る必要はありません。出力がTTYかどうかを確認してください。
[ -t 1 ]https://unix.stackexchange.com/a/401938/70524
を使用 /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を取得するため、少し簡単になります。
これを行うためのスクリプトは実際にはありませんが、コンセプトを証明しました。
もう1つの方法は$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
$
ここでは、変数をパージしません。
ご回答ありがとうございます。私はさまざまなことをテストして、次のテストスクリプトに行きました。
test.sh:
hist=`fc -nl -0`
# remove leading and trailing whitespaces
hist="$(echo "${hist}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
echo "Command line from history: '$hist'"
if [ -t 1 ]; then
echo "Direct output to TTY, no pipe involved."
else
echo "No TTY, maybe a piped command."
fi
if [ -p /dev/stdout ]; then
echo "stdout is a pipe."
else
echo "stdout is not a pipe."
fi
readlink -e /proc/self/fd/1
rst=$?
if [ $rst -eq 0 ]; then
echo "Readlink test status okay, no pipe involved."
else
echo "Readlink test status error $rst, maybe a piped command."
fi
テスト:
$ ./test.sh test1
Command line from history: './test.sh test1'
Direct output to TTY, no pipe involved.
stdout is not a pipe.
/dev/pts/3
Readlink test status okay, no pipe involved.
$ ./test.sh test2 | cat
Command line from history: './test.sh test2 | cat'
No TTY, maybe a piped command.
stdout is a pipe.
Readlink test status error 1, maybe a piped command.
$ echo "another command before pipe doesn't matter" | ./test.sh test3
Command line from history: 'echo "another command before pipe doesn't matter" | ./test.sh test3'
Direct output to TTY, no pipe involved.
stdout is not a pipe.
/dev/pts/3
Readlink test status okay, no pipe involved.
コマンドライン履歴は、スクリプトの最上行にシバンがない場合にのみ機能します。これが信頼できる他のシステムでも機能するかどうかはわかりません。
ステータスが成功した場合( "/ dev / pts / 3")、 "readlink"(またはArchemarから提案されたような "ファイル")からの出力を抑制することができませんでした。出力を/ dev / nullまたは変数にパイプすると、誤動作の原因になります。そのため、これはスクリプトのオプションにはなりません。
muruが言及したTTYチェックは簡単で、一部のユースケースではすでに十分である可能性があります。
編集:問題は、スクリプトがパイプ上にあるかどうかを判断するだけでなく、完全なコマンドラインを取得する方法でした。それ以上のシステム構成は必要ないので、私は彼の答えの単純な部分 "fc -nl -0"が好きです。これは100%のソリューションではありませんが、これは私の個人的な使用のためのものであり、したがって十分です。あなたの助けをありがとう他のすべての人。
[ -t 0 ]。したがって、stdinまたはstdoutのいずれかがTTYでないかどうかを確認し、それに応じて続行できます。
if [ -p /dev/stdout ]; ...(readlink /proc/self/fd/..これはBSDでは機能しません)。
echo -eほぼ確実に望んでいません-e。より多くのテストケースが必要$(...)です。ファイル内にリダイレクトし、内部で呼び出します。ただし、これが良いアイデアであるかどうか検討することをお勧めします。lsttyに出力するかパイプに出力するかに応じて出力を変更するようなプログラムは、使用するのが面倒です。