Como um script bash pode escrever sua saída padrão dentro de um pipeline?

Como um script bash pode escrever sua saída padrão dentro de um pipeline?

Eu tenho um script bash que chama uma função. A função, entre outras coisas, executa um pipeline que afunda sua saída. Para simplificar, aqui está um exemplo inventado:

#!/bin/bash
func() {
  ls "$@" | sort | rev > /tmp/output
}
func "$@"

Você faria algo assim para executá-lo. Ele faria seu trabalho e entregaria sua carga útil em um arquivo. Não há saída para a tela.

$ ./myscript .

Agora, digamos que eu queira a saída sortna saída padrão. Como eu faria isso?

Eu posso conseguir assim:

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

No entanto, usar /dev/ttyestá errado, porque não funcionará:

$ ./myscript > myfile

Existe uma maneira mais correta de se referir à saída padrão de um script bash de dentro de um pipleine dentro de uma função?

(Estou usando o bash 4.3.0 no Arch Linux)

Responder1

A maneira mais simples que consigo pensar em fazer isso é:

ls "$@" | sort | tee >(rev > /tmp/output)

Ele teeenviará uma cópia para STDOUT e, como não há mais |depois dela, ela é herdada, o que significa que irá para o TTY se não for redirecionado, e para o seu, myfilese for.
A outra cópia será enviada rev > /tmp/outputem seu STDIN. No bash, >(...)executa o que estiver entre parênteses e depois substitui o >(...)pelo caminho para um canal conectado ao seu STDIN.

Responder2

Como a outra resposta não está sendo clara sobre isso, a outra (outra) maneira é

exec 3>&1
ls | sort | tee /dev/fd/3 | rev > /tmp/output

Oexec 3>&1 idiotalica o descritor de arquivo 1 (stdout) como descritor de arquivo 3. Em seguida, tee /dev/fd/3grava uma cópia da sortsaída de para esse descritor de arquivo. Isso deve funcionar em qualquer shell, mas pode depender do sistema operacional. Uma variante que deve ser independente do sistema operacional, mas bashespecífica, é:

exec 3>&1
ls | sort | tee >( cat >&3 ) | rev > /tmp/output

Em bash, fornece um identificador de arquivo para um canal para um processo em execução>(command)command. Em qualquer (?) Shell, executacommand >&fdcommand com seu stdout redirecionado para o descritor de arquivo especificado, que deve ter sido previamente estabelecido.

Uma versão mais complicada, que deve funcionar em qualquer sistema *nix (Posix), é

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 é praticamente igual ao segundo exemplo ( catlê do fifo e grava no stdout), mas usando menos fumaça e espelhos no nível do shell.

Observe que essas respostas são flexíveis, pois você pode escrever a saída de na lssaída padrão apenas reorganizando os comandos.

informação relacionada