
Следующий код 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