Cách chọn các dòng giữa hai mẫu điểm đánh dấu có thể xảy ra nhiều lần với awk / sed


119

Sử dụng awkhoặc sedlàm cách nào để chọn các dòng nằm giữa hai mẫu điểm đánh dấu khác nhau? Có thể có nhiều phần được đánh dấu bằng các mẫu này.

Ví dụ: Giả sử tệp chứa:

abc
def1
ghi1
jkl1
mno
abc
def2
ghi2
jkl2
mno
pqr
stu

Và mẫu bắt đầu là abcvà mẫu kết thúc là mno Vì vậy, tôi cần đầu ra là:

def1
ghi1
jkl1
def2
ghi2
jkl2

Tôi đang sử dụng sed để khớp với mẫu một lần:

sed -e '1,/abc/d' -e '/mno/,$d' <FILE>

Có cách nào trong sedhoặc awk thực hiện lặp đi lặp lại cho đến khi kết thúc tệp không?

Câu trả lời:


188

Sử dụng awkvới một lá cờ để kích hoạt in khi cần thiết:

$ awk '/abc/{flag=1;next}/mno/{flag=0}flag' file
def1
ghi1
jkl1
def2
ghi2
jkl2

Cái này hoạt động ra sao?

  • /abc/đối sánh với các dòng có văn bản này, cũng như /mno/không.
  • /abc/{flag=1;next}đặt thời flagđiểm abctìm thấy văn bản . Sau đó, nó bỏ qua dòng.
  • /mno/{flag=0}bỏ thiết lập flagthời điểm mnotìm thấy văn bản .
  • Cuối cùng flaglà một mẫu với hành động mặc định, đó là print $0: nếu flagbằng 1 thì dòng được in.

Để có mô tả chi tiết hơn và các ví dụ, cùng với các trường hợp khi các mẫu được hiển thị hoặc không, hãy xem Cách chọn dòng giữa hai mẫu? .


30
Nếu bạn muốn in mọi thứ giữa và bao gồm cả mẫu thì bạn có thể sử dụng awk '/abc/{a=1}/mno/{print;a=0}a' file.
scai

6
Vâng, @scai! hoặc thậm chí awk '/abc/{a=1} a; /mno/{a=0}' file- với điều này, đặt ađiều kiện trước khi /mno/chúng tôi thực hiện nó đánh giá dòng là đúng (và in nó) trước khi thiết lập a=0. Bằng cách này chúng ta có thể tránh viết print.
fedorqui 'VẬY đừng làm hại nữa'

12
@scai @fedorqui Đối với việc bao gồm đầu ra mẫu, bạn có thể làmawk '/abc/,/mno/' file
Jotne

1
@hkasera awk '/abc/{flag=1}/mno/{flag=0}flag' filenên thực hiện.
fedorqui 'VẬY đừng làm hại nữa'

2
@EirNym đó là một tình huống kỳ lạ có thể được xử lý theo những cách rất khác nhau: bạn muốn in dòng nào? Có lẽ awk 'flag; /PAT1/{flag=1; next} /PAT1/{flag=0}' filesẽ làm cho.
fedorqui 'VẬY đừng làm hại nữa'

45

Sử dụng sed:

sed -n -e '/^abc$/,/^mno$/{ /^abc$/d; /^mno$/d; p; }'

Các -nphương tiện tùy chọn không in theo mặc định.

Mẫu tìm kiếm các dòng chứa chỉ abcđể chỉ mno, và sau đó thực hiện các hành động trong { ... }. Hành động đầu tiên xóa abcdòng; dòng thứ hai mno; và pin các dòng còn lại. Bạn có thể thư giãn các regexes theo yêu cầu. Bất kỳ dòng nào nằm ngoài phạm vi của abc.. mnođơn giản là không được in.


Cảm ơn đã trả lời và giải thích! :)
dvai

@JonathanLeffler tôi có thể biết mục đích của việc sử dụng là gì-e
Kasun Siyambalapitiya

1
@KasunSiyambalapitiya: Chủ yếu có nghĩa là tôi thích sử dụng nó. Về mặt hình thức, nó chỉ định rằng đối số tiếp theo là (một phần của) tập lệnh sedsẽ thực thi. Nếu bạn muốn hoặc cần sử dụng một số đối số để bao gồm toàn bộ tập lệnh, thì bạn phải sử dụng -etrước mỗi đối số như vậy; nếu không, nó là tùy chọn (nhưng rõ ràng).
Jonathan Leffler


Đẹp! (Tôi thích sed hơn awk.) Khi sử dụng các biểu thức chính quy phức tạp, sẽ rất tuyệt nếu không phải lặp lại chúng. Không thể xóa dòng đầu tiên / cuối cùng của phạm vi "đã chọn"? Hoặc để áp dụng đầu tiên dcho tất cả các dòng cho đến trận đấu đầu tiên, sau đó dáp dụng cho tất cả các dòng bắt đầu với trận đấu thứ hai?
hans_meine

18

Điều này có thể phù hợp với bạn (GNU sed):

sed '/^abc$/,/^mno$/{//!b};d' file

Xóa tất cả các dòng ngoại trừ những dòng giữa các dòng bắt đầu abcmno



Điều này thật tuyệt. Các {//!b}ngăn chặn abcmnokhông được bao gồm trong đầu ra, nhưng tôi không thể tìm ra cách. Bạn có thể giải thích?
Brendan

1
@Brendan lệnh //!bđọc nếu dòng hiện tại không phải là một trong các dòng phù hợp với phạm vi, hãy ngắt và do đó in các dòng đó nếu không tất cả các dòng khác sẽ bị xóa.
potong

13
sed '/^abc$/,/^mno$/!d;//d' file

chơi gôn hai nhân vật tốt hơn ppotong {//!b};d

Các dấu gạch chéo trống về phía trước //có nghĩa là: "sử dụng lại cụm từ thông dụng cuối cùng được sử dụng". và lệnh làm tương tự như dễ hiểu hơn:

sed '/^abc$/,/^mno$/!d;/^abc$/d;/^mno$/d' file

Đây dường như là POSIX :

Nếu RE trống (nghĩa là không có mẫu nào được chỉ định) sed sẽ hoạt động như thể RE cuối cùng được sử dụng trong lệnh cuối cùng được áp dụng (dưới dạng địa chỉ hoặc là một phần của lệnh thay thế) được chỉ định.


1
Tôi nghĩ rằng giải pháp thứ hai sẽ không có kết quả gì vì lệnh thứ hai cũng là một phạm vi. Tuy nhiên kudo cho người đầu tiên.
potong

@potong đúng! Tôi phải nghiên cứu thêm tại sao cái đầu tiên hoạt động. Cảm ơn!
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

7

Từ các liên kết của phản hồi trước đó, liên kết đã làm điều đó cho tôi, chạy kshtrên Solaris, là:

sed '1,/firstmatch/d;/secondmatch/,$d'
  • 1,/firstmatch/d: từ dòng 1 cho đến lần đầu tiên bạn tìm thấy firstmatch, xóa.
  • /secondmatch/,$d: từ lần xuất hiện đầu tiên secondmatchcho đến khi kết thúc tệp, xóa.
  • Dấu chấm phẩy phân tách hai lệnh, được thực hiện theo trình tự.

Chỉ tò mò, tại sao giới hạn phạm vi ( 1,) lại xuất hiện trước đó /firstmatch/? Tôi đoán điều này cũng có thể được diễn đạt '/firstmatch/1,d;/secondmatch,$d'?
Luke Davis

2
Với "1, / firstmatch / d", bạn đang nói "từ dòng 1 cho đến lần đầu tiên bạn tìm thấy 'firstmatch', hãy xóa". Trong khi đó, với "/ secondmatch /, $ d" bạn nói "từ lần xuất hiện đầu tiên của 'secondmatch' cho đến khi kết thúc tệp, hãy xóa". dấu chấm phẩy phân tách hai lệnh, được thực hiện theo trình tự.
FanDeLaU

2
perl -lne 'print if((/abc/../mno/) && !(/abc/||/mno/))' your_file

Tốt để biết tương đương perl vì nó là một thay thế khá tốt cho cả awk và sed.
akhan

2

một cái gì đó như thế này phù hợp với tôi:

file.awk:

BEGIN {
    record=0
}

/^abc$/ {
    record=1
}

/^mno$/ {
    record=0;
    print "s="s;
    s=""
}

!/^abc|mno$/ {
    if (record==1) {
        s = s"\n"$0
    }   
}

sử dụng: awk -f file.awk data ...

sửa: Giải pháp O_o fedorqui tốt hơn / đẹp hơn của tôi.


3
Trong GNU awk if (record=1)nên là if (record==1), tức là gấp đôi = - xem toán tử so sánh gawk
George Hawkins

2

Câu trả lời của Don_crissti từ Chỉ hiển thị văn bản giữa 2 mẫu phù hợp ?

firstmatch="abc"
secondmatch="cdf"
sed "/$firstmatch/,/$secondmatch/!d;//d" infile

hiệu quả hơn nhiều so với ứng dụng của AWK, xem tại đây .


Tôi không nghĩ rằng việc liên kết các so sánh thời gian có ý nghĩa nhiều ở đây, vì yêu cầu của các câu hỏi là khá khác nhau, do đó các giải pháp.
fedorqui 'VẬY đừng làm hại'.

2
Tôi không đồng ý vì chúng ta nên có một số tiêu chí để so sánh các câu trả lời. Chỉ một số ít có ứng dụng SED.
Léo Léopold Hertz 준영

0

Tôi đã cố gắng sử dụng awkđể in các dòng giữa hai mẫu trong khi mẫu2 cũng khớp với mẫu1 . Và dòng pattern1 cũng nên được in.

ví dụ nguồn

package AAA
aaa
bbb
ccc
package BBB
ddd
eee
package CCC
fff
ggg
hhh
iii
package DDD
jjj

nên có một loạt

package BBB
ddd
eee

Mẫu1 ở đâu package BBB, mẫu2 là package \w*. Lưu ý rằng đó CCCkhông phải là một giá trị đã biết nên không thể khớp theo nghĩa đen.

Trong trường hợp này, cả @scai awk '/abc/{a=1}/mno/{print;a=0}a' filevà @fedorqui đều không phù awk '/abc/{a=1} a; /mno/{a=0}' filehợp với tôi.

Cuối cùng, tôi đã giải quyết được nó bằng cách awk '/package BBB/{flag=1;print;next}/package \w*/{flag=0}flag' file, haha

Nỗ lực nhiều hơn một chút dẫn đến awk '/package BBB/{flag=1;print;next}flag;/package \w*/{flag=0}' file, để in dòng pattern2, nghĩa là,

package BBB
ddd
eee
package CCC
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.