Bagaimana cara menggunakan sed / grep untuk mengekstrak teks di antara dua kata?


134

Saya mencoba menampilkan string yang berisi segala sesuatu di antara dua kata string:

memasukkan:

"Here is a String"

keluaran:

"is a"

Menggunakan:

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

termasuk titik akhir, tetapi saya tidak ingin memasukkannya.


8
Apa yang seharusnya menjadi hasil jika inputnya Here is a Here String? Atau I Hereby Dub Thee Sir Stringy?
ghoti

5
FYI. Perintah Anda berarti mencetak segala sesuatu antara baris yang memiliki kata Di sini dan baris yang memiliki kata String - bukan yang Anda inginkan.
Hai Vu

sedFAQ umum lainnya adalah "bagaimana saya bisa mengekstrak teks di antara baris tertentu"; ini adalah stackoverflow.com/questions/16643288/…
tripleee

Jawaban:


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

2
Terima kasih! Bagaimana jika saya ingin menemukan segalanya antara "satu adalah" dan "Tali" di "Ini adalah satu adalah Tali"? (sed -e 's / one adalah (. *) String / \ 1 /'?
user1190650

5
@ user1190650 Itu akan berfungsi jika Anda ingin melihat "Here is a" juga. Anda dapat menguji itu: echo "Here is a one is a String" | sed -e 's/one is\(.*\)String/\1/'. Jika Anda hanya ingin bagian antara "satu" dan "String", maka Anda perlu membuat regex cocok seluruh baris: sed -e 's/.*one is\(.*\)String.*/\1/'. Selain itu, s/pattern/replacement/ucapkan "gantikan 'penggantian' untuk 'pola' di setiap baris". Itu hanya akan mengubah apa pun yang cocok dengan "pola", jadi jika Anda ingin mengganti seluruh baris, Anda perlu membuat "pola" cocok dengan seluruh garis.
Brian Campbell

9
Ini rusak ketika inputnyaHere is a String Here is a String
Jay D

1
Akan lebih bagus untuk melihat solusi untuk sebuah kasus: "Ini adalah String bla bla. Berikut ini adalah Bla bla String. Berikut ini adalah 2 blash blash String". Output harus mengambil hanya substring pertama antara Here dan String "
Jay D

1
@JayD sed tidak mendukung pencocokan non-serakah, lihat pertanyaan ini untuk beberapa alternatif yang disarankan.
Brian Campbell

180

GNU grep juga dapat mendukung pandangan ke depan & belakang positif & negatif: Untuk kasus Anda, perintahnya adalah:

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

Jika ada beberapa kejadian Heredan string, Anda dapat memilih apakah Anda ingin mencocokkan dari yang pertama Heredan terakhir stringatau mencocokkannya satu per satu. Dalam hal regex, itu disebut sebagai pertandingan serakah (kasus pertama) atau pertandingan tidak serakah (kasus kedua)

$ 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
Perhatikan bahwa -Popsi GNU grep tidak ada dalam yang greptermasuk dalam * BSD, atau yang datang dengan SVR4 (Solaris, dll). Di FreeBSD, Anda dapat menginstal devel/pcreport yang termasukpcregrep , yang mendukung PCRE (dan lihat-depan / belakang). OSX versi lama menggunakan GNU grep, tetapi di OSX Mavericks, -Pberasal dari versi FreeBSD, yang tidak menyertakan opsi.
ghoti

1
Hai, Bagaimana cara mengekstrak konten yang berbeda saja?
Durgesh Suthar

4
Ini tidak berfungsi karena jika string akhir Anda "string" terjadi lebih dari satu kali, itu akan menjadi kejadian terakhir , bukan yang berikutnya kejadian .
Buttle Butkus

6
Dalam hal Here is a string a string, keduanya " is a " dan " is a string a "merupakan jawaban yang valid (abaikan tanda kutip), sesuai persyaratan pertanyaan. Tergantung pada Anda yang mana dari yang Anda inginkan dan kemudian jawabannya dapat berbeda. Bagaimanapun, untuk kebutuhan Anda, ini akan berhasil:echo "Here is a string a string" | grep -o -P '(?<=Here).*?(?=string)'
anishsane

2
@BND, Anda harus mengaktifkan fitur pencarian multi-baris pcregrep . echo $'Here is \na string' | grep -zoP '(?<=Here)(?s).*(?=string)'
anishsane

58

Jawaban yang diterima tidak menghapus teks yang bisa sebelum Hereatau sesudah String. Ini akan:

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

Perbedaan utama adalah penambahan .*segera sebelum Heredan sesudah String.


Jawaban Anda menjanjikan. Namun satu masalah. Bagaimana saya bisa mengekstraknya ke String yang terlihat pertama jika ada beberapa String di baris yang sama? Terima kasih
Mian Asbat Ahmad

@MianAsbatAhmad Anda ingin membuat *quantifier, antara Heredan String, tidak serakah (atau malas). Namun, jenis regex yang digunakan oleh sed tidak mendukung quantifiers malas ( ?segera setelah .*) menurut pertanyaan Stackoverflow ini . Biasanya untuk menerapkan quantifier malas, Anda hanya akan mencocokkan dengan segala sesuatu kecuali token yang tidak ingin Anda cocokkan, tetapi dalam hal ini, tidak hanya ada satu token, melainkan seluruh string String,.
kendaraan roda

Terima kasih, saya mendapatkan jawabannya menggunakan awk, stackoverflow.com/questions/51041463/...
Mian Asbat Ahmad

Sayangnya ini tidak berfungsi jika string memiliki jeda baris
Witalo Benicio

Itu tidak seharusnya. .tidak cocok dengan jeda baris. Jika Anda ingin mencocokkan jeda baris, Anda dapat menggantinya .dengan sesuatu seperti [\s\s].
kendaraan roda

35

Anda dapat menghapus string di Bash saja:

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

Dan jika Anda memiliki grep GNU yang mencakup PCRE , Anda dapat menggunakan pernyataan selebar nol:

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

mengapa metode ini sangat lambat? ketika melucuti halaman html besar menggunakan metode ini dibutuhkan sekitar 10 detik.
Adam Johns

@ AdamJohns, metode apa? Yang PCRE? PCRE cukup rumit untuk diurai, tetapi 10 detik tampak ekstrem. Jika Anda khawatir, saya sarankan Anda mengajukan pertanyaan termasuk kode contoh, dan lihat apa yang dikatakan para ahli.
ghoti

Saya pikir itu sangat lambat bagi saya karena memegang sumber file html yang sangat besar dalam suatu variabel. Ketika saya menulis konten ke file dan kemudian mengurai file kecepatannya meningkat secara dramatis.
Adam Johns

22

Melalui GNU awk,

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

grep dengan dukungan parameter -P( perl-regexp ) \K, yang membantu membuang karakter yang sebelumnya cocok. Dalam kasus kami, string yang sebelumnya cocok Heresehingga dibuang dari hasil akhir.

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

Jika Anda ingin hasilnya menjadi is amaka Anda bisa mencoba di bawah ini,

$ 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

Ini tidak berfungsi untuk :, echo "Here is a string dfdsf Here is a string" | awk -v FS="(Here|string)" '{print $2}'hanya mengembalikan is asebagai ganti is a is a@Avinash Raj
alper

20

Jika Anda memiliki file panjang dengan banyak multi-line ocurrences, ada baiknya untuk terlebih dahulu mencetak baris-baris angka:

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

3
Terima kasih! Ini adalah satu-satunya solusi yang bekerja dalam kasus saya (file teks berganda, daripada satu string tanpa jeda baris). Jelas, untuk memilikinya tanpa penomoran baris, -nopsi di catharus dihilangkan.
Jeffrey Lebowski

... dalam hal mana catdapat sepenuhnya dihilangkan; sedtahu cara membaca file atau input standar.
tripleee

9

Ini mungkin bekerja untuk Anda (sed GNU):

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

Ini menyajikan setiap representasi teks antara dua penanda (dalam hal ini Heredan String) pada baris baru dan mempertahankan baris baru dalam teks.


7

Semua solusi di atas memiliki kekurangan di mana string pencarian terakhir diulang di tempat lain dalam string. Saya menemukan yang terbaik untuk menulis fungsi 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"

6

Anda dapat menggunakan perintah dua s

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

Juga berfungsi

$ 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

Untuk memahami sedperintah, kita harus membangunnya langkah demi langkah.

Ini teks asli Anda

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

Mari kita coba hapus Herestring dengan sopsi ubstition dised

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

Pada titik ini, saya percaya Anda akan dapat menghapus Stringjuga

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

Tapi ini bukan output yang Anda inginkan.

Untuk menggabungkan dua perintah sed, gunakan -eopsi

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

Semoga ini membantu


4

Anda dapat menggunakan \1(lihat http://www.grymoire.com/Unix/Sed.html#uh-4 ):

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

Konten yang ada di dalam kurung akan disimpan sebagai \1.


Ini menghilangkan string alih-alih menghasilkan sesuatu di antaranya. Coba hapus "Hello" dengan "is" di perintah sed dan itu akan menampilkan "Hello a"
Jonathan

1

Masalah. Pesan Claws Mail saya yang tersimpan dibungkus sebagai berikut, dan saya mencoba untuk mengekstrak baris Subjek:

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>

Per A2 di utas ini, Bagaimana cara menggunakan sed / grep untuk mengekstrak teks antara dua kata? ekspresi pertama, di bawah, "berfungsi" selama teks yang cocok tidak mengandung baris baru:

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

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key

Namun, walaupun telah mencoba banyak varian ( .+?; /s; ...), saya tidak bisa membuatnya berfungsi:

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

Solusi 1.

Per Ekstrak teks antara dua string pada baris yang berbeda

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

pemberian yang mana

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

Solusi 2. *

Per Bagaimana saya bisa mengganti baris baru (\ n) menggunakan sed?

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

akan mengganti baris baru dengan spasi.

Chaining itu dengan A2 di Cara menggunakan sed / grep untuk mengekstrak teks antara dua kata? , kita mendapatkan:

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

pemberian yang mana

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

Varian ini menghilangkan spasi ganda:

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

memberi

[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
petualangan menyenangkan :))
Alexandru-Mihai Manolescu
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.