¿Cómo puede un script bash escribir su salida estándar desde dentro de una canalización?

¿Cómo puede un script bash escribir su salida estándar desde dentro de una canalización?

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 sortsalida estándar. ¿Como podría hacerlo?

Puedo lograrlo así:

ls "$@" | sort | tee /dev/tty | rev > /tmp/output

Sin embargo, usar /dev/ttyes 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á teeuna 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 myfilesi lo es.
La otra copia se enviará rev > /tmp/outputa 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/3escribe una copia de sortla 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 bashespecí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 >&fdcommand 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 ( catlee 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 lsen la salida estándar simplemente reorganizando los comandos.

información relacionada