Quiero encontrar archivos que tengan "abc" Y "efg" en ese orden, y esas dos cadenas están en líneas diferentes en ese archivo. Por ejemplo: un archivo con contenido:
blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
Debería coincidir.
Respuestas:
Grep no es suficiente para esta operación.
pcregrep, que se encuentra en la mayoría de los sistemas Linux modernos, puede usarse como
donde
-M
,--multiline
permitir que los patrones coincidan con más de una líneaTambién hay un pcre2grep más nuevo . Ambos son proporcionados por el proyecto PCRE .
pcre2grep está disponible para Mac OS X a través de puertos Mac como parte del puerto
pcre2
:y vía Homebrew como:
o para pcre2
pcre2grep también está disponible en Linux (Ubuntu 18.04+)
fuente
-M, --multiline
: permite que los patrones coincidan con más de una línea.'abc.*(\n|.)*?efg'
.*
->'abc(\n|.)*?efg'
para acortar la expresión regular (y ser pedante)pcregrep
facilita las cosas, perogrep
también funcionará. Por ejemplo, consulte stackoverflow.com/a/7167115/123695No estoy seguro de si es posible con grep, pero sed lo hace muy fácil:
fuente
sed
, pero si nunca antes había visto una expresión así.Aquí hay una solución inspirada en esta respuesta :
si 'abc' y 'efg' pueden estar en la misma línea:
si 'abc' y 'efg' deben estar en diferentes líneas:
Parámetros:
-z
Trate la entrada como un conjunto de líneas, cada una terminada por un byte cero en lugar de una nueva línea. es decir, grep trata la entrada como una línea grande.-l
imprimir el nombre de cada archivo de entrada desde el que normalmente se habría impreso la salida.(?s)
active PCRE_DOTALL, lo que significa que '.' encuentra cualquier personaje o nueva línea.fuente
l
. AFAIK no hay-1
opción de número .-z
opciones especifican grep para tratar las nuevas líneas comozero byte characters
entonces, ¿por qué necesitamos(?s)
la expresión regular? Si ya es un personaje que no.
es de nueva línea, ¿no debería poder emparejarlo directamente?sed debería ser suficiente como el póster LJ mencionado anteriormente,
en lugar de! d simplemente puede usar p para imprimir:
fuente
Confié mucho en pcregrep, pero con grep más nuevo no necesita instalar pcregrep para muchas de sus características. Solo usa
grep -P
.En el ejemplo de la pregunta del OP, creo que las siguientes opciones funcionan bien, con la segunda mejor coincidencia de cómo entiendo la pregunta:
Copié el texto como / tmp / test1 y eliminé la 'g' y lo guardé como / tmp / test2. Aquí está el resultado que muestra que el primero muestra la cadena coincidente y el segundo muestra solo el nombre del archivo (típico -o es para mostrar coincidencia y típico -l es para mostrar solo el nombre del archivo). Tenga en cuenta que la 'z' es necesaria para multilínea y '(. | \ N)' significa que coincide con 'cualquier cosa que no sea nueva línea' o 'nueva línea', es decir, cualquier cosa:
Para determinar si su versión es lo suficientemente nueva, ejecute
man grep
y vea si algo similar a esto aparece cerca de la parte superior:Eso es de GNU grep 2.10.
fuente
Esto se puede hacer fácilmente usando primero
tr
para reemplazar las nuevas líneas con algún otro carácter:Aquí, estoy usando el carácter de alarma
\a
(ASCII 7) en lugar de una nueva línea. Esto casi nunca se encuentra en su texto, ygrep
puede coincidir con.
o específicamente con\a
.fuente
\0
y, por lo tanto, necesitabagrep -a
y coincidía con\x00
... ¡Me ha ayudado a simplificar!echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'
es ahoraecho $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'
grep -o
.awk one-liner:
fuente
abc
hasta el final del archivo si el patrón final no está presente en el archivo o si falta el último patrón final. Puede solucionarlo, pero complicará la secuencia de comandos de manera bastante significativa./efg/
de la salida?Puede hacerlo muy fácilmente si puede usar Perl.
También puede hacerlo con una sola expresión regular, pero eso implica tomar todo el contenido del archivo en una sola cadena, lo que podría terminar ocupando demasiada memoria con archivos grandes. Para completar, aquí está ese método:
fuente
.*?
) para obtener una coincidencia mínima.No sé cómo haría eso con grep, pero haría algo así con awk:
Sin embargo, debes tener cuidado de cómo lo haces. ¿Desea que la expresión regular coincida con la subcadena o la palabra completa? agregue \ w etiquetas según corresponda. Además, si bien esto se ajusta estrictamente a la forma en que mencionó el ejemplo, no funciona cuando abc aparece por segunda vez después de efg. Si desea manejar eso, agregue un if según corresponda en el / abc / case, etc.
fuente
Lamentablemente, no puedes. De los
grep
documentos:fuente
grep -Pz
Si está dispuesto a usar contextos, esto podría lograrse escribiendo
Esto mostrará todo entre "abc" y "efg", siempre que estén dentro de 500 líneas entre sí.
fuente
Si necesita que ambas palabras estén cercanas entre sí, por ejemplo, no más de 3 líneas, puede hacer esto:
El mismo ejemplo pero solo filtrando archivos * .txt:
Y también puede reemplazar el
grep
comando con elegrep
comando si también desea encontrar con expresiones regulares.fuente
Lancé una alternativa grep hace unos días que admite esto directamente, ya sea a través de la coincidencia multilínea o usando condiciones; espero que sea útil para algunas personas que buscan aquí. Así es como se verían los comandos para el ejemplo:
Multilínea:
Condiciones:
También podría especificar que 'efg' debe seguir a 'abc' dentro de un cierto número de líneas:
Puede encontrar más información en sift-tool.org .
fuente
sift -lm 'abc.*efg' testfile
funcione, porque la coincidencia es codiciosa y engulle todas las líneas hasta la últimaefg
en el archivo.Si bien la opción sed es la más simple y fácil, la única frase de LJ no es la más portátil. Aquellos atrapados con una versión de C Shell deberán escapar de su explosión:
Esto desafortunadamente no funciona en bash et al.
fuente
fuente
puede usar grep en caso de que no le guste la secuencia del patrón.
ejemplo
grep -l
encontrará todos los archivos que coincidan con el primer patrón, y xargs buscará el segundo patrón. Espero que esto ayude.fuente
Con buscador de plata :
similar a la respuesta del portador del anillo, pero con ag en su lugar. Las ventajas de velocidad del buscador de plata posiblemente podrían brillar aquí.
fuente
(echo abctest; echo efg)|ag 'abc.*(\n|.)*efg'
no coincideUtilicé esto para extraer una secuencia fasta de un archivo multi fasta usando la opción -P para grep:
El núcleo de la expresión regular es el
[^>]
que se traduce como "no mayor que el símbolo"fuente
Como alternativa a la respuesta de Balu Mohan, es posible hacer cumplir la orden de los patrones usando solamente
grep
,head
ytail
:Sin embargo, este no es muy bonito. Formateado de forma más legible:
Esto imprimirá los nombres de todos los archivos donde
"pattern2"
aparece después"pattern1"
, o donde ambos aparecen en la misma línea :Explicación
tail -n +i
- imprime todas las líneas después dei
th, inclusivegrep -n
- anteponer líneas coincidentes con sus números de líneahead -n1
- imprime solo la primera filacut -d : -f 1
- imprima la primera columna cortada usando:
como delimitador2>/dev/null
-tail
salida de error de silencio que ocurre si el$()
expresión vuelve vacíagrep -q
- Silenciogrep
y regreso inmediatamente si se encuentra una coincidencia, ya que solo estamos interesados en el código de salidafuente
&>
? También lo estoy usando, pero nunca lo vi documentado en ningún lado. Por cierto, ¿por qué tenemos que silenciar grep de esa manera, en realidad?grep -q
no hará el truco también?&>
le dice a bash que redirija tanto la salida estándar como el error estándar, vea REDIRECCIÓN en el manual de bash. Tienes mucha razón en que podríamos hacerlo engrep -q ...
lugar degrep ... &>/dev/null
, ¡buena captura!¿Esto también debería funcionar?
$ARGV
contiene el nombre del archivo actual cuando se leenfile_list /s
búsquedas de modificadores en la nueva línea.fuente
El patrón de archivos
*.sh
es importante para evitar que los directorios sean inspeccionados. Por supuesto, algunas pruebas también podrían evitarlo.los
busca un máximo de 1 coincidencia y devuelve (-n) el número de lino. Si se encontró una coincidencia (prueba -n ...) encuentre la última coincidencia de efg (encuentre todo y tome la última con la cola -n 1).
De lo contrario continuar.
Como el resultado es algo así
18:foofile.sh String alf="abc";
, necesitamos cortar ":" hasta el final de la línea.Debería devolver un resultado positivo si la última coincidencia de la segunda expresión ha pasado la primera coincidencia de la primera.
Luego informamos el nombre del archivo
echo $f
.fuente
¿Por qué no algo tan simple como:
devuelve 0 o un entero positivo.
egrep -o (solo muestra coincidencias, truco: varias coincidencias en la misma línea producen una salida de varias líneas como si estuvieran en líneas diferentes)
grep -A1 abc
(imprima abc y la línea que sigue)grep efg | wc -l
(0-n recuento de líneas efg encontradas después de abc en la misma línea o en las siguientes, el resultado puede usarse en un 'si')grep se puede cambiar a egrep, etc. si se necesita la coincidencia de patrones
fuente
Si tiene alguna estimación acerca de la distancia entre las 2 cadenas 'abc' y 'efg' que está buscando, puede usar:
De esa manera, el primer grep devolverá la línea con el 'abc' más # num1 líneas después de él, y # num2 líneas después, y el segundo grep tamizará todos esos para obtener el 'efg'. Entonces sabrás en qué archivos aparecen juntos.
fuente
Con ugrep lanzado hace unos meses:
Esta herramienta está altamente optimizada para la velocidad. También es compatible con GNU / BSD / PCRE-grep.
Tenga en cuenta que deberíamos usar una repetición perezosa
+?
, a menos que desee hacer coincidir todas las líneasefg
juntas hasta la últimaefg
en el archivo.fuente
Esto debería funcionar:
Si hay más de una coincidencia, puede filtrar usando grep -v
fuente