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

1

Em um script, quero encontrar arquivos que contenham algum texto. Eu preciso saber o arquivo que o texto é encontrado, e a linha completa dentro do arquivo que o texto é encontrado em. grep é o utilitário que faz isso, mas como posso obter a saída em uma forma utilizável, dado que pode haver : em nomes de arquivos? Existe algum tipo de modo --porcelain para grep que eu possa usar, como os comandos git costumam ter?

Exemplo: eu tenho uma pasta cheia de arquivos nomeados como test-num:1:date:jan-2 pelos quais eu quero passar. Os arquivos contêm FAILURE:<some reason> ou SUCCESS:<some reason> (entre outras coisas). Eu preciso de um script que procura por certas razões e armazena o nome do arquivo, e o motivo (a linha inteira do texto é bom) para processamento posterior. A saída pode estar em qualquer tipo de estrutura de dados, desde que eu possa executar o código sobre ela.

    
por Drew 31.10.2016 / 08:23

3 respostas

1

Não existe um grep --porcelain , o tratamento de 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
    
por 31.10.2016 / 08:48
0

As versões recentes (-ish) do GNU grep têm uma opção -Z , o que torna a saída não ambígua, mas é destinada principalmente a usos como grep -lZ … | xargs -0 . Ainda funciona se você estiver listando o conteúdo da linha, o byte nulo substitui o cólon e o conteúdo da linha ainda termina em uma nova linha¹, mas shells não são bons em lidar com bytes nulos, então você terá dificuldade em analisar essa saída .

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

Outra solução é usar uma linguagem como Perl ou Python. Perl é muito bom em emular o 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 será tudo em uma linha até os últimos dois pontos. Se todas as linhas correspondentes começarem com SUCCESS ou FAILURE e essas palavras não aparecerem nos nomes dos arquivos, você poderá usar isso para localizar a separação, etc.

¹ Exceto quando usar -z para filtrar registros terminados em nulo em vez de registros terminados por nova linha, então, null é o terminador de nome de arquivo e o terminador de resultado; sem -o , a saída ainda não é ambígua, com registros de saída alternados sendo nomes de arquivos e registros correspondentes na saída.

    
por 01.11.2016 / 00:23
0

How to safely use the output of grep in a script?

... The output can be in any sort of data structure, as long as I can run code over it.

Os scripts do shell não possuem estruturas de dados. Existem matrizes, mas é sobre isso - e não é fácil obter a saída canalizada em um array com segurança. (Nomes de arquivos podem conter novas linhas.)

A melhor maneira de executar código sobre seus arquivos em um script shell é simplesmente executar o código sobre os arquivos - e 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 a sua pergunta mais de perto, parece que você não quer realmente executar o código nos seus arquivos, você só quer fazer algum processamento de texto em certas linhas. Neste caso, a opção GNU Grep -z é provavelmente o que você deseja. Isso, e um conhecimento de Sed ou Awk, vai lidar com a sua pergunta.

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

    
por 01.11.2016 / 01:25

Tags