使用进程替换设置变量的bash命令

4
我正在运行一个写入文件的命令,但我希望能够有效地将其写入一个变量中。类似以下的方式:
echo test | tee >(var=$(cat)) >/dev/null && echo "var = $var"

我希望输出var = test,但由于进程替换中的变量赋值在单独的进程中运行,所以外部范围中未设置var变量。

有哪些选项可供选择?

请记住,我的命令(例如示例中的tee)会将内容输出到标准输出,我想丢弃该内容。

我不想创建临时文件。


echo test | tee >(var=$(cat) ; printf 'var = %s\n' "$var") - Jetchisel
echo test | tee >(var=$(cat) ; printf 'var = %s\n' "$var") - Jetchisel
echo test | tee >(var=$(cat) ; printf 'var = %s\n' "$var") - undefined
不确定在子shell之外如何实现它,但是嘿,生活充满了惊喜! - Jetchisel
不确定在子shell之外如何实现,但是嘿,生活充满了惊喜! - Jetchisel
不确定在子shell之外如何实现,但是嘿,生活充满了惊喜! - undefined
3个回答

4

如果您的操作系统支持 /dev/fd/,请尝试以下方法:

var=$(echo "test" | tee /dev/fd/3 3>&1 >/dev/null)

解释:`3>&1`使得shell将文件描述符#3打开为`tee`程序的标准输出的副本(被`$( )`捕获);然后`>/dev/null`将该标准输出替换为指向`/dev/null`的输出(丢弃它)。有了这些重定向,`tee /dev/fd/3`将其输出发送到文件描述符#3(仍然会被`$()`捕获)。

2
var=$( echo test | tee >(cat) >/dev/null ) && echo "var = $var"

?


解释会很有帮助。 - dawg
令人惊讶的是,这似乎起作用了。解释一下会很有帮助。为什么cat命令的输出没有被重定向到/dev/null? - DBear
1
@DBear 约翰·波林格似乎在我回来之前已经解释过了。像他一样,我也不确定你在问什么,但我们做出了相同的假设。戈登·戴维森编辑的答案基本上与我们的相同,但可能更高效,因为不需要中间的 cat 进程。然而,它依赖于选择一个命令尚未使用的文件描述符。 - jhnc
1
@DBear 约翰·波林格似乎在我回来之前已经做了解释。像他一样,我也不确定你在问什么,但我们做出了相同的假设。戈登·戴维森编辑的答案基本上与我们的相同,但可能更高效,因为不需要中间的 cat 进程。然而,它依赖于选择一个命令尚未使用的文件描述符。 - jhnc
1
@DBear 约翰·波林格似乎在我回来之前已经做了解释。像他一样,我不确定你在问什么,但我们做出了相同的假设。戈登·戴维森编辑的答案基本上与我们的相同,但可能更高效,因为不需要中间的 cat 进程。然而,它依赖于选择一个命令尚未使用的文件描述符。 - undefined
显示剩余4条评论

2

从...开始

请记住,我的命令(例如示例中的tee)会将输出内容发送到标准输出,而我希望将其丢弃。

... 我认为你告诉我们,在你最感兴趣的情况下,示例命令中的tee是某个其他程序的替代品。

我还注意到你说你有一个写入文件的命令。虽然tee确实是这样一个命令,但你提供的命令行中存在一个拼写错误。在tee命令行上给出的文件名被用作输出文件,而<(var=$(cat))则是用于读取的。我猜你的意思更像是这样的示例:

echo test | my_program >(var=$(cat)) >/dev/null && echo "var = $var"

尽管如此,当然还是不起作用。

底线是:你想将命令的输出捕获到一个文件中,而不是标准输出,而且不引入临时文件。

看起来你正在使用大多数正确的工具来完成这个任务,但是没有将它们以正确的方式组合起来。如果你想避免使用临时文件,那么我认为没有其他办法,只能在顶层执行命令替换:

var=$(...) && echo "var = ${var}"

原来这个>(cat)是一个伪文件,它会将所有写入它的内容发送到cat的标准输出。将所有这些组合在一起,你就得到了这个:
var=$(echo test | my_program >(cat) >/dev/null) && echo "var = ${var}"

请注意,在该命令中,my_program的标准输出重定向与cat无关。 cat命令继承自由命令替换引入的子shell环境的标准输出,这正是命令替换所捕获的内容。
还要注意,管道必须位于命令替换内部,以使变量赋值处于“顶级”位置,如上所述。也就是说,您不能这样做...
# DOES NOT WORK
echo test | { var=$(my_program >(cat) >/dev/null); } && echo "var = ${var}"

因为管道为每个组件命令引入了一个子shell环境,就像命令替换对其命令所做的那样。

非常感谢您详细的回答。在此之前,我并不理解带有输出重定向的进程替换的语义。事实上,我在原始问题中有一个拼写错误。我已经纠正了它。 - DBear
非常感谢您详细的回答。在此之前,我并不理解使用输出重定向的进程替换的语义。事实上,我在原始问题中有一个拼写错误。我已经纠正了它。 - DBear
非常感谢您详细的回答。在此之前,我并不理解使用输出重定向的进程替换的语义。事实上,我在原始问题中有一个拼写错误。我已经纠正了它。 - undefined
我不建议这样做,因为代码很混乱(将其管道传递给shell赋值?!)且容易出错(通常会失败,正如你所说),但是使用set +mshopt -s lastpipe,你的“不起作用”的例子实际上会起作用。 - jhnc
我不建议这样做,因为代码很混乱(将其管道化到shell赋值?!)而且容易出错(通常会失败,正如你所述),但是使用set +mshopt -s lastpipe,你的“DOES NOT WORK”示例实际上会起作用。 - jhnc
我不建议这样做,因为代码很混乱(将其管道传递到shell赋值?!)并且容易出错(通常会失败,正如您所述)。但是,使用set +mshopt -s lastpipe,您的“DOES NOT WORK”示例实际上会起作用。 - undefined

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