İki kelime arasında metin çıkarmak için sed / grep nasıl kullanılır?


134

Bir dizenin iki kelimesi arasındaki her şeyi içeren bir dize çıkarmaya çalışıyorum:

giriş:

"Here is a String"

çıktı:

"is a"

Kullanımı:

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

uç noktaları içerir, ancak bunları dahil etmek istemiyorum.


8
Girdi ise sonuç ne olmalıdır Here is a Here String? Veya I Hereby Dub Thee Sir Stringy?
ghoti

5
Bilginize. Komutunuz, Here kelimesini içeren satır ile String kelimesini içeren satır arasındaki her şeyi yazdırmak anlamına gelir - istediğinizi değil.
Hai Vu

Diğer bir sık sedsorulan SSS ise "belirli satırlar arasında metni nasıl çıkarabilirim"; bu stackoverflow.com/questions/16643288/…
üçlü

Yanıtlar:


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

2
Teşekkürler! Ya "İşte bir Dize" de "Biri" ile "Dize" arasındaki her şeyi bulmak istersem? (sed -e 's / biri (. *) Dize / \ 1 /'?
user1190650

5
@ user1190650 "İşte bir" yi de görmek istiyorsanız bu işe yarayacaktır. Bunu test edebilirsiniz: echo "Here is a one is a String" | sed -e 's/one is\(.*\)String/\1/'. Sadece arasındaki bölümü ve "dize" "one olan" istiyorsanız, o zaman regex bütün satırla aynı yapmak gerekir: sed -e 's/.*one is\(.*\)String.*/\1/'. Sed'de, s/pattern/replacement/"her satırdaki" desen "yerine" değiştirme "deyin. Yalnızca "desen" ile eşleşen her şeyi değiştirecektir, bu nedenle tüm satırı değiştirmesini istiyorsanız, "desen" in tüm satırla eşleşmesini sağlamanız gerekir.
Brian Campbell

9
Bu, girdi olduğunda bozulurHere is a String Here is a String
Jay D

1
Bir vakanın çözümünü görmek harika olurdu: "İşte blah blah String İşte 1 a blah blah String İşte 2 a blash blash String" çıktısı yalnızca Here ile String arasındaki ilk alt dizeyi almalı "
Jay D

1
@JayD sed açgözlü olmayan eşleştirmeyi desteklemiyor, önerilen bazı alternatifler için bu soruya bakın .
Brian Campbell

180

GNU grep ayrıca olumlu ve olumsuz ileriye bakmayı ve geriye bakmayı da destekleyebilir: Sizin durumunuz için komut şöyle olacaktır:

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

Birden fazla kopyasını varsa Hereve string, ilk gelen eşleşmesi istediğinizi seçebilirsiniz Hereve son stringveya tek tek onları maç. Normal ifade açısından açgözlü eşleşme (ilk durum) veya açgözlü olmayan eşleşme (ikinci durum) olarak adlandırılır.

$ 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 

31
GNU grep'in -Pseçeneğinin grep* BSD'de bulunanlarda veya herhangi bir SVR4 (Solaris, vb.) İle birlikte gelenlerde bulunmadığına dikkat edin. FreeBSD'de, PCRE'yi destekleyen (ve ileriye / arkaya bakma) devel/pcreiçeren bağlantı noktasını kurabilirsiniz pcregrep. OSX'in eski sürümleri GNU grep kullanıyordu, ancak OSX Mavericks'te, -Pseçeneği içermeyen FreeBSD sürümünden türetilmiştir.
ghoti

1
Merhaba, Yalnızca farklı içeriği nasıl çıkarırım?
Durgesh Suthar

4
Bu işe yaramaz çünkü bitiş dizeniz "dizge" birden çok kez geçiyorsa , bir sonrakini değil, son geçtiği yeri alacaktır .
Buttle Butkus

6
Durumunda Here is a string a string, hem " is a " ve " is a string a "soru ihtiyaçlarına göre geçerli cevaplar (tırnak görmezden) vardır. Bunlardan hangisini istediğiniz size bağlıdır ve sonra cevap buna göre farklı olabilir. Her neyse, ihtiyacın için bu işe yarayacak:echo "Here is a string a string" | grep -o -P '(?<=Here).*?(?=string)'
anishsane

2
@BND, pcregrep'in çok satırlı arama özelliğini etkinleştirmeniz gerekiyor . echo $'Here is \na string' | grep -zoP '(?<=Here)(?s).*(?=string)'
anishsane

58

Kabul edilen yanıt, öncesinde Hereveya sonrasında olabilecek metni kaldırmaz String. Bu irade:

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

Temel fark, .*hemen önce Hereve sonra eklenmesidir String.


Cevabınız umut verici. Yine de bir sorun. Aynı satırda birden fazla String varsa, onu ilk görülen String'e nasıl çıkarabilirim? Teşekkürler
Mian Asbat Ahmad

@MianAsbatAhmad Nicelik belirleyiciyi ve *arasında açgözlü olmayan (veya tembel) yapmak istersiniz . Ancak, sed tarafından kullanılan normal ifade türü, bu Stackoverflow sorusuna göre tembel niceleyicileri ( hemen sonrasında ) desteklemez . Genellikle tembel bir nicelik belirteci uygulamak için, eşleştirmek istemediğiniz belirteç dışındaki her şeyle eşleşirsiniz, ancak bu durumda, yalnızca tek bir simge yoktur, onun bütün bir dizesi vardır . HereString?.*String
tekerlekli araç


Maalesef dizide satır sonları varsa bu işe yaramaz
Witalo Benicio

Olmaması gerekiyor. .satır sonlarıyla eşleşmiyor. Satır sonlarını eşleştirmek istiyorsanız, .gibi bir şeyle değiştirebilirsiniz [\s\s].
Wheeler

35

Sadece Bash'de dizeleri çıkarabilirsin :

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

Ve PCRE içeren bir GNU grepiniz varsa, sıfır genişlik iddiasını kullanabilirsiniz:

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

bu yöntem neden bu kadar yavaş? Bu yöntemi kullanarak büyük bir html sayfasını çıkarırken 10 saniye gibi sürer.
Adam Johns

@AdamJohns, hangi yöntem? PCRE biri mi? PCRE'nin ayrıştırılması oldukça karmaşıktır, ancak 10 saniye aşırı görünüyor. Endişeniz varsa, örnek kod içeren bir soru sormanızı ve uzmanların ne dediğini görmenizi öneririm .
ghoti

Sanırım benim için çok yavaştı çünkü çok büyük bir html dosyasının kaynağını bir değişkende tutuyordu. İçeriği dosyaya yazdığımda ve ardından dosyayı ayrıştırdığımda hız önemli ölçüde arttı.
Adam Johns

22

GNU awk aracılığıyla,

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

grep with -P( perl-regexp ) parametresi destekler \K, bu da önceden eşleşen karakterlerin atılmasına yardımcı olur. Bizim durumumuzda, önceden eşleşen dizge Hereson çıktıdan atıldı.

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

Çıktının olmasını is aistiyorsanız, aşağıdakileri deneyebilirsiniz,

$ 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

Bu şunun için işe yaramaz:, yerine echo "Here is a string dfdsf Here is a string" | awk -v FS="(Here|string)" '{print $2}'yalnızca döner @Avinash Rajis ais a is a
alper

20

Çok satırlı oluşları olan uzun bir dosyanız varsa, önce sayı satırlarını yazdırmak yararlıdır:

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

3
Teşekkürler! Benim durumumda çalışan tek çözüm budur (satır sonu olmayan tek bir dize yerine çok satırlı metin dosyası). Açıkçası, satır numaralandırmasız olması için içindeki -nseçeneğin catatlanması gerekir.
Jeffrey Lebowski

... bu durumda cattamamen ihmal edilebilir; sedbir dosyanın veya standart girdinin nasıl okunacağını bilir.
üçlü

9

Bu sizin için işe yarayabilir (GNU sed):

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

Bu, metnin her bir temsilini (bu örnekte Hereve String) bir satırsonu üzerindeki iki işaretçi arasında sunar ve metin içindeki yeni satırları korur.


7

Yukarıdaki çözümlerin tümü, son arama dizesinin dizede başka bir yerde tekrarlandığı eksikliklere sahiptir. En iyisi bir bash işlevi yazmayı buldum.

    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"

6

İki s komutunu kullanabilirsiniz

$ echo "Here is a String" | sed 's/.*Here//; s/String.*//'
 is a 

Ayrıca çalışır

$ echo "Here is a StringHere is a String" | sed 's/.*Here//; s/String.*//'
 is a

$ echo "Here is a StringHere is a StringHere is a StringHere is a String" | sed 's/.*Here//; s/String.*//'
 is a 

6

Anlamak sedKomutu için onu adım adım inşa etmeliyiz.

İşte orijinal metniniz

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

Ubstition seçeneği Hereile dizeyi kaldırmaya sçalışalım.sed

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

Bu noktada, ben kaldırmak mümkün olacağına inanıyorum Stringyanı

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

Ancak bu istediğiniz çıktı değil.

İki sed komutunu birleştirmek için -eseçeneği kullanın

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

Bu yardımcı olur umarım


4

Şunları kullanabilirsiniz \1( http://www.grymoire.com/Unix/Sed.html#uh-4 adresine bakın ):

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

Parantez içindeki içerikler olarak saklanacaktır \1.


Bu, arada bir şey çıkarmak yerine dizeleri kaldırır. Sed komutundaki "Merhaba" ifadesini "is" ile kaldırmayı deneyin ve "Merhaba a" çıktısını verecektir
Jonathan

1

Sorun. Depolanan Claws Mail iletilerim aşağıdaki şekilde sarılıyor ve Konu satırlarını çıkarmaya çalışıyorum:

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>

Bu konudaki A2 başına, iki kelime arasındaki metni çıkarmak için sed / grep nasıl kullanılır? aşağıdaki ilk ifade, eşleşen metin yeni satır içermediği sürece "çalışır":

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

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key

Ancak, çok sayıda varyantı denememe rağmen ( .+?; /s; ...), bunları çalıştıramadım:

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

Çözüm 1.

Metni Çıkarma Başına Farklı satırlardaki iki dize arasında metin

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

hangi verir

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

Çözüm 2. *

Başına Ben Yeni satır (\ n) yerini alabilir nasıl sed kullanarak?

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

yeni satırları boşlukla değiştirir.

Bunu A2 ile zincirleme, İki kelime arasında metin çıkarmak için sed / grep nasıl kullanılır? , anlıyoruz:

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

hangi verir

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

Bu varyant çift boşlukları kaldırır:

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

vererek

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

1
güzel macera :))
Alexandru-Mihai Manolescu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.