Итеративно записывать в тот же файл подстановки процесса на 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, так и перенаправленный handle в значительной степени. Что-то в духе

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, но работает в Bash 4.4 в нескольких дистрибутивах (Arch, Ubuntu и Debian)

решение1

macOS поставляется с очень старой версией bash. Эта ошибка (что дескриптор файла для этой подстановки процесса закрывается перед вызовом teeв этом контексте)¹ была исправлена ​​в более новых версиях. Вы получите ту же проблему в Linux (с другим сообщением об ошибке, поскольку /dev/fd/xтам реализовано по-другому) с bash 3.2 там.

Здесь вы могли бы использовать 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

Связанный контент