
El siguiente código bash-fu funciona bien en Linux pero falla en 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)
El error es:
PROG 1
foo
bar
bar
foo
PROG 2
tee: /dev/fd/63: Bad file descriptor
foo
tee: /dev/fd/63: Bad file descriptor
bar
En Linux PROG 2
escribe las mismas líneas que PROG 1
sin errores. En MacOS parece que el identificador de tubería está cerrado o no se hereda.
Lo anterior es la muestra minimizada para reproducir el problema. En realidad, proceso mucho tanto la salida vinculada como el identificador redirigido. Algo en el espíritu de
function trick {
for file in $files
do
echo $file | tee -a $1 | grep -Eo "^.."
done
}
trick >(sort -u | sed 's|o|x|g')
El código no funciona en Bash 4.1, pero funciona en Bash 4.4 en múltiples distribuciones (Arch, Ubuntu y Debian)
Respuesta1
macOS viene con una versión muy antigua de bash
. Ese error (que el descriptor de archivo para la sustitución de ese proceso se cierra antes de llamar tee
en ese contexto)¹ se solucionó en versiones más recientes. Obtendría el mismo problema en Linux (con un mensaje de error diferente, ya que /dev/fd/x
allí se implementa de manera diferente) con bash 3.2 allí.
Aquí podrías usar zsh
o ksh93
en su lugar. Es una buena idea evitar bash
este lugar de todos modos ya queno espera procesos en sustituciones de procesos(zsh los espera, se le puede indicar a ksh93 que wait
lo haga).
Tenga en cuenta que incluso con la última versión (4.4.12 al momento de escribir este artículo), bash
todavía hay algunos errores aquí 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
y algunos todavía activados por tuberías 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 cierra ese descriptor de archivo tan pronto como hay una canalización. Un reproductor más corto:
$ 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