¿Cómo usar sed / grep para extraer texto entre dos palabras?

Estoy intentando dar salida a una cadena que contiene todo entre dos palabras de una cadena:

entrada:

"Here is a String" 

salida:

 "is a" 

Utilizando:

 sed -n '/Here/,/String/p' 

incluye los puntos finales, pero no quiero incluirlos.

 sed -e 's/Here\(.*\)String/\1/' 

Grep simple también puede admitir look-ahead y look-back positivos y negativos: para su caso, el comando sería:

  echo "Here is a string" | grep -o -P '(?<=Here).*(?=string)' 

Puedes desnudar cadenas solo en Bash :

 $ foo="Here is a String" $ foo=${foo##*Here } $ echo "$foo" is a String $ foo=${foo%% String*} $ echo "$foo" is a $ 

Y si tiene un grep de GNU que incluye PCRE , puede usar una aserción de ancho cero:

 $ echo "Here is a String" | grep -Po '(?<=(Here )).*(?= String)' is a 

La respuesta aceptada no elimina el texto que podría estar antes de Here o después de String . Esta voluntad:

 sed -e 's/.*Here\(.*\)String.*/\1/' 

La principal diferencia es la adición de .* Inmediatamente antes de Here y después de String .

A través de GNU awk,

 $ echo "Here is a string" | awk -v FS="(Here|string)" '{print $2}' is a 

grep con parámetro -P ( perl-regexp ) admite \K , lo que ayuda a descartar los caracteres coincidentes previamente. En nuestro caso, la cadena coincidente previamente estaba Here por lo que se descartó de la salida final.

 $ echo "Here is a string" | grep -oP 'Here\K.*(?=string)' is a $ echo "Here is a string" | grep -oP 'Here\K(?:(?!string).)*' is a 

Si quieres que la salida sea is a entonces puedes probar la siguiente,

 $ echo "Here is a string" | grep -oP 'Here\s*\K.*(?=\s+string)' is a $ echo "Here is a string" | grep -oP 'Here\s*\K(?:(?!\s+string).)*' is a 

Si tiene un archivo largo con muchas ocurrencias de varias líneas, es útil imprimir primero las líneas numéricas:

 cat -n file | sed -n '/Here/,/String/p' 

Esto podría funcionar para usted (GNU sed):

 sed '/Here/!d;s//&\n/;s/.*\n//;:a;/String/bb;$!{n;ba};:b;s//\n&/;P;D' file 

Esto presenta cada representación de texto entre dos marcadores (en este caso Here y String ) en una nueva línea y preserva las nuevas líneas dentro del texto.

Todas las soluciones anteriores tienen deficiencias en las que la última cadena de búsqueda se repite en otra parte de la cadena. Me pareció mejor escribir una función bash.

  function str_str { local str str="${1#*${2}}" str="${str%%$3*}" echo -n "$str" } # test it ... mystr="this is a string" str_str "$mystr" "this " " string" 

Puede usar \1 (consulte http://www.grymoire.com/Unix/Sed.html#uh-4 ):

 echo "Hello is a String" | sed 's/Hello\(.*\)String/\1/g' 

El contenido que está dentro de los corchetes se almacenará como \1 .

Problema. Mis mensajes de Claws Mail almacenados se envuelven de la siguiente manera, y estoy tratando de extraer las líneas de Asunto:

 Subject: [SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular link in major cell growth pathway: Findings point to new potential therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway identified [Lysosomal amino acid transporter SLC38A9 signals arginine sufficiency to mTORC1]] Message-ID: <20171019190902.18741771@VictoriasJourney.com> 

Por A2 en este hilo, ¿Cómo usar sed / grep para extraer texto entre dos palabras? la primera expresión, a continuación, “funciona” siempre que el texto coincidente no contenga una nueva línea:

 grep -o -P '(?<=Subject: ).*(?=molecular)' corpus/01 [SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key 

Sin embargo, a pesar de probar numerosas variantes ( .+?; /s; ... ), no pude hacer que funcionaran:

 grep -o -P '(?<=Subject: ).*(?=link)' corpus/01 grep -o -P '(?<=Subject: ).*(?=therapeutic)' corpus/01 etc. 

Solución 1.

Por Extraer texto entre dos cadenas en diferentes líneas

 sed -n '/Subject: /{:a;N;/Message-ID:/!ba; s/\n/ /g; s/\s\s*/ /g; s/.*Subject: \|Message-ID:.*//g;p}' corpus/01 

lo que da

 [SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular link in major cell growth pathway: Findings point to new potential therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway identified [Lysosomal amino acid transporter SLC38A9 signals arginine sufficiency to mTORC1]] 

Solución 2. *

¿Cómo puedo reemplazar una nueva línea (\ n) usando sed?

 sed ':a;N;$!ba;s/\n/ /g' corpus/01 

reemplazará las nuevas líneas con un espacio.

Encadenando eso con A2 en Cómo usar sed / grep para extraer texto entre dos palabras? , obtenemos:

 sed ':a;N;$!ba;s/\n/ /g' corpus/01 | grep -o -P '(?<=Subject: ).*(?=Message-ID:)' 

lo que da

 [SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular link in major cell growth pathway: Findings point to new potential therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway identified [Lysosomal amino acid transporter SLC38A9 signals arginine sufficiency to mTORC1]] 

Esta variante elimina espacios dobles:

 sed ':a;N;$!ba;s/\n/ /g; s/\s\s*/ /g' corpus/01 | grep -o -P '(?<=Subject: ).*(?=Message-ID:)' 

dando

 [SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular link in major cell growth pathway: Findings point to new potential therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway identified [Lysosomal amino acid transporter SLC38A9 signals arginine sufficiency to mTORC1]]