MacOsで同じプロセス置換ファイルに繰り返し書き込む

MacOsで同じプロセス置換ファイルに繰り返し書き込む

次の 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

関連情報