Estoy buscando una forma de buscar archivos donde existen dos instancias de palabras en el mismo archivo. He estado usando lo siguiente para realizar mis búsquedas hasta este momento:
find . -exec grep -l "FIND ME" {} ;
El problema con el que me encuentro es que si no hay exactamente un espacio entre "FIND" y "ME", el resultado de la búsqueda no arroja el archivo. ¿Cómo adapto la cadena de búsqueda anterior en la que las palabras "FIND" y "ME" existen en un archivo en lugar de "FIND ME"?
Estoy usando AIX.
Respuesta aceptada:
Con herramientas GNU:
find . -type f -exec grep -lZ FIND {} + | xargs -r0 grep -l ME
Puedes hacer de manera estándar:
find . -type f -exec grep -q FIND {} ; -exec grep -l ME {} ;
Pero eso correría hasta dos grep
s por archivo. Para evitar ejecutar tantos grep
s y seguir siendo portátil y al mismo tiempo permitir cualquier carácter en los nombres de archivo, podría hacer:
convert_to_xargs() {
sed "s/[[:blank:]"']/\\&/g" | awk '
{
if (NR > 1) {
printf "%s", line
if (!index($0, "//")) printf "\"
print ""
}
line = $0
}'
END { print line }'
}
export LC_ALL=C
find .//. -type f |
convert_to_xargs |
xargs grep -l FIND |
convert_to_xargs |
xargs grep -l ME
La idea es convertir la salida de find
en un formato adecuado para xargs (que espera un espacio en blanco (SPC/TAB/NL en el C
configuración regional, YMMV en otras configuraciones regionales) lista separada de palabras donde las comillas simples, dobles y las barras diagonales inversas pueden escapar de los espacios en blanco y entre sí).
Por lo general, no puede posprocesar la salida de find -print
, porque separa los nombres de archivo con un carácter de nueva línea y no escapa a los caracteres de nueva línea que se encuentran en los nombres de archivo. Por ejemplo, si vemos:
./a
./b
No tenemos forma de saber si se trata de un archivo llamado b
en un directorio llamado a<NL>.
o si son los dos archivos a
y b
en el directorio actual.
Usando .//.
, porque //
no puede aparecer de otra manera en una ruta de archivo como resultado de find
(porque no existe un directorio con un nombre vacío y /
no está permitido en un nombre de archivo), sabemos que si vemos una línea que contiene //
, entonces esa es la primera línea de un nuevo nombre de archivo. Entonces podemos usar ese awk
Comando para escapar todos los caracteres de nueva línea excepto aquellos que preceden a esas líneas.
Si tomamos el ejemplo anterior, find
daría salida en el primer caso (un archivo):
.//a
./b
Cual awk escapa a:
.//a
./b
Así que xargs
lo ve como un argumento. Y en el segundo caso (dos archivos):
.//a
.//b
¿Qué awk
dejaría como está, así que xargs
ve dos argumentos.
Necesitas el LC_ALL=C
entonces sed
, awk
(y algunas implementaciones de xargs
) funcionan para secuencias arbitrarias de bytes (aunque no formen caracteres válidos en la configuración regional del usuario), para simplificar el espacio en blanco definición a solo SPC y TAB y para evitar problemas con diferentes interpretaciones de caracteres cuya codificación contiene la codificación de barra invertida por las diferentes utilidades.