Làm cách nào để sử dụng sed / grep để trích xuất văn bản giữa hai từ?


134

Tôi đang cố gắng xuất ra một chuỗi chứa mọi thứ giữa hai từ của một chuỗi:

đầu vào:

"Here is a String"

đầu ra:

"is a"

Sử dụng:

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

bao gồm các điểm cuối, nhưng tôi không muốn bao gồm chúng.


8
Điều gì sẽ là kết quả nếu đầu vào là Here is a Here String? Hay là I Hereby Dub Thee Sir Stringy?
ghoti

5
FYI. Lệnh của bạn có nghĩa là in mọi thứ giữa dòng có chữ Here và dòng có chữ String - không phải thứ bạn muốn.
Hải Vũ

Câu sedhỏi thường gặp khác là "làm cách nào tôi có thể trích xuất văn bản giữa các dòng cụ thể"; đây là stackoverflow.com/questions/16643288/
Mạnh

Câu trả lời:


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

2
Cảm ơn! Điều gì xảy ra nếu tôi muốn tìm mọi thứ giữa "một là" và "Chuỗi" trong "Đây là một chuỗi"? (sed -e 's / one là (. *) Chuỗi / \ 1 /'?
user1190650

5
@ user1190650 Điều đó sẽ hoạt động nếu bạn muốn xem "Đây là một". Bạn có thể kiểm tra nó : echo "Here is a one is a String" | sed -e 's/one is\(.*\)String/\1/'. Nếu bạn chỉ muốn phần giữa "một là" và "Chuỗi", thì bạn cần làm cho biểu thức chính quy khớp với toàn bộ dòng : sed -e 's/.*one is\(.*\)String.*/\1/'. Trong sed, s/pattern/replacement/nói "thay thế 'thay thế' cho 'mẫu' trên mỗi dòng". Nó sẽ chỉ thay đổi bất cứ thứ gì phù hợp với "mẫu", vì vậy nếu bạn muốn nó thay thế toàn bộ dòng, bạn cần làm cho "mẫu" khớp với toàn bộ dòng.
Brian Campbell

9
Điều này bị phá vỡ khi đầu vào làHere is a String Here is a String
Jay D

1
Sẽ rất tuyệt khi thấy giải pháp cho một trường hợp: "Đây là chuỗi blah blah Đây là 1 chuỗi blah blah Đây là 2 chuỗi blash blash" chỉ nên chọn chuỗi con đầu tiên giữa Here và String "
Jay D

1
@JayD sed không hỗ trợ kết hợp không tham lam, hãy xem câu hỏi này để biết một số lựa chọn thay thế được đề xuất.
Brian Campbell

179

GNU grep cũng có thể hỗ trợ hướng nhìn tích cực & tiêu cực & nhìn lại: Đối với trường hợp của bạn, lệnh sẽ là:

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

Nếu có nhiều lần xuất hiện Herestring, bạn có thể chọn xem bạn muốn khớp từ lần đầu tiên Herevà lần cuối stringhoặc kết hợp chúng riêng lẻ. Về mặt regex, nó được gọi là kết hợp tham lam (trường hợp thứ nhất) hoặc kết hợp không tham lam (trường hợp thứ hai)

$ 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
Lưu ý rằng -Ptùy chọn của GNU grep không tồn tại greptrong * BSD hoặc các tùy chọn đi kèm với bất kỳ SVR4 (Solaris, v.v.). Trong FreeBSD, bạn có thể cài đặt devel/pcrecổng bao gồm pcregrep, hỗ trợ PCRE (và nhìn về phía trước / phía sau). Các phiên bản cũ hơn của OSX đã sử dụng GNU grep, nhưng trong OSX Mavericks, -Pcó nguồn gốc từ phiên bản FreeBSD, không bao gồm tùy chọn.
ghoti

1
Xin chào, làm thế nào để tôi chỉ trích xuất nội dung khác biệt?
Durgesh Suthar

4
Điều này không hoạt động bởi vì nếu chuỗi kết thúc của bạn "chuỗi" xảy ra nhiều lần, nó sẽ có lần xuất hiện cuối cùng , không phải lần xuất hiện tiếp theo .
Butussy Butkus

6
Trong trường hợp Here is a string a string, cả hai " is a "" is a string a "là câu trả lời hợp lệ (bỏ qua các trích dẫn), theo yêu cầu câu hỏi. Nó phụ thuộc vào bạn mà một trong những bạn muốn và sau đó trả lời có thể khác nhau cho phù hợp. Dù sao, đối với yêu cầu của bạn, điều này sẽ hoạt động:echo "Here is a string a string" | grep -o -P '(?<=Here).*?(?=string)'
anishsane

2
@BND, bạn cần kích hoạt tính năng tìm kiếm nhiều dòng của pcregrep . echo $'Here is \na string' | grep -zoP '(?<=Here)(?s).*(?=string)'
anishsane

58

Câu trả lời được chấp nhận không xóa văn bản có thể trước Herehoặc sau String. Điều này sẽ:

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

Sự khác biệt chính là việc bổ sung .*ngay trước Herevà sau String.


Câu trả lời của bạn đầy hứa hẹn. Một vấn đề mặc dù. Làm cách nào tôi có thể trích xuất nó thành Chuỗi được nhìn thấy đầu tiên nếu có nhiều Chuỗi trong cùng một dòng? Cảm ơn
Mian Asbat Ahmad

@MianAsbatAhmad Bạn muốn tạo bộ *định lượng, giữa HereString, không tham lam (hoặc lười biếng). Tuy nhiên, loại regex được sử dụng bởi sed không hỗ trợ các bộ định lượng lười biếng ( ?ngay sau đó .*) theo câu hỏi Stackoverflow này . Thông thường để triển khai một bộ định lượng lười biếng, bạn sẽ chỉ khớp với mọi thứ trừ mã thông báo mà bạn không muốn khớp, nhưng trong trường hợp này, không chỉ có một mã thông báo, thay vào đó là toàn bộ chuỗi , String.
xe

Cảm ơn, tôi đã nhận được câu trả lời bằng cách sử dụng awk, stackoverflow.com/questions/51041463/ trên
Mian Asbat Ahmad

Thật không may, điều này không hoạt động nếu chuỗi bị đứt dòng
Witalo Benermo

Nó không phải là để. .không khớp dòng ngắt. Nếu bạn muốn khớp dòng ngắt, bạn có thể thay thế .bằng một cái gì đó như [\s\s].
xe lăn

35

Bạn có thể tách chuỗi trong Bash một mình:

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

Và nếu bạn có một GNU grep bao gồm PCRE , bạn có thể sử dụng xác nhận độ rộng bằng không:

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

Tại sao phương pháp này quá chậm? khi tước một trang html lớn bằng phương pháp này, sẽ mất khoảng 10 giây.
Adam Johns

@AdamJohns, phương pháp nào? PCRE một? PCRE khá phức tạp để phân tích, nhưng 10 giây có vẻ cực đoan. Nếu bạn quan tâm, tôi khuyên bạn nên đặt câu hỏi bao gồm mã ví dụ và xem các chuyên gia nói gì.
ghoti

Tôi nghĩ rằng nó rất chậm đối với tôi vì nó đang giữ một nguồn tệp html rất lớn trong một biến. Khi tôi viết nội dung vào tệp và sau đó phân tích tệp, tốc độ tăng lên đáng kể.
Adam Johns

22

Thông qua GNU awk

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

grep với tham số -P( perl-regapi ) hỗ trợ \K, giúp loại bỏ các ký tự trùng khớp trước đó. Trong trường hợp của chúng tôi, chuỗi khớp trước đó là Heredo nó bị loại khỏi đầu ra cuối cùng.

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

Nếu bạn muốn đầu ra là is athì bạn có thể thử dưới đây,

$ 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

Điều này không hoạt động cho : echo "Here is a string dfdsf Here is a string" | awk -v FS="(Here|string)" '{print $2}', nó chỉ trả về is athay vì phải là is a is a@Avinash Raj
vào

20

Nếu bạn có một tệp dài với nhiều dòng đa dòng, thì việc in các dòng số đầu tiên là rất hữu ích:

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

3
Cảm ơn! Đây là giải pháp duy nhất hoạt động trong trường hợp của tôi (tệp văn bản nhiều dòng, thay vì một chuỗi không có ngắt dòng). Rõ ràng, để có nó mà không đánh số dòng, -ntùy chọn trong catphải được bỏ qua.
Jeffrey Lebowski

... Trong trường hợp đó catcó thể được bỏ qua hoàn toàn; sedbiết cách đọc một tập tin hoặc đầu vào tiêu chuẩn.
tripleee

9

Điều này có thể làm việc cho bạn (GNU sed):

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

Điều này trình bày mỗi đại diện của văn bản giữa hai điểm đánh dấu (trong trường hợp này HereString) trên một dòng mới và bảo tồn các dòng mới trong văn bản.


7

Tất cả các giải pháp trên đều có thiếu sót trong đó chuỗi tìm kiếm cuối cùng được lặp lại ở nơi khác trong chuỗi. Tôi thấy tốt nhất để viết một hàm 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

Bạn có thể sử dụng hai lệnh s

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

Cũng hoạt động

$ 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

Để hiểu sedlệnh, chúng ta phải xây dựng nó từng bước.

Đây là văn bản gốc của bạn

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

Hãy thử xóa Herechuỗi với stùy chọn ubstition trongsed

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

Tại thời điểm này, tôi tin rằng bạn sẽ có thể để loại bỏ Stringcũng

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

Nhưng đây không phải là đầu ra mong muốn của bạn.

Để kết hợp hai lệnh sed, sử dụng -etùy chọn

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

Hi vọng điêu nay co ich


4

Bạn có thể sử dụng \1(tham khảo http://www.grymoire.com/Unix/Sed.html#uh-4 ):

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

Các nội dung bên trong ngoặc sẽ được lưu trữ dưới dạng \1.


Điều này loại bỏ các chuỗi thay vì đầu ra một cái gì đó ở giữa. Hãy thử xóa "Hello" bằng "is" trong lệnh sed và nó sẽ xuất ra "Hello a"
Jonathan

1

Vấn đề. Các thư Claws Mail được lưu trữ của tôi được gói như sau và tôi đang cố trích xuất các dòng Chủ đề:

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>

Mỗi A2 trong chủ đề này, Làm thế nào để sử dụng sed / grep để trích xuất văn bản giữa hai từ? biểu thức đầu tiên, bên dưới, "hoạt động" miễn là văn bản phù hợp không chứa dòng mới:

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

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key

Tuy nhiên, mặc dù đã thử rất nhiều biến thể ( .+?; /s; ...), tôi không thể làm cho chúng hoạt động:

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

Giải pháp 1.

Mỗi trích xuất văn bản giữa hai chuỗi trên các dòng khác nhau

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

cái nào cho

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

Giải pháp 2. *

Per Làm cách nào tôi có thể thay thế một dòng mới (\ n) bằng sed?

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

sẽ thay thế dòng mới bằng một không gian.

Xâu chuỗi với A2 trong Cách sử dụng sed / grep để trích xuất văn bản giữa hai từ? , chúng tôi nhận được:

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

cái nào cho

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

Biến thể này loại bỏ khoảng trắng kép:

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

cho

[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
cuộc phiêu lưu tuyệt vời :))
Alexandru-Mihai Manolescu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.