Tengo un conjunto de txt
archivos cuyos nombres pueden contener espacios o caracteres especiales como #
.
Tengo una grep
solución grep -L "cannot have" $(grep -l "must have" *.txt)
para enumerar todos los archivos que tienen must have
pero no cannot have
.
Por ejemplo, hay un archivo abc defg.txt
que 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 -Z
para imprimir la lista de nombres de archivos delimitados por NUL y usar xargs -r0
para 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 zsh
tambié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 zsh
só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 find
en su lugar y grep
usar un comando de shell:
find . -name '*.txt' -print0 | xargs -0 -I{} sh -c 'grep -q "must have" -- "{}" && grep -L "cannot have" -- "{}"'