Tengo un directorio llamado "etiquetas" en el que hay archivos de texto que contienen etiquetas para "gato" o "perro" o ambos en líneas separadas.
El contenido de los archivos en el directorio de etiquetas es:
cat labels/1.txt
cat
cat labels/2.txt
dog
cat labels/3.txt
cat
dog
Quiero obtener los nombres de los archivos que contienen únicamente la etiqueta "cat". Intenté el siguiente comando:
ls labels | grep -Rwl "cat"
labels/1.txt
labels/3.txt
Pero este comando devuelve los nombres de aquellos archivos que contienen "cat" o ambos. Pero mi requisito es obtener los nombres de archivos que contengan sólo "gato", no tanto "gato" como "perro".
De manera similar, cuando intento obtener nombres de archivos que contienen únicamente "perro". Si busco de la misma manera, devuelve nombres de archivos que contienen "perro" o ambas etiquetas.
ls labels | grep -Rwl "dog"
labels/2.txt
labels/3.txt
Respuesta1
Puede usar grep
dos veces: a) para enumerar todos los archivos con cat
, luego b) filtrar dog
los que contienen. Utilice -l
y -L
, respectivamente, donde -l
enumera los nombres de archivos con coincidencias y -L
nombres de archivos sin coincidencias:
grep -L 'dog' $(grep -l 'cat' <list of files>)
Ver man grep
:
-L, --archivos-sin-coincidencia
Suprimir la producción normal; en su lugar, imprima el nombre de cada archivo de entrada del cual normalmente no se habría impreso ninguna salida. El escaneo se detendrá en el primer partido.
-l, --archivos-con-coincidencias
Suprimir la producción normal; en su lugar, imprima el nombre de cada archivo de entrada desde el cual normalmente se habría impreso la salida. El escaneo se detendrá en el primer partido.
Respuesta2
Con GNU grep
y xargs
( -R
que ya estás usando es una grep
extensión de GNU de todos modos, aunque -r
es preferible allí):
grep -rwlZ cat labels/ | xargs -r0 grep -wL dog
Enumeraría los archivos que contienen al menos una cat
palabra y ninguna dog
palabra (palabraen ese contexto significa: "no rodeado decaracteres de palabra",caracteres de palabrasiendo caracteres alfanuméricos y guión bajo). Reemplazar -w
con -x
para buscar líneas cuyo contenido completoes cat
/ dog
.
Respuesta3
Si desea enumerar los nombres de los archivos que contienen "gato" pero no "perro", intente algo como esto, usando find
GNU awk
(o cualquier otro awk
que admita ENDFILE
bloques, ya que es una extensión de GNU para awk
):
$ find labels/ -type f -exec awk -v IGNORECASE=1 '
/\<cat\>/ { cat = 1 };
/\<dog\>/ { dog = 1 };
ENDFILE {
if (cat == 1 && dog == 0) {
print FILENAME
};
cat = 0;
dog = 0;
}' {} +
labels/file1.txt
O podrías usar perl
en lugar de awk
:
$ find labels/ -type f -exec perl -l -n -e '
$cat = 1 if m/\bcat\b/i;
$dog = 1 if m/\bdog\b/i;
if (eof) {
print $ARGV if ($cat && ! $dog);
$cat=0;
$dog=0;
}' {} +
labels/file1.txt
El resultado de las versiones anteriores de awk y perl se produjo con los siguientes archivos en el labels/
subdirectorio:
$ tail labels/*
==> labels/file1.txt <==
cat
==> labels/file2.txt <==
dog
==> labels/file3.txt <==
cat
dog
labels/file1.txt
es el único nombre de archivo impreso porque es el único archivo que contiene "gato" y no contiene "perro".
Respuesta4
for f in *; do diff -q <(sort -u "$f") <(echo cat) >/dev/null && echo "$f"; done