¿Cómo busco una cadena en un grupo de archivos cuando algunos de esos archivos contienen espacios?

2

Estoy ejecutando Mac 10.7.5. En una terminal, usaré este comando para encontrar archivos con cadenas

find src/main -name "*" | xargs grep -i 'mystring'

Sin embargo, cuando los archivos contienen espacios, obtendré resultados como

grep: images/mystuff/essential-questions-selected-arrow-ela: No such file or directory
grep: 5.24.38: No such file or directory
grep: PM.png: No such file or directory

El archivo real en el ejemplo anterior es "essential-questions-selected-arrow-ela 5.24.38 PM.png" ¿Cómo ejecuto el comando anterior correctamente, incluso si los archivos que se buscan contienen espacios?

Dave A
fuente

Respuestas:

4

Si quieres usar find salida en xargs, la forma recomendada es canalizarlo usando NUL Caracteres para delimitar cada nombre de archivo:

find src/main -name "*" -print0 | xargs -0 grep -i 'mystring'

La razón de esto es que la concha (y xargs específicamente) en general divide los argumentos (o entrada en el caso de xargs ) basado en espacios en blanco, es decir, espacios, pestañas y nuevas líneas. Una vez que usas -0, xargs leerá cada campo separado por una NUL, Que es que find -print0 salidas

Esto funcionará en GNU. find y xargs así como las versiones incluidas en OS X. Otras versiones pueden no tener las opciones, ya que no son necesarias en POSIX.


Pero, de nuevo, esto no es realmente necesario. los "*" El patrón de nombre se expande a todos los nombres posibles. grep puede atender por sí solo, por lo que todo lo que se necesita es:

grep -ri 'mystring' src/main

En Bash 4 (no se incluye con OS X de forma predeterminada) también puede hacer globos recursivos, por ejemplo. en general .txt archivos, usando la globstar opción :

shopt -s globstar
grep -i 'mystring' **/*.txt
slhck
fuente
buen consejo. No creo que MacOS use herramientas GNU. @daveA, revisa tu página de manual para buscar y xargs para opciones que te permitan separar los resultados con un carácter que no sea un espacio en blanco.
glenn jackman
1
Las variantes BSD de find y xargs incluido en el soporte OS X la -print0 y -0 opciones, respectivamente.
slhck
2

De lejos, la forma más segura y fácil de hacer este tipo de cosas es usar find's -exec opción. Desde man find:

-exec utility [argument ...] ;
     True if the program named utility returns a zero value as its exit status.
     Optional arguments may be passed to the utility.  The expression must be 
     terminated by a semicolon (``;'').  If you invoke find from a shell you 
     may need to quote the semicolon if the shell would otherwise treat it as a 
     control operator.  If the string ``{}'' appears anywhere in the utility 
     name or the arguments it is replaced by the pathname of the current file.  
     Utility will be executed from the directory from which find was executed.  
     Utility and arguments are not subject to the further expansion of shell 
     patterns and constructs.

-exec utility [argument ...] {} +
     Same as -exec, except that ``{}'' is replaced with as many pathnames as 
     possible for each invo-cation invocationcation of utility. This behaviour 
     is similar to that of xargs(1).

En otras palabras, la -exec La opción ejecutará lo que le des en los resultados de buscar, reemplazando {} con cada archivo (o directorio) encontrado. Entonces, para grep para una cadena en particular, harías:

find src/main -name "*" -exec grep -i 'mystring' {} +

Eso, sin embargo, también encontrará directorios y dará un error. Funcionará, fíjate, solo se quejará cuando intentes ejecutarlo en un directorio, habrías tenido el mismo problema usando xargs. Lo que realmente intentas hacer aquí es encontrar todos los archivos y solo los archivos. En ese caso, el -name '*' no es necesario ya que find src/main es exactamente lo mismo que find src/main -name "*". Entonces, en lugar de usar eso, especifique que solo desea buscar archivos:

find src/main -type f -exec grep -i 'mystring' {} +
terdon
fuente
Necesitas escapar o una sola cita ; o +. De lo contrario serían consumidos por la cáscara, no find.
slhck
@slhck al menos en bash y Linux, no hay razón para escapar +, no tiene un significado especial para el shell, que es una de las razones por las que usé eso en lugar de ;.
terdon