使用bash命令的输出结果(通过管道)作为另一个命令的参数

16

我正在寻找一种方法,以命令1的输出作为另一个命令2的参数。

我在尝试使用另一个命令(实际上是tty管道sed)给出的模式来grep who命令的输出时遇到了这个问题。

上下文:

如果 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行),但我仍然好奇如何使管道和命令嵌套的组合起作用...


1
你也可以使用 who am i 命令来获取当前用户信息。 - kev
如果不清楚的话——who | grep $(tty | sed 's/\/dev\///') 无法工作的原因是它在标准输入已经连接到 who 的输出之后运行了 tty,所以 tty 输出 not a tty 而不是 /dev/pts/5 - ruakh
4个回答

15

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

7

您可以使用Bash变量操作来完成此操作,而不必使用sed,尽管正如@ruakh所指出的那样,在单行版本(没有分号分隔命令)中,这种方法不起作用。 我保留了第一种方法,因为我认为它很有趣,但需要注意的是,如果您要在单行中执行,请使用分号分隔命令。

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

这首先将tty的输出放入一个变量中,然后消除了grep对它使用的前导/dev/。但是如果没有分号,当bash执行grep的变量扩展/篡改时,TTY不在环境中。

这是一个可以工作的版本,因为它生成了一个带有已修改环境(包含TTY)的子shell:

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

结果留在$WHOLINE中。

2
那样不起作用:${TTY#/dev/}被过早地扩展了。您需要将变量赋值作为单独的命令执行(使用换行符或;&&或其他方法)。 - ruakh
@ruakh:你说得对,这只是因为我之前已经定义了TTY才起作用。谢谢。 - Eduardo Ivanec
我想找到一种不使用变量的方法,但考虑到tty+pipe问题,似乎不可能。我将这个解决方案标记为已接受,但其他解决方案也非常相关。(附注:感谢bash变量处理信息) - CDuv

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 输出了 "not a tty"。这是因为它是被馈送了 who 的输出管道的一部分,所以它的标准输入确实不是 tty。这就是其他人回答能够成功的真正原因:他们将 tty 从管道中拿出来,这样它就能看到你的实际终端。顺便说一句,你提出的命令基本正确(除了管道问题),但不必要地复杂。不要使用 echo $(tty),它基本上与 tty 相同。

3
你可以像这样做:
tid=$(tty | sed 's#/dev/##') && who | grep "$tid"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接