以下 bash-fu 代码在 Linux 上运行良好,但在 MacOS 上崩溃:
files="foo bar"
echo PROG 1
for file in $files
do
echo $file | tee -a tempfile.txt
done
sort -u tempfile.txt
echo PROG 2
function trick {
for file in $files
do
echo $file | tee -a $1
done
}
trick >(sort -u)
错误是:
PROG 1
foo
bar
bar
foo
PROG 2
tee: /dev/fd/63: Bad file descriptor
foo
tee: /dev/fd/63: Bad file descriptor
bar
在 Linux 上PROG 2
写入相同的行,PROG 1
没有错误。在 MacOS 上,管道句柄似乎是关闭的或不继承的。
以上是重现该问题的最小化示例。实际上,我大量处理连接输出和重定向句柄。一些精神上的东西
function trick {
for file in $files
do
echo $file | tee -a $1 | grep -Eo "^.."
done
}
trick >(sort -u | sed 's|o|x|g')
该代码无法在 Bash 4.1 中运行,但可以在多个发行版(Arch、Ubuntu 和 Debian)中的 Bash 4.4 中运行
答案1
macOS 附带了一个非常旧的版本bash
。该错误(该进程替换的文件描述符tee
在该上下文中调用之前已关闭)已在较新版本中修复。在 Linux 上/dev/fd/x
使用 bash 3.2 时你会遇到同样的问题(有不同的错误消息,因为那里的实现方式不同)。
在这里,您可以使用zsh
orksh93
代替。bash
无论如何,避免这里是个好主意,因为它不等待进程替换中的进程(zsh 等待他们,ksh93 可以被告知wait
)。
请注意,即使使用最新版本(截至撰写时为 4.4.12),bash
仍然存在一些错误,例如:
$ bash -c 'eval cat <(echo test)'
test # OK but:
$ bash -c 'eval "echo foo;cat" <(echo test)'
foo
cat: /dev/fd/63: No such file or directory
$ bash -c 'eval f=<(echo test) "; cat \$f"'
cat: /dev/fd/63: No such file or directory
有些仍然由管道触发,例如:
$ cat file
echo "$1"
cat "$1"
$ bash -c 'source ./file <(echo test)'
/dev/fd/63
test # OK but:
$ cat file2
echo "$1" | wc -c
cat "$1"
$ bash -c 'source ./file2 <(echo test)'
11
cat: /dev/fd/63: No such file or directory
1 一旦存在管道,bash 就会关闭该文件描述符。更短的再现器:
$ bash -c 'f() { :; cat "$1"; }; f <(echo OK)'
OK
$ bash -c 'f() { :|:; cat "$1"; }; f <(echo test)'
cat: /dev/fd/63: No such file or directory