Os multiplos pipelines de shell q tava falando:
seq 1 100 \
> >(awk '$1 % 2 == 0' | wc -l | read i && echo "$i divisiveis por 2") \
> >(awk '$1 % 3 == 0' | wc -l | read i && echo "$i divisiveis por 3") \
> >(awk '$1 % 5 == 0' | wc -l | read i && echo "$i divisiveis por 5")
O grande lance aqui é que voce pode duplicar os file descriptors de stdout/stderr de qualquer processo direto no bash/zsh/ksh(acho). Se voce tiver interesse, por baixo ele usa a syscall dup
/dup2
(man -a dup
). Na pratica o comando tee acaba sendo um wrapper pra usar essa syscall a partir do shell:
Por exemplo:
% seq 1 3 > a > b
% cat a b
1
2
3
1
2
3
que da na mesma que:
seq 1 3 | tee a b > /dev/null
No caso do tee voce tem q jogar fora o stdout dele porque por padrao ele sempre duplica pro stdout o que chega no stdin dele. Voce poderia jogar para um dos arquivos que voce colocaria nos argumentos, mas a legibilidade fica ruim:
seq 1 3 | tee a > b
De qualquer forma, pra jogar o stdout de um processo no stdin de outro, é simplesmente fazer um pipe entre eles, o que é costume de sefazer. Porem voce pode fazer de uma forma mais indireta: jogando o stdout para um arquivo, e esse arquivo na verdade funcionando como o stdin de um processo:
seq 1 5 | grep 3
seq 1 5 > >(grep 3)
A grande diferenca de fazer esta forma é que voce pode duplicar o stdout, permitindo "forkar" a pipeline:
seq 1 5 > >(grep 1) > >(grep 3) > >(grep 5)
O >(comando)
e <(comando)
sao bem legais. Eles permitem executar comandos "disfarçados" de arquivos.
No caso do >(comando)
, todo write no arquivo é na verdade um write no stdin do processo.
No caso do <(comando)
, todo read no arquivo é na verdade um read do stdout do processo.
Por exemplo, o diff
so le de arquivos. E se voce quiser fazer um diff de comandos? Voce pode jogar o stdout destes comandos pra arquivos e fazer o diff ler eles:
find arvore1 > find1
find arvore2 > find2
diff find1 find2
Porem isso usa disco a toa. Voce poderia fazer td via streams e gastar algo em torno de 8k de memoria:
diff <(find arvore1) <(find arvore2)
Desta forma o shell disponibiliza os resultados dos finds em memoria (normalmente 4k de stdout sao lidos por vez de cada processo) para serem lidos pelo diff :)
E o que acontece se arquivo a ser lido/escrito nao ser tratado como um stream, se fizer random access? Da erro. Exemplo:
% echo foobar > a
% python -c 'import sys; f = open(sys.argv[1]); f.seek(0); print "ok"' a
ok
% python -c 'import sys; f = open(sys.argv[1]); f.seek(0); print "ok"' <(cat a)
Traceback (most recent call last):
File "<string>", line 1, in <module>
IOError: [Errno 29] Illegal seek