web-dev-qa-db-fra.com

Comment utiliser sed / grep pour extraire du texte entre deux mots?

J'essaie de sortir une chaîne qui contient tout ce qui se trouve entre deux mots d'une chaîne:

contribution:

"Here is a String"

sortie:

"is a"

En utilisant:

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

inclut les points finaux, mais je ne veux pas les inclure.

101
user1190650
sed -e 's/Here\(.*\)String/\1/'
85
Brian Campbell

GNU grep peut également prendre en charge l’anticipation positive et négative. Dans votre cas, la commande serait la suivante:

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

S'il existe plusieurs occurrences de Here et string, vous pouvez choisir de faire correspondre le premier Here et le dernier string ou de les faire correspondre individuellement. En termes de regex, il est appelé match glouton (premier cas) ou match non glouton (deuxième cas)

$ echo 'Here is a string, and Here is another string.' | grep -oP '(?<=Here).*(?=string)' # Greedy match
 is a string, and Here is another 
$ echo 'Here is a string, and Here is another string.' | grep -oP '(?<=Here).*?(?=string)' # Non-greedy match (Notice the '?' after '*' in .*)
 is a 
 is another 
147
anishsane

La réponse acceptée ne supprime pas le texte qui pourrait être avant Here ou après String. Cette volonté:

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

La principale différence est l'ajout de .* immédiatement avant Here et après String.

42
wheeler

Vous pouvez effacer les chaînes dans Bash seul:

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

Et si vous avez un GNU grep qui inclut PCRE , vous pouvez utiliser une assertion de largeur nulle:

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

À travers GNU awk,

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

grep avec -P (Perl-regexp) prend en charge \K, ce qui permet de supprimer les caractères précédemment correspondants. Dans notre cas, la chaîne précédemment mise en correspondance était Here et a donc été supprimée de la sortie finale.

$ 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 vous voulez que la sortie soit is a, alors vous pouvez essayer ce qui suit,

$ 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
20
Avinash Raj

Si vous avez un fichier long avec plusieurs occurrences multilignes, il est utile d’imprimer d’abord les lignes numériques:

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

Cela pourrait fonctionner pour vous (GNU sed):

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

Ceci présente chaque représentation de texte entre deux marqueurs (dans ce cas Here et String) sur une nouvelle ligne et conserve les nouvelles lignes dans le texte.

8
potong

Toutes les solutions ci-dessus ont des défauts dans lesquels la dernière chaîne de recherche est répétée ailleurs dans la chaîne. Je trouvais préférable d'écrire une fonction 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"
5
Gary Dean

Vous pouvez utiliser \1 (voir http://www.grymoire.com/Unix/Sed.html#uh-4 ):

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

Le contenu entre crochets sera stocké sous le nom \1.

3
mvairavan

Pour comprendre la commande sed, nous devons la construire étape par étape.

Voici votre texte original

user@linux:~$ echo "Here is a String"
Here is a String
user@linux:~$ 

Essayons de supprimer Here avec l'option substition dans sed.

user@linux:~$ echo "Here is a String" | sed 's/Here //'
is a String
user@linux:~$ 

À ce stade, je pense que vous pourrez supprimer String ainsi

user@linux:~$ echo "Here is a String" | sed 's/String//'
Here is a
user@linux:~$ 

Mais ce n'est pas votre sortie souhaitée.

Pour combiner deux commandes sed, utilisez l’option -e

user@linux:~$ echo "Here is a String" | sed -e 's/Here //' -e 's/String//'
is a
user@linux:~$ 

J'espère que cela t'aides

2
Sabrina

Problème. Mes messages Claws Mail stockés sont emballés comme suit et j'essaie d'extraire les lignes Objet:

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: <[email protected]>

Par A2 dans ce fil de discussion, Comment utiliser sed/grep pour extraire du texte entre deux mots? la première expression, ci-dessous, "fonctionne" tant que le texte correspondant ne contient pas de nouvelle ligne:

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

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key

Cependant, malgré l’essai de nombreuses variantes (.+?; /s; ...), je n’arrivais pas à les faire fonctionner:

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

Solution 1.

Per Extrait le texte entre deux chaînes sur des lignes différentes

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

qui donne

[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]]                              

Solution 2. *

Per Comment puis-je remplacer une nouvelle ligne (\ n) avec sed?

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

remplacera les nouvelles lignes par un espace.

En chaînant cela avec A2 dans Comment utiliser sed/grep pour extraire du texte entre deux mots? , on obtient:

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

qui donne

[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]] 

Cette variante supprime les doubles espaces:

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

donnant

[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]]
0
Victoria Stuart