Hacer que grep funcione para nombres de archivos especiales

Hacer que grep funcione para nombres de archivos especiales

Tengo un conjunto de txtarchivos cuyos nombres pueden contener espacios o caracteres especiales como #.

Tengo una grepsolución grep -L "cannot have" $(grep -l "must have" *.txt)para enumerar todos los archivos que tienen must havepero no cannot have.

Por ejemplo, hay un archivo abc defg.txtque contiene sólo 1 línea: must have.

Normalmente, la solución grep debería descubrirlo abc defg.txt, pero devuelve:

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

Creo que para los nombres de archivos que contienen #, la solución grep tampoco es válida.

¿Alguien podría ayudarme a modificar la solución grep?

Respuesta1

SIestás dispuesto a ir más lejos, awk puede hacerlo de una sola 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 una mirada más reciente, puede simplificar con BEGINFILE y ENDFILE. (Como todas las respuestas de awk, puede colocar los comandos de awk en un archivo con -f y, como la mayoría, puede convertirlos fácilmente a perl si lo prefiere).

Respuesta2

Como ya estás usando opciones específicas de GNU ( -L), puedes hacer:

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

La idea es usar -Zpara imprimir la lista de nombres de archivos delimitados por NUL y usar xargs -r0para pasar esa lista como argumentos al segundo grep.

La sustitución de comandos, de forma predeterminada, se divide en espacio, tabulación y nueva línea (y NUL en zsh). Otros caparazones similares a Bourne zshtambién realizan globos sobre cada palabra resultante de esa división.

Podrías hacerlo:

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
  )

Pero eso aún se interrumpiría en los nombres de archivos que contienen caracteres de nueva línea.

En zsh(y zshsólo), puedes hacer:

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

O:

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

Respuesta3

Considere usar finden su lugar y grepusar un comando de shell:

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

información relacionada