我试图写一个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,如果写者永远不停止(但永远不会停止)。但自然,即使是读取,如果线分隔符从来没有额外写入问题)