Faça o grep funcionar para nomes de arquivos especiais

3

Eu tenho um conjunto de arquivos txt cujos nomes podem conter espaço ou caracteres especiais como # .

Eu tenho um grep solution grep -L "cannot have" $(grep -l "must have" *.txt) para listar todos os arquivos que têm must have , mas não cannot have .

Por exemplo, existe um arquivo abc defg.txt que contém apenas 1 linha: must have .

Então, normalmente, a solução grep deve descobrir abc defg.txt , mas ela retorna:

grep: abc: No such file or directory
grep: defg.txt: No such file or directory

Eu acho que para nomes de arquivos contendo # , a solução grep também é inválida.

Alguém poderia me ajudar a alterar a solução do grep?

    
por SoftTimur 08.05.2014 / 10:00

3 respostas

2

SE você está disposto a ir mais longe, o awk pode fazer isso de uma só vez:

awk 'function s(){if(a&&!b){print f}} FNR==1{s();f=FILENAME;a=b=0} 
  /must have/{a=1} /cannot have/{b=1} END{s()}' filepattern

Para o gawk mais recente, você pode simplificar com BEGINFILE e ENDFILE. (Como todas as respostas awk você pode colocar os comandos awk em um arquivo com -f, e como a maioria você pode facilmente converter para perl se você preferir.)

    
por 08.05.2014 / 12:48
2

Como você já está usando as opções específicas do GNU ( -L ), você pode fazer:

grep -lZ -- "must have" *.txt | xargs -r0 grep -L -- "cannot have"

A idéia é usar -Z para imprimir a lista de nomes de arquivos delimitados por NUL e usar xargs -r0 para passar essa lista como argumentos para o segundo grep .

A substituição de comandos, por padrão, divide em espaço, tabulação e nova linha (e NUL em zsh ). Os shells semelhantes a Bourne, além de zsh , também executam globbing em cada palavra resultante dessa divisão.

Você poderia fazer:

IFS='
' # split on newline only
set -f # disable globbing
grep -L -- "cannot have" $(
    set +f # we need globbing for *.txt in this subshell though
    grep -l -- "must have" *.txt
  )

Mas isso ainda quebraria em nomes de arquivos contendo caracteres de nova linha.

Em zsh (e zsh apenas), você pode fazer:

IFS=$'
grep -L -- "cannot have" ${(ps:
grep -lZ -- "must have" *.txt | xargs -r0 grep -L -- "cannot have"
:)"$(grep -lZ -- "must have" *.txt)"}
' grep -L -- "cannot have" $(grep -lZ -- "must have" *.txt)

Ou:

IFS='
' # split on newline only
set -f # disable globbing
grep -L -- "cannot have" $(
    set +f # we need globbing for *.txt in this subshell though
    grep -l -- "must have" *.txt
  )
    
por 08.05.2014 / 10:24
-1

Considere usar find e grep usando um comando shell:

find . -name '*.txt' -print0 | xargs -0 -I{} sh -c 'grep -q "must have" -- "{}" && grep -L "cannot have" -- "{}"'
    
por 08.05.2014 / 10:20