Muitas vezes, ao trabalhar na linha de comando, encontro-me especificando a mesma operação em várias instâncias diferentes especificadas por algum fluxo de entrada e, em seguida, querendo recombinar suas saídas de alguma maneira específica.
Dois casos de uso de ontem:
Eu queria ver os assuntos de vários certificados SSL agrupados em um arquivo PEM.
cat mypemfile.crt | (openssl x509 -noout -text | fgrep Subject:)
só me mostra o primeiro. Preciso dividir os certificados e executar o mesmo comando em cada um deles e concatenar os resultados.
csplit
pode dividi-los, mas apenas em arquivos. Isso é um incômodo.Por que não posso simplesmente dizer
cat mypemfile.crt | csplit-tee '/BEGIN/ .. /END/' | on-each ( openssl x509 -noout -text | fgrep Subject: ) | merge --cat
?
Executamos uma instância JupyterHub que divide servidores de notebook como contêineres Docker. Quero observar seus registros com carimbo de data e hora. É bastante fácil para um contêiner:
sudo docker logs -t -f $container_id
(O
-t
adiciona um carimbo de data/hora e-f
mantém o canal aberto, comotail -f
.)É fácil listar os logs de todos os contêineres, classificados por carimbos de data/hora:
sudo docker ps | awk '{print $1}' | while read container_id do sudo docker logs -t $container_id done | sort
ou
sudo docker ps | awk '{print $1}' | xargs -n1 sudo docker logs -t | sort
ou
sudo docker ps | awk '{print $1}' | parallel sudo docker logs -t {} | sort
mas nada disso me permitirá usar a
-f
opção de observar os logs conforme eles chegam.Por que não posso simplesmente usar
sudo docker ps | awk '{print $1}' | csplit-tee /./ | on-each (xargs echo | sudo docker logs -t -f) | merge --line-by-line --lexicographically
ou
sudo docker ps | awk '{print $1}' | parallel --multipipe sudo docker logs -t -f {} | merge --line-by-line --lexicographically
?
Claramente, isso requer
- Suporte específico para shell. Talvez precisássemos de um símbolo distinto de "multi-pipe".
- Novas ferramentas (
csplit-tee
,on-each
emerge
) que dividem e mesclam pipelines. - Uma convenção fixa sobre como especificar, dentro das ferramentas, arbitrariamente muitos descritores de arquivos de entrada e saída, de modo que esse shell os trate como pipelines paralelos.
Isso foi feito? Ou algo equivalente que eu possa aplicar aos meus casos de uso?
É viável sem suporte específico ao kernel? Eu sei que o kernel normalmente tem um número máximo fixo de descritores de arquivos abertos, mas uma implementação pode contornar isso, não tentando cegamente abri-los todos de uma vez.
É viável, sabendo queParalelo GNUÉ viável?
Responder1
Com GNU Paralelo:
cat mypemfile.crt |
parallel --pipe -N1 --recstart '-----BEGIN' 'openssl x509 -noout -text | fgrep Subject:'
Não testado:
sudo docker ps | awk '{print $1}' |
sudo parallel -j0 --lb docker logs -t -f {}
sudo docker ps | awk '{print $1}' |
sudo parallel -j0 --tag --lb docker logs -t -f {}
Responder2
Não, o shell não pode fazer isso. Um pipe é apenas um fluxo de uma origem para um destino, geralmente em processos diferentes.
Vejamos seu primeiro exemplo:
cat mypemfile.crt |
(openssl x509 -noout -text | fgrep Subject:)
Você deseja que o arquivo de entrada seja dividido ao longo de linhas contendo BEGIN e END, mas cat
não se importa com o conteúdo, e os pipes não possuem indicação fora do limite de separadores de registros.
Você poderia conseguir isso com convenções específicas na origem e no destino de um pipe, mas isso eliminaria a principal vantagem de um pipe, ser capaz de combinar programas como blocos de construção para tarefas que não foram previstas por seus autores.
Neste exemplo específico, seria muito mais fácil modificar openssl
para processar vários certificados em um fluxo do que modificar openssl
para processar vários fluxos E modificar cat
para fornecer esses vários fluxos.