Digamos que eu tenha um programa blackbox
e um arquivo com o seguinte conteúdo:
in this file
this line contains =TAG=
so does =TAG= this one
as =TAG= does this other line
this line does not
nor does this line
=TAG= here again
gone again
Como posso executar blackbox
somente nas linhas que contêm =TAG=
?
Nota 1: Uma maneira é usar um while read
loop, mas isso é considerado uma prática ruim . Então, qual é a maneira canônica e correta de fazer isso (se houver uma)?
Nota 2: Claro, se eu estivesse apenas editando texto, uma solução com AWK
ou sed
seria apropriada — mas blackbox
pode ter efeitos colaterais desejados. Esta pergunta é para aquelas situações em que preciso executar outro processo .
Nota 3: Você pode perguntar o que acontece se blackbox
for algo como nl
ou sort
—onde executá-lo em várias linhas juntas tem um resultado diferente de executar um novo processo em cada linha. Nesse caso, eu quero ser capaz de fazer isso de cada uma dessas três maneiras :
Por bloco: substitua cada bloco de linhas contíguas contendo
=TAG=
pelo resultado daqueleblackbox
bloco.Saída esperada com
blackbox
=nl
:in this file 1 this line contains =TAG= 2 so does =TAG= this one 3 as =TAG= does this other line this line does not nor does this line 1 =TAG= here again gone again
Linha a linha: substitua cada linha que contém
=TAG=
pelo resultado deblackbox
naquela linha.Saída esperada com
blackbox
=nl
:in this file 1 this line contains =TAG= 1 so does =TAG= this one 1 as =TAG= does this other line this line does not nor does this line 1 =TAG= here again gone again
Continuamente: envia todas as linhas que contêm
=TAG=
umblackbox
processo e substitui cada bloco pelas linhas que seriam impressas antes deblackbox
receber o próximo bloco.Saída esperada com
blackbox
=nl
:in this file 1 this line contains =TAG= 2 so does =TAG= this one 3 as =TAG= does this other line this line does not nor does this line 4 =TAG= here again gone again
(Se usássemos
sort
, todas as linhas correspondentes acabariam classificadas no último bloco correspondente, porque elas não seriam impressas até o final.)
Não encontrei nenhuma pergunta aqui sobre o problema geral, mas todos esses são casos especiais deste problema:
- Como escrever chaves de prefixo de script com barras invertidas (linha a linha)
- Editar arquivo com base na existência de uma string (linha a linha)
- Substituir condicionalmente as linhas do arquivo1 pelas linhas correspondentes do arquivo2 (linha a linha)
- É possível acelerar isso ao ler o script bash? (linha por linha)
- Substituir string pelo conteúdo de um arquivo usando sed (linha a linha)
- alterar nova linha com espaço em determinada condição com sed (bloco a bloco)
- Classificação de blocos de linhas que correspondem apenas à primeira (por bloco)
- Remover linhas que correspondem ao padrão, além de quaisquer linhas que o seguem que correspondem a um padrão diferente (em blocos)
- Substituir linhas que correspondem a um padrão por linhas de outro arquivo em ordem (continuamente)
- Algo como `colar` mas com um alinhamento vertical após um delimitador? (continuamente)
- Passar vários argumentos de linha de comando para um executável com arquivos de texto (continuamente)
- Exclua a enésima linha de cada linha que corresponda a um padrão (continuamente)
Aqui está uma maneira de fazer a primeira coisa que você pediu ( em blocos ), supondo
blackbox
que sejanl
:Se você quiser fazer isso uma linha de cada vez, como na sua segunda solicitação ( linha a linha ), basta mover
close()
:O GNU awk também suporta coprocessos caso você precise de mais controle sobre onde a saída do comando externo aparece na saída geral ou se precisar de qualquer outro processamento adicional antes de imprimi-lo.
E falando nisso... aqui está uma maneira de implementar sua terceira requisição, Continuously , usando o GNU awk para coprocessos:
Isso pressupõe que sua entrada não seja tão massiva que não caiba na memória. Se esse não for o caso, você pode fazer a mesma coisa com uma abordagem de 2 passagens lendo a entrada duas vezes em vez de armazená-la no
lines[]
array.O acima pode quebrar se o pipe ficar cheio (obrigado @Stéphane Chazelas por apontar isso), se o seu sistema estiver cheio
stdbuf
, você pode fazer o seguinte para tornarnl
a saída em buffer de linha:Com um awk não-GNU, você pode gravar em um arquivo temporário em vez de canalizar para um processo e, então, ler do arquivo temporário em vez de ler do processo, por exemplo:
ou, novamente, usar
stdbuf
da mesma forma que fazemos para a solução de coprocesso.Se você estiver pensando em usar,
getline
leia http://awk.freeshell.org/AllAboutGetline primeiro.Com
perl
:Este último pressupõe que o comando não armazena em buffer sua saída (o que evitamos
nl
-- na verdade, pedimos o buffer deL
saídao
ine -- aqui,stdbuf -oL
como encontrado em sistemas GNU ou FreeBSD) e que, ao ler uma linha de entrada, produz a(s) linha(s) correspondente(s) de saída dentro dos próximos 10.000 microssegundos (0,1 segundo).Aqui o comando é codificado como
"nl"
or"stdbuf", "-oL", "nl"
(que você também pode escreverqw(stdbuf -oL nl)
) dentro doperl
código. Para um comando e seu argumento serem passados como argumentos extras paraperl
:Em vez de um comando simples, você pode criar qualquer código shell usando o código acima e
sh -c 'any shell code'
como comando simples ou usar aopen $cmd, "|any shell code"
sintaxe¹¹ Na verdade, nas versões modernas do
perl
,perl
o ignorará a execução de um shell se o comando for simples o suficiente.