
Tengo un script bash que llama a una función. La función, entre otras cosas, ejecuta una canalización que absorbe su salida. Para simplificarlo, aquí hay un ejemplo artificial:
#!/bin/bash
func() {
ls "$@" | sort | rev > /tmp/output
}
func "$@"
Luego harías algo como esto para ejecutarlo. Haría sus cosas y entregaría su carga útil a un archivo. No hay salida a la pantalla.
$ ./myscript .
Ahora, digamos que quiero la sort
salida estándar. ¿Como podría hacerlo?
Puedo lograrlo así:
ls "$@" | sort | tee /dev/tty | rev > /tmp/output
Sin embargo, usar /dev/tty
es incorrecto porque no funcionará:
$ ./myscript > myfile
¿Existe una forma más correcta de referirse a la salida estándar de un script bash desde una tubería dentro de una función?
(Estoy usando bash 4.3.0 en Arch Linux)
Respuesta1
La forma más sencilla que se me ocurre de hacer esto es:
ls "$@" | sort | tee >(rev > /tmp/output)
Enviará tee
una copia a STDOUT y, dado que ya no hay una |
después, se hereda, lo que significa que irá al TTY si no se redirige, y a usted myfile
si lo es.
La otra copia se enviará rev > /tmp/output
a su STDIN. En bash, >(...)
ejecuta lo que esté entre paréntesis y luego lo sustituye >(...)
por la ruta a una tubería conectada a su STDIN.
Respuesta2
Dado que la otra respuesta no es clara al respecto, la otra (otra) forma es
exec 3>&1
ls | sort | tee /dev/fd/3 | rev > /tmp/output
Elexec 3>&1
duplicarlimita el descriptor de archivo 1 (stdout) como el descriptor de archivo 3. Luego tee /dev/fd/3
escribe una copia de sort
la salida de ese descriptor de archivo. Esto debería funcionar en cualquier shell, pero puede depender del sistema operativo. Una variante que debería ser independiente del sistema operativo pero que es bash
específica es:
exec 3>&1
ls | sort | tee >( cat >&3 ) | rev > /tmp/output
En bash
, le proporciona un identificador de archivo para una tubería a un proceso en ejecución>(command)
command
. En cualquier shell (?), se ejecutacommand >&fd
command
con su salida estándar redirigida al descriptor de archivo especificado, que debe haber sido establecido previamente.
Una versión más complicada, que debería funcionar en cualquier sistema *nix (Posix), es
pipe_to_stdout=$(mktemp -u)
mkfifo "$pipe_to_stdout"
cat "$pipe_to_stdout" &
ls | sort | tee "$pipe_to_stdout" | rev > /tmp/output
rm "$pipe_to_stdout"
que es prácticamente lo mismo que el segundo ejemplo ( cat
lee desde el FIFO y escribe en la salida estándar), pero usando menos humo y espejos a nivel de shell.
Tenga en cuenta que estas respuestas son flexibles, ya que puede escribir la salida de ls
en la salida estándar simplemente reorganizando los comandos.