众所周知,该cmd | read -r var1 var2
构造在 bash 中不起作用,因为由于管道的原因,读取命令是在子 shell 中执行的。我曾经用来read -r var1 var2 <<< "$(cmd)"
解决这个问题,但最近我了解了这个cmd_drain < <(cmd_src)
结构,它似乎也同样有效:read -r var1 var2 < <$(cmd)
。
这两种解决方案有区别吗?没有似乎在微不足道的情况下有任何区别:
$ hd < <(echo Hello)
00000000 48 65 6c 6c 6f 0a |Hello.|
00000006
$ hd <<< $(echo Hello)
00000000 48 65 6c 6c 6f 0a |Hello.|
00000006
我还尝试了一些特殊字符并得到了相同的结果。我的直觉是,结果将始终是相同的,期望cmd_drain <<< "$(cmd_src)"
首先运行cmd_src
并将整个结果缓冲在内存中,然后再将其输入到cmd_drain
,同时cmd_drain < <(cmd_src)
将不断地将输出输入cmd_src
到cmd_drain
。我认为它的行为就像cmd_src | cmd_drain
除了cmd_src
将在子 shell 中运行而不是在cmd_drain
.我的假设正确吗?
额外问题:围绕结构是否需要引用$()
?
答案1
是的,你的假设是正确的。在cmd_drain < <(cmd_src)
(又名流程替代,与正常重定向相结合),Bash 将替换<(cmd_src)
为文件的路径,从中cmd_src
可以读取 的输出。来自文档:
进程列表异步运行,其输入或输出显示为文件名。该文件名作为扩展结果作为参数传递给当前命令。如果
>(list)
使用表单,写入文件将为列表提供输入。如果<(list)
使用该形式,则应读取作为参数传递的文件以获得 list 的输出。
在 中cmd_drain <<< "$(cmd_src)"
, <<< ...
被像其他任何一样对待这里的字符串, 所以:
该单词会经历波形符扩展、参数和变量扩展、命令替换、算术扩展和引号删除。不执行路径名扩展和分词。结果作为单个字符串提供给标准输入上的命令,并附加换行符 [...]
因此,您不需要$()
在那里引用,但特别是因为此处的字符串<<<
语法不会进行分词或文件名扩展。通常,你必须这么做。
再次注意此处字符串文档的最后一句 - 附加了换行符:
bash-5.0$ od -c <<< $(printf %s foo)
0000000 f o o \n
0000004
bash-5.0$ od -c < <(printf %s foo)
0000000 f o o
0000003
这是否重要取决于您正在运行的内容。
在 中hd <<< $(echo Hello)
,命令替换删除了 的尾随换行符输出echo
,并且此处字符串添加了一个换行符,从而有效地为您提供了相同的输出。但是,如上面的示例所示,删除/添加换行符可能很棘手,并且您不需要确切地获得什么cmd_src
输出。