例如命令行:
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
Answers:
没有
bash(或您的shell)将派生两个不同的命令。
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]
您不需要知道您是否正在为此工作。只需检查输出是否为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,这将使其变得更容易。
我实际上没有执行此操作的脚本,但是我已经证明了这个概念。
另一种方法可能是通过访问$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
$
当然,在通过eg 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.
命令行历史记录仅在脚本顶部没有Shebang的情况下起作用。不知道这是否可以在其他系统上可靠运行。
当状态成功时(“ / dev / pts / 3”),我无法抑制“ readlink”(或“文件”,如Archemar的建议)的输出。将输出管道输出到/ dev / null或变量会导致故障。因此,对于我而言,这不是脚本中的选项。
大师提到的TTY检查很容易,对于某些用例可能已经足够了。
编辑:我的功劳是mosvy,因为问题是如何获取完整的命令行,而不仅仅是确定脚本是否在管道上。我喜欢他的答案中的简单部分“ fc -nl -0”,因为不需要进一步的系统配置。这不是100%的解决方案,但这只是供我个人使用,因此已足够。感谢所有其他人的帮助。
[ -t 0 ]。因此,您可以检查stdin或stdout是否不是TTY,然后进行相应处理。
if [ -p /dev/stdout ]; ...(就像readlink /proc/self/fd/..在BSD上不起作用一样)。
echo -e几乎肯定不希望的-e。您需要更多的测试用例,重定向到文件,并在内部调用$(...)。但是,我敦促您考虑这是否是一个好主意。诸如ls根据其输出到tty或管道而更改其输出的程序令人讨厌使用。