
Estoy intentando escribir un bash
script que busque en el contenido de los archivos en un árbol de directorios específico la presencia de una subcadena específica.
Usar grep
la función recursiva de 's por sí sola no es suficiente, ya que potencialmente necesito iterar sobre el /
directorio (y todos los subdirectorios) de un sistema, lo que hace que grep
se quede sin memoria y aborte. Por lo tanto, decidí obtener una lista de todos los directorios y subdirectorios en el árbol de directorios especificado utilizando find
las siguientes variables que denotan los argumentos pasados al script.
searchdir=$HOME # passed in a script argument
searchstr="secret" # passed in a script argument
Llamo a la find
utilidad y almaceno el resultado en un archivo temporal.
TF=$(mktemp)
find ${searchdir} -type d 1>$TF 2>/dev/null
Con la lista de todos los directorios en el archivo temporal, procedo a iterar sobre las líneas de este archivo usando un while-do
bucle con la intención de realizar una búsqueda en todos los archivos en cada directorio. Para grep
, utilizo el formato de parámetros proporcionado enesta respuestapara buscar todos los archivos, incluidos los ocultos, en un único directorio.
cat $TF | while read line || [[ -n $line ]];
do
grepdir="${line}/{*,.*}"
grep -sHn "${searchstr}" ${grepdir}
done
... sin embargo, ese código no produce ningún resultado.
Comprobé que...
Contiene ${TF}
la lista correcta de todos los directorios. Generar la ${grepdir}
variable proporciona el resultado que espero encontrar.
/home/user/{*,.*}
/home/user/.ssh/{*,.*}
/home/user/test/{*,.*}
# ... and so on
Si ejecuto el grep
comando con un directorio codificado, particularmente el ~/test/
directorio, que contiene dos archivos de prueba con la cadena que se supone que debe encontrar
grep -sHn "${searchstr}" /home/user/test/{*,.*}
... genera correctamente los dos archivos que contienen la subcadena "secreto".
/home/user/test/asdf:7:secret
/home/user/test/test.txt:5:asdfasfdsecretaasdfafd
Un formato que me funciona es el que se menciona originalmente en elrespuesta que discute el uso recursivo degrep
. Si hago esto:
cat $TF | while read line || [[ -n $line ]];
do
grep -rn "${line}" -e "${searchstr}"
done
... Obtengo algunos resultados (técnicamente correctos, pero con muchas entradas duplicadas), pero dado que grep
está procesando los directorios de forma recursiva y tengo una lista de todos los directorios, seguramente obtendré los mismos resultados muchas veces y en directorios como el directorio raíz antes mencionado grep
fallará por completo, que es lo que estoy tratando de evitar.
Probablemente también debería mencionar que mis trucos desesperados para hacerlo funcionar, como pasar $(echo "${grepdir}")
el parámetro, tampoco produjeron resultados.
Lo más probable es que haya una idea errónea en mi forma de pensar o comprender bash
. ¿No debería bash
expandir la ${grepdir}
variable antes de realizar una llamada grep
? ¿Dónde va mal mi guión?
Respuesta1
Regla número 1: cuando un comando o secuencia de comandos no hace lo que usted desea,
mire los mensajes de error. No los arrojes /dev/null
.
Estás recibiendo mensajes de error como
grep: /home/user/{*,.*}: No such file or directory
grep: /home/user/.ssh/{*,.*}: No such file or directory
grep: /home/user/test/{*,.*}: No such file or directory
pero no los estás viendo.
si miramosfiesta(1), vemos
La expansión se realiza en la línea de comando después de dividirla en palabras. Se realizan siete tipos de expansión: expansión de llaves, expansión de tilde, expansión de parámetros y variables, sustitución de comandos, expansión aritmética, división de palabras y expansión de nombres de rutas.
El orden de las expansiones es: expansión de llaves; expansión de tilde, expansión de parámetros y variables, expansión aritmética y sustitución de comandos (realizada de izquierda a derecha); división de palabras; y expansión del nombre de ruta.
La parte importante para su situación es que la expansión de la llave ocurre antes de la expansión variable. Entonces, si dijiste
grep -sHn "${searchstr}" "${line}"/{*,.*}
entonces
- la expansión de llaves convertiría el último token en
"${line}"/*
y"${line}"/.*
, - la expansión variable convertiría lo anterior en
/home/user/*
y/home/user/.*
, y luego - La expansión del nombre de ruta convertiría lo anterior en una lista de nombres de archivos.
Pero, cuando dices
grep -sHn "${searchstr}" ${grepdir}
entonces
- La expansión variable convierte el último token en
/home/user/{*,.*}
,
y entonces ya es demasiado tarde para que se produzca la expansión del aparato ortopédico.
grep
busca un archivo llamado literalmente /home/user/{*,.*}
.
PD
grep -sHn "${searchstr}" "${line}/{*,.*}"
tampoco funcionaría, porque las comillas evitarían que se produjera la expansión de llaves y la expansión del nombre de ruta.
PPS No necesitas todos esos aparatos ortopédicos;
grep -sHn "$searchstr" "$line"/{*,.*}
estaría bien.
Respuesta2
La razón por la que grep aborta cuando se repite en todo el sistema probablemente no sea que no pueda hacer frente a la cantidad de datos, sino que tropiece con uno u otro pseudo archivo o archivo de dispositivo en /proc, /sys o /dev. Puede excluir los directorios infractores con la --exclude
opción en la línea de comando.
La razón por la que no expande los comodines es porque están citados en esta línea:
grepdir="${line}/{*,.*}"
Cambiarlo a esto probablemente ayudará a que se expandan.
grepdir="${line}/"{*,.*}
Otra forma de lograr esto (con menos secuencias de comandos de su parte) sería seleccionar los archivos usando find
y canalizando las rutas de los archivos para xargs
su procesamiento:find / ... -print 0 | xargs -0 ...
Sin embargo, de cualquier manera probablemente seguiría tropezando con cualquier archivo con el que tropezó el grep recursivo original, a menos que los excluya.