Como usar com segurança a saída do grep em um script?

Como usar com segurança a saída do grep em um script?

Em um script, quero encontrar arquivos que contenham algum texto. Preciso saber em que arquivo o texto se encontra e a linha completa dentro do arquivo em que o texto se encontra. grepé o utilitário que faz isso, mas como posso obter a saída em um formato utilizável, visto que pode haver :em nomes de arquivos? Existe algum tipo de --porcelainmodo grepque eu possa usar, como gitos comandos costumam fazer?

Exemplo: tenho uma pasta cheia de arquivos com esse nome test-num:1:date:jan-2que desejo percorrer. Os arquivos contêm FAILURE:<some reason>ou SUCCESS:<some reason>(entre outras coisas). Preciso de um script que pesquise determinados motivos e armazene o nome do arquivo e o motivo (toda a linha do texto está bem) para processamento posterior. A saída pode estar em qualquer tipo de estrutura de dados, desde que eu possa executar código sobre ela.

Responder1

Não existe grep --porcelain, lidar com caracteres especiais em nomes de arquivos sempre foi uma reflexão tardia no UNIX. Você poderia tentar algo assim, ao preço da eficiência:

pattern='some pattern'
for file in ./*; do
    grep -- "$pattern" "$file" | while read -r line; do
        printf 'file: %s, line: %s\n' "$file" "$line"
    done
done

Responder2

Versões recentes (-ish) do GNU grep têm uma opção -Zque torna a saída inequívoca, mas é voltada principalmente para usos como grep -lZ … | xargs -0. Ainda funciona se você estiver listando o conteúdo da linha, o byte nulo substitui os dois pontos e o conteúdo da linha ainda termina em uma nova linha¹, mas os shells não são bons para lidar com bytes nulos, então você terá dificuldade em analisar esta saída .

Uma solução simples (com uma pequena penalidade no desempenho) é executar o grep em cada arquivo individualmente.

Outra solução é usar uma linguagem como Perl ou Python. Perl é muito bom em emular grep;  grep REGEXé basicamente perl -ne '/REGEXP/ and print'.

Mas você pode não precisar disso se a saída não for realmente ambígua. Por exemplo, se as linhas correspondentes não contiverem dois pontos, o nome do arquivo estará em uma linha até o último dois pontos. Se todas as linhas correspondentes começarem com SUCCESSou FAILUREe essas palavras não aparecerem nos nomes dos arquivos, você poderá usar isso para localizar a separação, etc.

¹ Exceto ao usar -zpara filtrar registros terminados em nulo em vez de registros terminados em nova linha, então nulo é o terminador do nome do arquivo e o terminador do resultado; sem -oa saída ainda é inequívoca, com registros de saída alternados sendo nomes de arquivos e registros correspondentes na saída.

Responder3

Como usar com segurança a saída do grepem um roteiro?

... A saída pode estar em qualquer tipo deestrutura de dados,contanto que eu possa executar código sobre ele.

Os scripts Shell realmente não possuem estruturas de dados. Existem arrays, mas isso é tudo - e não é fácil obter saída canalizada para um array com segurança. (Nomes de arquivospodecontêm novas linhas.)

O melhor caminho paraexecutar códigosobre seus arquivos em um script de shell é apenas executar o código sobre os arquivos - não tentar salvar os nomes dos arquivos para uso posterior.

Para fazer isso, use find:

find somedir -type f -exec grep -q somepattern {} \; -exec somecommand {} \;

No entanto, ao ler sua pergunta mais de perto, parece que você realmente não querexecutar códigosobre seus arquivos, você só deseja fazer algum processamento de texto em determinadas linhas. Neste caso, a opção GNU Grep -zé provavelmente o que você deseja. Isso, e o conhecimento de Sed ou Awk, resolverão sua pergunta.


Pode ser inteligente alterar a convenção de nomenclatura de arquivos.

informação relacionada