是否可以访问完整的命令行,包括 bash 脚本中的管道?

是否可以访问完整的命令行,包括 bash 脚本中的管道?

例如命令行:

test.sh arg1 | grep "xyz"

是否可以在 bash 脚本 test.sh 中获取包括以下 grep 的完整命令行?

答案1

bash(或你的 shell)将分叉两个不同的命令。

  1. test.sh arg1
  2. 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]

(编辑)参见穆鲁的评论关于知道你是否在管道上。

你不需要知道你是否正处于这样的困境中。只需检查输出是否为 TTY。[ -t 1 ] https://unix.stackexchange.com/a/401938/70524

答案2

没有办法做到这一点一般来说

但是交互式bashshell 可以利用历史机制和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自动变量,但它本质上是不稳定的并且很难捕获所需的值。

我认为你只能通过 an 来捕获它eval,这还涉及以特殊方式调用命令行,例如:

CMD="${BASH_COMMAND##* eval }" eval './test.sh arg1 | grep "xyz"'

Here$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
$

当然,它也可以在通过 egsh -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
$

这里没有清除变量。

相关内容