
以下 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