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?


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)

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.


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


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"

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

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

0

Para quem procura uma linha:

grep xyz abc.txt | while read -r line; do echo "Processing $line"; done
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.