Faça o grep funcionar para nomes de arquivos especiais

Faça o grep funcionar para nomes de arquivos especiais

Eu tenho um conjunto de txtarquivos cujos nomes podem conter espaços ou caracteres especiais como #.

Eu tenho uma grepsolução grep -L "cannot have" $(grep -l "must have" *.txt)para listar todos os arquivos que possuem, must havemas não cannot have.

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

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

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

Acho que para nomes de arquivos que contêm #, a solução grep também é inválida.

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

Responder1

SEvocê 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 ficar mais atualizado, você pode simplificar com BEGINFILE e ENDFILE. (Como todas as respostas do awk, você pode colocar os comandos do awk em um arquivo com -f e, como a maioria, pode converter facilmente para perl, se preferir.)

Responder2

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

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

A ideia é usar -Zpara imprimir a lista de nomes de arquivos delimitados por NUL e usar xargs -r0para passar essa lista como argumentos para o segundo arquivo grep.

A substituição de comando, por padrão, divide-se em espaço, tabulação e nova linha (e NUL em zsh). Conchas semelhantes a Bourne, além de zshtambém realizarem 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 seria interrompido em nomes de arquivos contendo caracteres de nova linha.

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

IFS=$'\0'
grep -L -- "cannot have" $(grep -lZ -- "must have" *.txt)

Ou:

grep -L -- "cannot have" ${(ps:\0:)"$(grep -lZ -- "must have" *.txt)"}

Responder3

Considere findusar grepum comando shell:

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

informação relacionada