为什么管道对于需要 stdin 的命令不起作用?

为什么管道对于需要 stdin 的命令不起作用?

因此,我希望将输出放入unzip -Z1数组中,然后找到了这个答案;他们的第一个选择,使用mapfile输入重定向和进程替换,非常有效。

但后来我想,“等等,进程替换”,它会从 stdout 创建一个文件描述符,“然后使用输入重定向将内容放入 stdin?”
这不就等同于管道吗?

显然不是。不是的。在这里我尝试将的内容放入ls变量中,但使用管道:ls | mapfile -t test
在此处输入图片描述 没什么。

但是,如果我按照这个答案来回答:id estmapfile -t test < <(ls)
在此处输入图片描述 瞧。

为什么? diff无法分辨。至少在他们之间内容
在此处输入图片描述

我什么也没找到天生特殊关于< <(),除了创建一个文件描述符之外。以此为理论,我尝试了 HEREDOC,它也会创建一个文件描述符。它起作用了:

mapfile -t test2 << END && printf '%s ' "${test2[@]}"
this
is
an
array
END

stdin但如果通过via进行清洗则不行cat,请尝试:

cat << END | mapfile -t test3 && printf '%s ' "${test3[@]}"
this
is
an
array
END

那么,我的问题是。HEREDOC
发现/文件描述符理论不应该对它有影响,mapfile因为它的手册都说它使用“标准输入”,对吗?
如果“标准输入”在某种程度上意味着它必须是一个更花哨的描述符;为什么?为什么它不能使用简单的一次读取输入流来生成数组?
最后,如果这两个问题都得到了回答/在正确的轨道上,为什么命令在预期 fd 时没有失败?你会看到我的终端0打印了一堆(我们&&在最后的例子中使用),那是$?在我的prompt,所以内置没有报告错误。

我在 Fedora 30 和 RHEL 6 上尝试过这个,所以我不认为这是一个错误。

答案1

它确实有效;但结果随后会立即消失。

不同之处在于,这foo | mapfile是一个由两个新进程组成的管道——每个元素(无论是外部程序还是 shell 内置程序)都在子 shell 中运行。因此,尽管 mapfile 仍可执行其工作,但它无法将结果传输到父 shell 进程。

为了进行比较,使用(mapfile < foo.txt)ls | while read line; do ... done甚至都会得到相同的结果(foo=bar); echo $foo。在所有这些情况下,如果比较 $BASHPID 和 $$,您将看到不同的值 - 前者显示子 shell 进程 PID 与主 shell PID 不同。

使用重定向时,mapfile命令在主 shell 进程中运行,并且可以正确更改 shell 变量。它不太关心它是从真实文件、<<生成的临时文件还是<()生成的魔法文件进行重定向 - 在这种情况下,shell 将其作为单独的步骤处理,并且不会导致构建管道。(<()或的内部命令$() 当然是一个子壳。)


请注意,该变量实际上可以在子 shell 中使用 - 只是不能向上传输。因此对于一次性操作,管道的第二部分可能非常复杂:

unzip -l | {
    mapfile -t test
    for file in "${test[@]}"; do
        echo "I got $file!"
    done
}
zipinfo | while read -r file; do
    echo "I got $file!"
done

相关内容