在 MacO 上迭代写入同一进程替换文件

在 MacO 上迭代写入同一进程替换文件

以下 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 时你会遇到同样的问题(有不同的错误消息,因为那里的实现方式不同)。

在这里,您可以使用zshorksh93代替。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

相关内容