Escreve iterativamente no mesmo arquivo de substituição de processo em MacOs

1

O seguinte código bash-fu funciona bem no Linux, mas quebra 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 que PROG 1 sem erros. No MacOS, parece que o identificador de pipe está fechado ou não herdado.

O acima é a amostra minimizada para reproduzir o problema. Na realidade, eu processo tanto a saída quanto o 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)

    
por nponeccop 01.12.2017 / 07:21

1 resposta

2

O macOS vem com uma versão muito antiga de bash . Esse bug (que o descritor de arquivo para essa substituição de processo é fechado antes de chamar tee nesse contexto) foi corrigido em versões mais novas. Você obteria o mesmo problema no Linux (com uma mensagem de erro diferente, pois /dev/fd/x é implementado de forma diferente lá) com o bash 3.2 lá.

Aqui, você pode usar zsh ou ksh93 . É uma boa idéia evitar bash aqui mesmo assim como ele não espera por processos em substituições de processos (o zsh espera por eles, o ksh93 pode ser avisado para wait para eles).

Note que, mesmo com a versão mais recente (4.4.12), bash ainda tem alguns bugs, 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 são acionados por canais 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 o 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
    
por 01.12.2017 / 11:43