
O seguinte código bash-fu funciona bem no Linux, mas falha no 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)
O erro é:
PROG 1
foo
bar
bar
foo
PROG 2
tee: /dev/fd/63: Bad file descriptor
foo
tee: /dev/fd/63: Bad file descriptor
bar
No Linux PROG 2
escreve as mesmas linhas PROG 1
sem erros. No MacOS, parece que o identificador do pipe está fechado ou não é herdado.
O acima é a amostra minimizada para reproduzir o problema. Na realidade, eu processo pesadamente a saída do empate e o identificador redirecionado. Algo no espírito de
function trick {
for file in $files
do
echo $file | tee -a $1 | grep -Eo "^.."
done
}
trick >(sort -u | sed 's|o|x|g')
O código não funciona no Bash 4.1, mas funciona no Bash 4.4 em várias distros (Arch, Ubuntu e Debian)
Responder1
O macOS vem com uma versão muito antiga do bash
. Esse bug (que o descritor de arquivo para aquela substituição de processo é fechado antes de chamar tee
nesse contexto)¹ foi corrigido em versões mais recentes. Você teria o mesmo problema no Linux (com uma mensagem de erro diferente, conforme /dev/fd/x
implementado de forma diferente) com o bash 3.2.
Aqui, você poderia usar zsh
ou ksh93
em vez disso. É uma boa ideia evitar bash
aqui de qualquer maneira, poisnão espera por processos em substituições de processos(zsh espera por eles, ksh93 pode ser informado wait
para eles).
Observe que mesmo com a versão mais recente (4.4.12 no momento da escrita), bash
ainda há alguns bugs aqui, como:
$ 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
e alguns ainda acionados por pipes como:
$ 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 fecha esse descritor de arquivo assim que houver um pipeline. Um reprodutor mais curto:
$ 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