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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9635705/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 21:46:00  来源:igfitidea点击:

Use output of bash command (with pipe) as a parameter for another command

bashshellpipecommand-line-argumentsnested

提问by CDuv

I'm looking for a way to use the ouput of a command (say command1) as an argument for another command (say command2).

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

I encountered this problem when trying to grepthe output of whocommand but using a pattern given by another set of command (actually ttypiped to sed).

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

Context:

语境:

If ttydisplays:

如果tty显示:

/dev/pts/5

And whodisplays:

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)

Goal:

目标:

I want only the line(s) regarding "pts/5" So I piped ttyto sedas follows:

我想只是关于“PTS / 5”所以我的管道线(S)ttysed如下:

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

Test:

测试:

The attempted following command doesn't work:

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

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

Possible solution:

可能的解决方案:

I've found out that the following works just fine:

我发现以下工作正常:

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

But I'm sure the use of evalcould be avoided.

但我确信eval可以避免使用。

As a final side node: I've noticed that the "-m" argument to whogives me exactly what I want (get only the line of whothat is linked to current user). But I'm still curious on how I could make this combination of pipes and command nesting to work...

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

采纳答案by Eduardo Ivanec

You can do this without resorting to sed with the help of Bash variable mangling, although as @ruakh points out this won't work in the single line version (without the semicolon separating the commands). I'm leaving this first approach up because I think it's interesting that it doesn't work in a single line:

您可以在Bash 变量 mangling的帮助下无需求助于 sed 就可以做到这一点,尽管正如@ruakh 指出的那样,这在单行版本中不起作用(没有分号分隔命令)。我将保留第一种方法,因为我认为有趣的是它不能在一行中工作:

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

This first puts the output of ttyinto a variable, then erases the leading /dev/on grep's use of it. But without the semicolon TTYis notin the environment by the moment bash does the variable expansion/mangling for grep.

这首先将 的输出tty放入一个变量中,然后擦除/dev/grep 使用它的前导。但是,如果没有分号TTY不是由那一刻的bash环境确实变扩建/重整grep的。

Here's a version that does work because it spawns a subshell with the already modified environment (that has TTY):

这是一个确实有效的版本,因为它生成了一个具有已修改环境(具有TTY)的子shell :

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

The result is left in $WHOLINE.

结果留在$WHOLINE.

回答by ghoti

One usually uses xargsto make the output of one command an option to another command. For example:

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

$ cat command1
#!/bin/sh

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

$ cat command2
#!/bin/sh

printf '1 = %s\n' ""

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

But ... while that was your question, it's not what you really want to know.

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

If you don't mind storing your tty in a variable, you can use bash variable mangling to do your substitution:

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

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

(You want the -w because if you're on pts/6 you shouldn't see pts/60's logins.)

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

You're limited to doing this in a variable, because if you try to put the ttycommand into a pipe, it thinks that it's not running associated with a terminal anymore.

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

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

Note that nothing in this answer so far is specific to bash. Since you're using bash, another way around this problem is to use process substitution. For example, while this does not work:

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

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

This does:

这样做:

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

回答by Gordon Davisson

@Eduardo's answer is correct (and as I was writing this, a couple of other good answers have appeared), but I'd like to explain why the original command is failing. As usual, set -xis very useful to see what's actually happening:

@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

It's not completely explicit in the above, but what's happening is that ttyis outputting "not a tty". This is because it's part of the pipeline being fed the output of who, so its stdin is indeed not a tty. This is the real reason everyone else's answers work: they get ttyout of the pipeline, so it can see your actual terminal.

上面的内容并不完全明确,但发生的事情tty是输出“不是 tty”。这是因为它是被馈送输出的管道的一部分who,所以它的标准输入确实不是一个 tty。这就是其他人的答案有效的真正原因:他们tty退出管道,因此它可以看到您的实际终端。

BTW, your proposed command is basically correct (except for the pipeline issue), but unnecessarily complex. Don't use echo $(tty), it's essentially the same as just tty.

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

回答by anubhava

You can do it like this:

你可以这样做:

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