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 sort
na saída padrão. Como eu faria isso?
Eu posso conseguir assim:
ls "$@" | sort | tee /dev/tty | rev > /tmp/output
No entanto, usar /dev/tty
está 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 tee
enviará 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, myfile
se for.
A outra cópia será enviada rev > /tmp/output
em 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/3
grava uma cópia da sort
saí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 bash
especí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 >&fd
command
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 ( cat
lê 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 ls
saída padrão apenas reorganizando os comandos.