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

时间:2012-03-09 14:30:32

标签: bash shell pipe command-line-arguments nested

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

我在尝试grep who命令的输出时遇到了这个问题,但是使用了另一组命令(实际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的行当前用户)。但是我仍然很好奇如何将管道和命令嵌套组合起来......

4 个答案:

答案 0 :(得分:10)

通常使用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)

答案 1 :(得分:6)

@ 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正在输出“不是tty”。这是因为它是管道的一部分被输入who的输出,所以它的标准输入确实不是tty。这是其他人的答案工作的真正原因:他们从管道中获取tty,因此它可以看到您的实际终端。

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

答案 2 :(得分:5)

你可以在Bash variable mangling的帮助下不使用sed来做到这一点,尽管@ruakh指出这在单行版本中不起作用(没有用分号分隔命令)。我要离开这第一个方法了,因为我觉得有趣的是它不能在一行中起作用:

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

首先将tty的输出放入变量中,然后在grep使用它时删除前导/dev/。但是,在bash为grep执行变量扩展/修改的时候,如果没有分号TTY在环境中

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

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

结果保留在$WHOLINE

答案 3 :(得分:2)

你可以这样做:

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