用 cat 进行简单的进程替换似乎挂起:
cat >( echo hello; )
永远不会结束。还尝试过:
cat >( echo hello; exit; )
从子进程手动关闭标准输出文件描述也不起作用:
cat >( echo hello; 1>&- )
cat >( echo hello; 1>&- exit; )
我尝试过 GNU bash,版本 4.4.23(1)-release,GNU bash,版本 4.1.2(2)-release 和 zsh 5.5.1 (x86_64-debian-linux-gnu)
有人可以解释为什么会发生这种情况吗?我真的很喜欢进程替换......
谢谢
编辑:如果 cat 要从 echo 中读取,则进程替换中的正确重定向是:
cat <( echo hello; )
答案1
你的代码cat >( echo hola; )
永远不会退出,因为,
- 它首先会回显该模式
hola
- 并接受输入进行写入。
- 当我们给
Ctrl+D
它时,它会继续搜索要写入的文件。
选项1:将上下文写入回显的文件中
cat > $( echo hola; )
因此,无论我们在下面给出什么上下文,都将保存到名为hola
.
注意:我们可以通过以下方式退出 cat 块Ctrl+D
选项2:捕捉回显的上下文。
cat <(echo hola;)
答案2
我不知道你想实现什么目标,但是:
>(...)
使正在运行的任何内容的标准输入都可用于写入cat
需要一个可以读取的文件
不保证 (1) 提供的任何内容都可以读取。在 macOS 上,我刚刚收到一个错误:
bash-4.4$ cat >( echo foo)
foo
cat: /dev/fd/63: Permission denied
在 Linux 上,对我来说,它是一根管道,另一端什么也没有写:
$ strace -e openat cat >(echo foo)
foo
...
openat(AT_FDCWD, "/dev/fd/63", O_RDONLY) = 3
和:
$ ll /proc/$(pgrep cat)/fd/3
lr-x------ 1 muru muru 64 Aug 13 18:55 /proc/3381/fd/3 -> 'pipe:[37501]'
$ lsof | grep 37501
strace 3433 muru 63w FIFO 0,12 0t0 37501 pipe
cat 3435 muru 3r FIFO 0,12 0t0 37501 pipe
cat 3435 muru 63w FIFO 0,12 0t0 37501 pipe
另一端关闭标准输入并退出并不重要 - 它只打开了管道阅读,没有写入,并且没有任何内容写入管道。所以cat
将留在那里,等待阅读完成。 (这里strace
和cat
这里都有一个打开的文件句柄用于写入该管道,但这是因为 bash 打开了它以进行写入并使其可供它们使用。两者都不会写入它。)
答案3
流程替代本身就是一个主题。需要认识到的重要一点是,它更像是 FIFO,而不是 stdin 或 stdout。你可以用 echo 看到这一点:
echo >( echo hello; )
给你:/dev/fd/63 你好
外部回声为您提供实际给出的参数,即/dev/fd/63
。所以,你的
cat >( echo hello; )
实际上是cat /dev/fd/43
,输出被发送到echo hello
。(43 只是一个例子;这个数字是随机的,但通常是 63)
您还可以看到以下之间的区别:
echo hop > >(cat)
这给出了hop
和
echo hop >(cat)
这使hop /dev/fd/63
。
另外,举个例子:
sed 's/$/klap/' | tee >(sed 's/^/hop/')
给出输入klop
klopklap
hopklopklap
theklopklap
是 的标准输出tee
,是 进程中替换hopklopklap
的标准输出。sed
那么为什么会cat >(echo hello)
挂起呢?因为,如前所述, cat 得到一个参数/dev/fd/32
,并且从该“文件”中它永远不会得到 EOF。
答案4
它cat
不会挂起,它正在等待您的输入。cat
读取标准输入并写入标准输出。您不重定向标准输入,因此标准输入是您的终端。当您键入 时Ctrl+D
,您表示文件结束,并且您的cat
将终止。