我们都知道mkfifo
管道。第一个管道创建了一个命名管道,因此必须选择一个名称,最有可能的是使用mktemp
,然后记得取消链接。另一个创建一个匿名管道,没有名称和删除的麻烦,但管道的末端与管道中的命令绑定在一起,以某种方式掌握文件描述符并在脚本的其余部分使用它们并不方便。在编译程序中,我只会执行ret=pipe(filedes)
;在 Bash 中有exec 5<>file
,所以人们会期望类似"exec 5<> -"
或"pipe <5 >6"
-Bash 中有类似的东西吗?
答案1
您可以在将命名管道附加到当前进程之后立即取消链接,这实际上会产生匿名管道:
# create a temporary named pipe
PIPE=$(mktemp -u)
mkfifo $PIPE
# attach it to file descriptor 3
exec 3<>$PIPE
# unlink the named pipe
rm $PIPE
...
# anything we write to fd 3 can be read back from it
echo 'Hello world!' >&3
head -n1 <&3
...
# close the file descriptor when we are finished (optional)
exec 3>&-
如果您确实想避免使用命名管道(例如文件系统是只读的),那么“掌握文件描述符”的想法也行得通。请注意,由于使用了 procfs,因此这是 Linux 特有的。
# start a background pipeline with two processes running forever
tail -f /dev/null | tail -f /dev/null &
# save the process ids
PID2=$!
PID1=$(jobs -p %+)
# hijack the pipe's file descriptors using procfs
exec 3>/proc/$PID1/fd/1 4</proc/$PID2/fd/0
# kill the background processes we no longer need
# (using disown suppresses the 'Terminated' message)
disown $PID2
kill $PID1 $PID2
...
# anything we write to fd 3 can be read back from fd 4
echo 'Hello world!' >&3
head -n1 <&4
...
# close the file descriptors when we are finished (optional)
exec 3>&- 4<&-
答案2
尽管我所知道的所有 shell 都不能在不分叉的情况下创建管道,但是有些 shell 确实比基本的 shell 管道更好。
在 bash、ksh 和 zsh 中,假设您的系统支持/dev/fd
(现在大多数系统都支持),您可以将命令的输入或输出绑定到文件名:<(command)
扩展为指定连接到 输出的管道的文件名command
,并>(command)
扩展为指定连接到 的输入的管道的文件名command
。此功能称为流程替代其主要目的是将多个命令通过管道传输到另一个命令中,例如,
diff <(transform <file1) <(transform <file2)
tee >(transform1 >out1) >(transform2 >out2)
这对于克服基本 shell 管道的一些缺点也很有用。例如,command2 < <(command1)
相当于command1 | command2
,只是它的状态是command2
。另一个用例是exec > >(postprocessing)
,它相当于将整个脚本的其余部分放在 中,但比 更易读{ ... } | postprocessing
。
答案3
Bash 4 具有共同进程。
协同进程在子 shell 中异步执行,就好像命令以 '&' 控制运算符终止一样,在执行 shell 和协同进程之间建立了双向管道。
协同处理的格式为:
coproc [NAME] command [redirections]
答案4
截至 2012 年 10 月,Bash 中似乎仍未提供此功能,但如果您只需要未命名/匿名管道来与子进程通信,则可以使用 coproc。目前 coproc 的问题在于,似乎一次只支持一个。我不明白 coproc 为何会有此限制。它们应该是现有任务后台代码(& op)的增强功能,但这是 bash 作者的问题。