使用 bash 命令的输出(带管道)作为另一个命令的参数

Posted

技术标签:

【中文标题】使用 bash 命令的输出(带管道)作为另一个命令的参数【英文标题】:Use output of bash command (with pipe) as a parameter for another command 【发布时间】:2012-03-27 00:01:43 【问题描述】:

我正在寻找一种将命令(比如 command1)的输出用作另一个命令(比如 command2)的参数的方法。

我在尝试grepwho 命令的输出但使用另一组命令给出的模式时遇到了这个问题(实际上是tty 通过管道传递给sed)。

上下文:

如果显示tty

/dev/pts/5

who 显示:

root     pts/4        2012-01-15 16:01 (xxxx)
root     pts/5        2012-02-25 10:02 (yyyy)
root     pts/2        2012-03-09 12:03 (zzzz)

目标:

我只想要关于“pts/5”的行 所以我将tty 传送到sed 如下:

$ tty | sed 's/\/dev\///'
pts/5

测试:

尝试的以下命令不起作用:

$ who | grep $(echo $(tty) | sed 's/\/dev\///')"

可能的解决方案:

我发现以下工作正常:

$ eval "who | grep $(echo $(tty) | sed 's/\/dev\///')"

但我确信可以避免使用eval

作为最后一个侧节点:我注意到who 的“-m”参数给了我我想要的东西(只得到链接到当前用户的who 行)。但是我仍然很好奇如何使管道和命令嵌套的这种组合发挥作用......

【问题讨论】:

您也可以使用who am i 了解您的身份。 如果不清楚 - who | grep $(tty | sed 's/\/dev\///') 不起作用的原因是它在 grep $(tty | sed 's/\/dev\///') 的标准输入已经连接到 @987654341 的输出之后运行 tty @,所以tty 输出not a tty 而不是/dev/pts/5 【参考方案1】:

通常使用xargs 使一个命令的输出成为另一个命令的选项。例如:

$ cat command1
#!/bin/sh

echo "one"
echo "two"
echo "three"

$ cat command2
#!/bin/sh

printf '1 = %s\n' "$1"

$ ./command1 | xargs -n 1 ./command2
1 = one
1 = two
1 = three
$ 

但是……虽然这是你的问题,但并不是你真正想知道的。

如果您不介意将 tty 存储在变量中,您可以使用 bash 变量修饰来进行替换:

$ tty=`tty`; who | grep -w "$tty#/dev/"
ghoti            pts/198  Mar  8 17:01 (:0.0)

(您需要 -w,因为如果您在 pts/6 上,您不应该看到 pts/60 的登录信息。)

您只能在变量中执行此操作,因为如果您尝试将 tty 命令放入管道中,它会认为它不再与终端关联运行。

$ true | echo `tty | sed 's:/dev/::'`
not a tty
$ 

请注意,到目前为止,此答案中没有任何内容是特定于 bash 的。由于您使用的是 bash,因此解决此问题的另一种方法是使用进程替换。例如,虽然这不起作用:

$ who | grep "$(tty | sed 's:/dev/::')"

这样做:

$ grep $(tty | sed 's:/dev/::') < <(who)

【讨论】:

【参考方案2】:

在Bash variable mangling 的帮助下,您可以在不借助 sed 的情况下执行此操作,尽管正如 @ruakh 指出的那样,这在单行版本中不起作用(没有分号分隔命令)。我将保留第一种方法,因为我认为有趣的是它不能在一行中工作:

TTY=$(tty); who | grep "$TTY#/dev/"

这首先将tty 的输出放入一个变量中,然后在grep 使用它时擦除前导/dev/。但是没有分号 TTYnot 在 bash 为 grep 进行变量扩展/修改的那一刻。

这是一个可以正常工作的版本,因为它使用已经修改的环境(具有TTY)生成一个子shell:

TTY=$(tty) WHOLINE=$(who | grep "$TTY#/dev/")

结果留在$WHOLINE

【讨论】:

这不起作用:"$TTY#/dev/" 过早扩展。您需要将变量赋值作为单独的命令进行(使用换行符或;&amp;&amp; 或诸如此类)。 @ruakh:你说得对,它只对我有用,因为我之前已经定义了TTY。谢谢。 我想找到一种不使用变量的方法,但考虑到 tty+pipe 问题,这似乎是不可能的。我将此解决方案标记为已接受,但其他解决方案也完全相关。 (PS:感谢 bash 变量修饰信息)【参考方案3】:

@Eduardo 的答案是正确的(在我写这篇文章时,已经出现了其他几个很好的答案),但我想解释一下为什么原来的命令会失败。像往常一样,set -x 对于查看实际情况非常有用:

$ set -x
$ who | grep $(echo $(tty) | sed 's/\/dev\///')
+ who
++ sed 's/\/dev\///'
+++ tty
++ echo not a tty
+ grep not a tty
grep: a: No such file or directory
grep: tty: No such file or directory

上面没有完全明确,但发生的事情是tty 正在输出“not a tty”。这是因为它是who 输出的管道的一部分,所以它的标准输入确实不是 tty。这是其他人的答案有效的真正原因:他们将tty 排除在管道之外,因此它可以看到您的实际终端。

顺便说一句,您提出的命令基本上是正确的(管道问题除外),但不必要地复杂。不要使用echo $(tty),它与tty基本相同。

【讨论】:

【参考方案4】:

你可以这样做:

tid=$(tty | sed 's#/dev/##') && who | grep "$tid"

【讨论】:

以上是关于使用 bash 命令的输出(带管道)作为另一个命令的参数的主要内容,如果未能解决你的问题,请参考以下文章

gsutil cp 管道在 docker 容器中作为 bash exec 命令失败

使用 Bash 按列拆分命令输出?

使用另一个命令通过管道传输时如何读取命令的返回码[重复]

数据流重定向和管道命令(bash学习02)

Linux下shell编程

sh 等效于从命名管道读取的 bash 命令