Tengo un directorio (por ejemplo, abc/def/efg
) con muchos subdirectorios (por ejemplo ,:) abc/def/efg/(1..300)
. Todos estos subdirectorios tienen un archivo común (por ejemplo, file.txt
). Quiero buscar una cadena solo en esto, file.txt
excluyendo otros archivos. ¿Cómo puedo hacer esto?
Solía grep -arin "pattern" *
, pero es muy lento si tenemos muchos subdirectorios y archivos.
command-line
grep
find
Rajesh Keladimath
fuente
fuente
Respuestas:
En el directorio principal, puede usar
find
y luego ejecutargrep
solo esos archivos:fuente
-H
agrep
fin de que, en los casos en un solo camino se pasa a ella, ese camino todavía se imprime (en lugar de sólo las líneas coincidentes desde el archivo).También es posible usar Globstar.
La construcción de
grep
comandos confind
, al igual que en la respuesta de Zanna , es un versátil, y de manera muy robusta y portátil para hacer esto (véase también la respuesta de sudodus ). Y Muru ha publicado un excelente enfoque de usargrep
's--include
opción . Pero si desea utilizar sólo elgrep
mando y su concha, hay otra manera de hacerlo - usted puede hacer el depósito propiamente realizar la recursividad es necesario :Las
-H
marcas de banderagrep
muestran el nombre del archivo, incluso si sólo se encuentra un archivo coincidente. Puede pasar el-a
,-i
y-n
banderas (de su ejemplo) paragrep
, así, si eso es lo que necesita. Pero no deje pasar-r
o-R
cuando se utiliza este método. Es la cáscara que recursivamente directorios en la expansión del patrón global que contiene**
, y nogrep
.Estas instrucciones son específicas del shell Bash. Bash es el shell de usuario predeterminado en Ubuntu (y la mayoría de los otros sistemas operativos GNU / Linux), por lo que si está en Ubuntu y no sabe cuál es su shell, es casi seguro que sea Bash. Aunque los shells populares generalmente admiten
**
globos de desplazamiento de directorio , no siempre funcionan de la misma manera. Para obtener más información, consulte la excelente respuesta de Stéphane Chazelas a El resultado de ls *, ls ** y ls *** en Unix.SE .Cómo funciona
Encendido de la Globstar golpe opción shell hace
**
caminos de los partidos que contienen el separador de directorio (/
). Por lo tanto, es un globo recurrente de directorio. Específicamente, comoman bash
explica:Debes tener cuidado con esto, ya que puedes ejecutar comandos que modifiquen o eliminen muchos más archivos de los que pretendes, especialmente si escribes
**
cuando querías escribir*
. (Es seguro en este comando, que no cambia ningún archivo).shopt -u globstar
Desactiva la opción de shell globstar.Hay algunas diferencias prácticas entre globstar y
find
.find
Es mucho más versátil que Globstar. Cualquier cosa que puedas hacer con globstar, también puedes hacerlo con elfind
comando. Me gusta globstar, y a veces es más conveniente, pero globstar no es una alternativa generalfind
.El método anterior no busca dentro de los directorios cuyos nombres comienzan con a
.
. A veces no desea repetir estas carpetas, pero a veces sí.Al igual que con un globo ordinario, el shell construye una lista de todas las rutas coincidentes y las pasa como argumentos a su comando (
grep
) en lugar del globo mismo. Si tiene tantos archivos llamadosfile.txt
que el comando resultante sería demasiado largo para que el sistema se ejecute, entonces el método anterior fallará. En la práctica, necesitaría (al menos) miles de esos archivos, pero podría suceder.Los métodos que utilizan
find
no están sujetos a esta restricción, porque:La manera de Zanna construye y ejecuta un
grep
comando con potencialmente muchos argumentos de ruta. Pero si se encuentran más archivos de los que se pueden enumerar en una sola ruta, la acción+
terminada-exec
ejecuta el comando con algunas de las rutas, luego lo ejecuta nuevamente con algunas rutas más, y así sucesivamente. En el caso degrep
una cadena en varios archivos, esto produce el comportamiento correcto.Al igual que el método globstar cubierto aquí, imprime todas las líneas coincidentes, con rutas antepuestas a cada una.
el camino de sudodus se ejecuta
grep
por separado para cadafile.txt
encontrado. Si hay muchos archivos, puede ser más lento que otros métodos, pero funciona.Ese método encuentra archivos e imprime sus rutas, seguido de líneas coincidentes, si las hay. Este es un formato de salida diferente del formato producido por mi método, el de Zanna y el de muru .
Obtener color con
find
Uno de los beneficios inmediatos de usar globstar es que, por defecto en Ubuntu,
grep
producirá resultados coloreados. Pero se puede conseguir fácilmente con estefind
, también .Las cuentas de usuario en Ubuntu se crean con un alias que hace que
grep
realmente se ejecutegrep --color=auto
(ejecutaralias grep
para ver). Es bueno que los alias se expandan prácticamente solo cuando los emite de forma interactiva , pero significa que si deseafind
invocargrep
con la--color
bandera, tendrá que escribirla explícitamente. Por ejemplo:fuente
bash
shell para que esto funcione. Usted no lo dice implícitamente en "la opción del shell Globstar fiesta" pero se puede pasar por alto fácilmente por la gente que lee demasiado rápido.**
, su crítica principal es correcta: la presentación de**
esta respuesta es específica de bash, siendo shopt solo bash y el término "globstar" es (creo) bash y solo tcsh. Había pasado por alto esto originalmente debido a esas complejidades, pero tienes razón en que es algo confuso. En lugar de discutirlo detenidamente en esta respuesta, me he vinculado a otra publicación (bastante exhaustiva) que hace el trabajo pesado.-e
que no debe aplicarse a las rutas, pero esto se soluciona fácilmente. Para el primer comando, simplemente omita-e
. Para el segundo, usefind . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;
ofind . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;
. Los usuarios a veces preferirán su camino (con-e
uso fijo) a los demás, que imprimen una ruta por línea coincidente ; el suyo imprime una ruta por archivo encontrado seguido degrep
resultados.grep
sí mismo , no hará lo que estás haciendo. Algunas otras críticas también estaban equivocadas.grep -H
ejecutado por-exec
no se coloreará sin--color
(oGREP_COLOR
). IEEE 1003.1-2008 no garantiza la{}
expansión##### {}:
, pero Ubuntu tiene GNU find, que lo hace . Si está bien con usted , editaré su publicación para corregir el-e
error (y aclarar su caso de uso) y podrá ver si desea recuperarla. (Tengo el representante para ver / editar publicaciones eliminadas).No necesitas
find
para esto;grep
puede manejar esto perfectamente bien por sí solo:De
man grep
:fuente
find?
El método dado en la respuesta de muru , de correr
grep
con la--include
bandera para especificar un nombre de archivo, es a menudo la mejor opción. Sin embargo, esto también se puede hacer confind
.El enfoque de esta respuesta se
find
ejecutagrep
por separado para cada archivo encontrado e imprime la ruta a cada archivo exactamente una vez , por encima de las líneas coincidentes encontradas en cada archivo. (Los métodos que imprimen la ruta delante de cada línea coincidente están cubiertos en otras respuestas).Puede cambiar el directorio a la parte superior del árbol de directorios donde tiene esos archivos. Entonces corre:
Eso imprime la ruta (en relación con el directorio actual
.
, e incluye el nombre del archivo) de cada archivo nombradofile.txt
, seguido de todas las líneas coincidentes en el archivo. Esto funciona porque{}
es un marcador de posición para el archivo encontrado. La ruta de cada archivo se separa de su contenido con un prefijo#####
y se imprime solo una vez, antes de las líneas coincidentes de ese archivo. (Los archivos llamadosfile.txt
que no contienen coincidencias aún tienen sus rutas impresas). Puede encontrar este resultado menos abarrotado que el que obtiene de los métodos que imprimen una ruta al comienzo de cada línea coincidente.Usarlo de
find
esta manera casi siempre será más rápido que ejecutarlogrep
en cada archivo (grep -arin "pattern" *
), porquefind
busca los archivos con el nombre correcto y omite todos los demás.Ubuntu usa GNU find , que siempre se expande
{}
incluso cuando aparece en una cadena más grande , como##### {}:
. Si necesita su comando para trabajarfind
en sistemas que pueden no admitir esto , o prefiere usar la-exec
acción solo cuando sea absolutamente necesario, puede usar:Para que la salida sea más fácil de leer , puede usar secuencias de escape ANSI para obtener nombres de archivos coloreados. Esto hace que el encabezado de la ruta de cada archivo se destaque mejor de las líneas coincidentes que se imprimen debajo:
Eso hace que su shell convierta el código de escape para el verde en la secuencia de escape real que produce el verde en un terminal, y haga lo mismo con el código de escape para el color normal. Se pasan estos escapes
find
, que los usan cuando imprime un nombre de archivo. (la$'
'
cita es necesaria aquí porquefind
la-printf
acción no reconoce\e
para interpretar códigos de escape ANSI).Si lo prefiere, puede utilizarlo
-exec
con elprintf
comando del sistema (que sí es compatible\e
). Entonces, otra forma de hacer lo mismo es:fuente
find abc/def/efg -name "file.txt" -type f -exec echo -e "##### {}:" \; -exec grep -i "pattern" {} \;
cd abc/def/efg
comando 'cambiar directorio' :-)-e
opciónecho
? Eso hará que destruya cualquier nombre de archivo que contenga barras invertidas. (2) No se garantiza que el uso{}
como parte de un argumento funcione. Sería mejor decir-exec echo "#####" {} \;
o-exec printf "##### %s:\n" {} \;
. (3) ¿Por qué no solo usar-print
o-printf
? (4) Considere tambiéngrep -H
.find . -name "file.txt" -type f -exec echo -e "\0033[32m{}:\0033[0m" \; -exec grep -i "pattern" {} \;
2) Puede que tengas razón, pero hasta ahora esto está funcionando para mí. 3) -print y -printf también son alternativas. 4) Esto ya está allí en la respuesta principal. - De todos modos, eres bienvenido con tu propia respuesta :-)-exec
llamadas. Simplemente usegrep -H
y eso imprimirá el nombre del archivo (en color), así como el texto coincidente.Solo para señalar que si las condiciones de la pregunta pueden tomarse como literarias, puede usar grep directo:
o
fuente