Como processar cada linha recebida como resultado do comando grep

152

Eu tenho várias linhas recuperadas de um arquivo após executar o comando grep da seguinte maneira:

var=`grep xyz abc.txt`

Digamos que eu tenha 10 linhas, que consiste em xyz como resultado.

Agora eu preciso processar cada linha que recebi como resultado do comando grep. Como proceder para isso?

XYZ_Linux
fonte
6
Nenhuma das respostas aqui menciona o poder grep -odesse tipo de coisa. A -obandeira retornará apenas o texto que corresponder, com uma correspondência por linha de saída. (Não é exaustiva, por isso echo aaa |grep 'a*'só lhe dá "aaa" e omite as três partidas parciais "", "a" e "aa")
Adam Katz

Respostas:

269

Uma das maneiras fáceis é não armazenar a saída em uma variável, mas iterar diretamente sobre ela com um loop while / read.

Algo como:

grep xyz abc.txt | while read -r line ; do
    echo "Processing $line"
    # your code goes here
done

Existem variações nesse esquema, dependendo exatamente do que você procura.

Se você precisar alterar variáveis ​​dentro do loop (e deixar essa alteração visível fora dele), poderá usar a substituição de processo, conforme indicado na resposta de fedorqui :

while read -r line ; do
    echo "Processing $line"
    # your code goes here
done < <(grep xyz abc.txt)
Esteira
fonte
se não houver linha com xyz?
XYZ_Linux
Então nada acontece, o loop não é executado.
Mat
13
O problema com esse método é que (por causa do pipe) tudo dentro do loop está em um subshell, portanto, definir variáveis ​​definidas fora do loop durante o loop não disponibiliza seus valores após o loop!
David Doria
3
@ David: forneceu uma alternativa para resolver sua preocupação. (fedorqui já tinha abordado também.)
Mat
Para comandos cuja última linha de saída não é encerrado com uma nova linha, você precisa de: while read p || [[ -n $p ]]; do ...(emprestado de stackoverflow.com/questions/1521462/... )
Ohad Schneider
21

Você pode while readexecutar o seguinte loop, que será alimentado pelo resultado do grepcomando usando a chamada substituição de processo:

while IFS= read -r result
do
    #whatever with value $result
done < <(grep "xyz" abc.txt)

Dessa forma, você não precisa armazenar o resultado em uma variável, mas "injeta" diretamente sua saída no loop.


Observe o uso IFS=e de read -racordo com as recomendações do BashFAQ / 001: Como posso ler um arquivo (fluxo de dados, variável) linha por linha (e / ou campo por campo)? :

A opção -r para ler impede a interpretação de barra invertida (geralmente usada como um par de nova linha de barra invertida, para continuar em várias linhas ou para escapar dos delimitadores). Sem essa opção, quaisquer barras invertidas sem escape na entrada serão descartadas. Você quase sempre deve usar a opção -r com read.

No cenário acima, IFS = impede o corte de espaços em branco à esquerda e à direita. Remova-o se desejar esse efeito.

Em relação à substituição do processo, é explicado na página de hackers do bash :

A substituição de processo é uma forma de redirecionamento em que a entrada ou saída de um processo (alguma sequência de comandos) aparece como um arquivo temporário.

fedorqui 'Então pare de prejudicar'
fonte
OK, eu bati na forversão. Tentei fazer um loop "${$(grep xyz abc.txt)[@]}"como em stackoverflow.com/a/14588210/1983854, mas não conseguiu. Então, eu apenas deixo a primeira versão.
fedorqui 'Então pare de prejudicar'
1
Você não pode aplicar a expansão de parâmetros a uma substituição de comando (a menos que esteja usando zsh, onde esse tipo de aninhamento provavelmente funciona).
Chepner #
Um possível problema com esse idioma é que, se algo dentro do loop tentar ler da entrada padrão, ele receberá parte do arquivo. Para evitar essa possibilidade, eu gostaria de enviar o arquivo pelo descritor de arquivo 3, em vez do stdin. Basta usar while IFS= read -r result <&3edone 3< <(grep ...
Gordon Davisson
8

Eu sugeriria usar awk em vez de grep + outra coisa aqui.

awk '$0~/xyz/{ //your code goes here}' abc.txt

Julien Grenier
fonte
1
Como você referencia a linha completa encontrada //your code goes here?
user857990
2
@ user857990 A linha inteira é representada como $ 0 em AWK.
Julien Grenier
2

Sem nenhuma iteração com a opção --line-buffered grep:

your_command | grep --line-buffered "your search"

Exemplo da vida real com um comando de depuração do roteador do Symfony PHP Framework, para grep todas as rotas relacionadas à "api":

php bin/console d:r | grep --line-buffered "api"
Nicolas Bonnici
fonte
A única solução que funciona se your_command é de longa duração (como tail -f some.log, no meu caso) ...
Izkata
0

Muitas vezes, a ordem do processamento não importa. O GNU Parallel é feito para esta situação:

grep xyz abc.txt | parallel echo do stuff to {}

Se o seu processamento for mais parecido com:

grep xyz abc.txt | myprogram_reading_from_stdin

e myprogramé lento, você pode executar:

grep xyz abc.txt | parallel --pipe myprogram_reading_from_stdin
Ole Tange
fonte
0

Repita os resultados grep com um loop while / read. Gostar:

grep pattern filename.txt | while read -r line ; do
    echo "Matched Line:  $line"
    # your code goes here
done
Abhi km
fonte
0

Para quem procura uma linha:

grep xyz abc.txt | while read -r line; do echo "Processing $line"; done
Laurent
fonte