使用 bash 命令的输出(带管道)作为另一个命令的参数
Posted
技术标签:
【中文标题】使用 bash 命令的输出(带管道)作为另一个命令的参数【英文标题】:Use output of bash command (with pipe) as a parameter for another command 【发布时间】:2012-03-27 00:01:43 【问题描述】:我正在寻找一种将命令(比如 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
行)。但是我仍然很好奇如何使管道和命令嵌套的这种组合发挥作用......
【问题讨论】:
您也可以使用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/
。但是没有分号 TTY
是 not 在 bash 为 grep 进行变量扩展/修改的那一刻。
这是一个可以正常工作的版本,因为它使用已经修改的环境(具有TTY
)生成一个子shell:
TTY=$(tty) WHOLINE=$(who | grep "$TTY#/dev/")
结果留在$WHOLINE
。
【讨论】:
这不起作用:"$TTY#/dev/"
过早扩展。您需要将变量赋值作为单独的命令进行(使用换行符或;
或&&
或诸如此类)。
@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 命令的输出(带管道)作为另一个命令的参数的主要内容,如果未能解决你的问题,请参考以下文章