
次の 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 ではパイプ ハンドルが閉じられているか、継承されていないようです。
上記は問題を再現するための最小限のサンプルです。実際には、tie出力とリダイレクトされたハンドルの両方を頻繁に処理します。
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 でも、bash 3.2 を使用すると、同じ問題が発生します (Linux では/dev/fd/x
実装が異なるため、エラー メッセージも異なります)。
zsh
ここでは、代わりにまたは を使用できます。いずれにしても、ここではksh93
使用しない方がよいでしょう。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
¹ 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