我试图写一个bash脚本,将获得在后台运行的命令的输出。不幸的是,我无法得到它的工作,我分配的输出的变量是空的 – 如果我用一个echo命令替换分配一切正常工作。
#!/bin/bash function test { echo "$1" } echo $(test "echo") & wait a=$(test "assignment") & wait echo $a echo done
echo done
将作业更改为
a=`echo $(test "assignment") &`
工作,但似乎应该有一个更好的方法这样做。
Bash确实有一个称为进程替换的功能来实现这一点。
$ echo <(yes) /dev/fd/63
这里,用连接到异步作业yes(以无限循环打印字符串y)的标准输出的(伪设备)文件的路径名替换表达式<(yes)。 现在让我们尝试读一下:
$ cat /dev/fd/63 cat: /dev/fd/63: No such file or directory
这里的问题是yes进程在此期间终止,因为它收到一个SIGPIPE(它没有标准输出的读者)。
解决方案是以下结构
$ exec 3< <(yes) # Save stdout of the 'yes' job as (input) fd 3.
你现在可以随时阅读背景工作。一个愚蠢的例子
$ for i in 1 2 3; do read <&3 line; echo "$line"; done y y y
注意,这与将后台作业写入驱动器支持的文件有稍微不同的语义:当缓冲区已满时,后台作业将被阻止(通过从fd读取来清空缓冲区)。相比之下,写入驱动器支持的文件只是当硬盘驱动器没有响应时阻塞。
过程替换不是POSIX sh特性。
这里有一个快速的黑客,给异步作业驱动器支持(几乎),而不指定文件名:
$ yes > backingfile & # Start job in background writing to a new file. Do also look at `mktemp(3)` and the `sh` option `set -o noclobber` $ exec 3< backingfile # open the file for reading in the current shell,as fd 3 $ rm backingfile # remove the file. It will disappear from the filesystem,but there is still a reader and a writer attached to it which both can use it. $ for i in 1 2 3; do read <&3 line; echo "$line"; done y y y
Linux最近还添加了O_TEMPFILE选项,这使得这样的黑客可能没有文件根本不可见。我不知道bash是否已经支持它。
更新:
@rthur,如果要捕获来自fd 3的整个输出,则使用
output=$(cat <&3)
但是请注意,一般来说,您不能捕获二进制数据:如果输出是POSIX意义上的文本,它只是一个定义的操作。我知道的实现只是过滤掉所有NUL字节。此外,POSIX指定必须删除所有尾部换行符。
(请注意,捕获输出将导致OOM,如果写者永远不停止(但永远不会停止)。但自然,即使是读取,如果线分隔符从来没有额外写入问题)