Xóa dòng chứa chuỗi nhất định và dòng sau


71

Tôi dùng cái này

cat foo.txt | sed '/bar/d'

để loại bỏ các dòng chứa chuỗi bartrong tệp.

Tuy nhiên, tôi muốn loại bỏ những dòng và dòng trực tiếp sau nó . Tốt nhất trong sed, awkhoặc công cụ nào khác có sẵn trong MinGW32.

Đó là một loại ngược lại những gì tôi có thể nhận được ở grepvới -A-Bin dòng phù hợp cũng như dòng trước / sau dòng phù hợp.

Có cách nào dễ dàng để đạt được nó?


2
Chỉ để biết thông tin: Tôi đang phân tích các bản ghi trong đó các mục là hai lớp. Vì vậy, tôi muốn tìm một mục phù hợp với mẫu và loại bỏ nó cũng như dòng tiếp theo. Do đó tôi không cần phải xử lý các dòng khớp liên tiếp, nhưng dù sao cũng cảm ơn vì sự hoàn chỉnh của câu trả lời của bạn!
jakub.g

Câu trả lời:


75

Nếu bạn có GNU sed (vì vậy Linux hoặc Cygwin không được nhúng):

sed '/bar/,+1 d'

Nếu bạn có barhai dòng liên tiếp, điều này sẽ xóa dòng thứ hai mà không phân tích nó. Ví dụ: nếu bạn có tệp 3 dòng bar/ bar/ foo, foodòng sẽ ở lại.


1
+1 cho độ dài :) Trong ví dụ cụ thể của tôi, tôi không có bars liên tiếp nên cái này rất dễ nhớ.
jakub.g

11
sed '/bar/d'nếu bạn chỉ muốn "Xóa dòng chứa chuỗi nhất định" chứ không phải dòng tiếp theo.
AJP

Nếu tôi muốn loại bỏ tất cả các dòng sau toán học thì sao?
Pandya

1
@Pandya Điều đó khác biệt. Bạn có thể sử dụng ví dụsed '/math/q'
Gilles

1
@AK Nếu bạn chỉ muốn xóa dòng phù hợp, nó thậm chí còn đơn giản hơn:sed '/bar/d'
Gilles

16

Nếu barcó thể xảy ra trên các dòng liên tiếp, bạn có thể làm:

awk '/bar/{n=2}; n {n--; next}; 1' < infile > outfile

có thể được điều chỉnh để xóa hơn 2 dòng bằng cách thay đổi 2 dòng ở trên với số dòng cần xóa bao gồm cả dòng phù hợp.

Nếu không, nó dễ dàng được thực hiện sedvới giải pháp của @MichaelRollins hoặc:

sed '/bar/,/^/d' < infile > outfile

Điểm cộng khác trong giải pháp AWK là tôi có thể thay thế /bar/bằng /bar|baz|whatever/. Trong sedcú pháp đó dường như không hoạt động.
jakub.g

@ jakub.g, tôi có GNU sed (v4.4 bây giờ). Không chắc chắn về những người khác. Những gì tôi biết là nó sử dụng cú pháp biểu thức chính quy "cơ bản" theo mặc định, đây là lý do tại sao ví dụ của bạn không hoạt động. Để đạt được những gì bạn muốn, bạn có thể đặt dấu gạch chéo ngược trước mỗi dòng dọc hoặc bạn có thể yêu cầu sedsử dụng các biểu thức thông thường "mở rộng". Thêm thông tin ở đây: gnu.org/software/sed/manual/html_node/ . Xin lưu ý rằng điều này cũng được áp dụng grep. Đây là ví dụ làm việc của riêng tôi : echo $'0a\n1b\n2c' | sed '/0a\|1b/d'.
Victor Yarema

12

Tôi không rành về sed, nhưng rất dễ để làm điều đó trong awk:

awk '/bar/{getline;next} 1' foo.txt 

Kịch bản awk đọc: đối với một dòng chứa thanh, hãy lấy dòng tiếp theo (getline), sau đó bỏ qua tất cả các xử lý tiếp theo (tiếp theo). Mẫu 1 ở cuối in các dòng còn lại.

Cập nhật

Như đã chỉ ra trong nhận xét, giải pháp trên không hoạt động liên tiếp bar. Đây là một giải pháp sửa đổi, đưa nó vào xem xét:

awk '/bar/ {while (/bar/ && getline>0) ; next} 1' foo.txt 

Bây giờ chúng tôi tiếp tục đọc để bỏ qua tất cả / thanh / dòng.


1
Để sao chép grep -A100%, bạn cũng cần xử lý barchính xác bất kỳ số dòng liên tiếp nào (bằng cách xóa toàn bộ khối và 1 dòng sau).
jw013

7

Bạn sẽ muốn sử dụng các khả năng kịch bản của sed để thực hiện điều này.

$ sed -e '/bar/ { 
 $!N
 d
 }' sample1.txt

Dữ liệu mẫu:

$ cat sample1.txt 
foo
bar
biz
baz
buz

Lệnh "N" nối thêm dòng đầu vào tiếp theo vào không gian mẫu. Điều này kết hợp với dòng từ khớp mẫu (/ bar /) sẽ là các dòng mà bạn muốn xóa. Sau đó, bạn có thể xóa bình thường bằng lệnh "d".


Làm cách nào để nhập một dòng mới trong bảng điều khiển? Hay đây chỉ là kịch bản?
jakub.g

@ jakub.g: với GNU sed:sed -e '/bar/{N;d}' sample1.txt
Cyrus

2

Nếu bất kỳ dòng nào ngay sau một trận đấu phải được loại bỏ thì sedchương trình của bạn sẽ phải xem xét các trận đấu liên tiếp. Nói cách khác, nếu bạn loại bỏ một dòng theo sau một trận đấu cũng khớp, thì có lẽ bạn cũng nên xóa dòng đó.

Nó được thực hiện đủ đơn giản - nhưng bạn phải nhìn phía sau một chút.

printf %s\\n     0 match 2 match match \
                 5 6 match match match \
                 10 11 12 match 14 15  |
sed -ne'x;/match/!{g;//!p;}'

0
6
11
12
15

Nó hoạt động bằng cách hoán đổi giữ và không gian mẫu cho mỗi dòng được đọc - vì vậy dòng cuối cùng có thể được so sánh với hiện tại mỗi lần. Vì vậy, khi sedđọc một dòng, nó trao đổi nội dung của bộ đệm của nó - và dòng trước đó là nội dung của bộ đệm chỉnh sửa của nó, trong khi dòng hiện tại được đặt trong không gian giữ.

Vì vậy, sedkiểm tra dòng trước cho khớp với matchvà nếu !không tìm thấy hai biểu thức trong {hàm }được chạy. sedsẽ get không gian giữ bằng cách ghi đè không gian mẫu - có nghĩa là dòng hiện tại là sau đó trong cả các tổ chức và mô hình không gian - và sau đó nó sẽ //kiểm tra xem nó cho phù hợp với cụm thường xuyên gần đây nhất là biên soạn của nó - match- và nếu không thực hiện matchnó được pnhuộm màu.

Điều này có nghĩa là một dòng chỉ được in nếu nó không dòng ngay trước đó không . Nó cũng từ bỏ bất kỳ giao dịch hoán đổi không cần thiết nào cho chuỗi es.match matchmatch

Nếu bạn muốn một phiên bản có thể giảm số lượng dòng tùy ý xảy ra sau một phiên bản matchthì sẽ cần thêm một chút công việc:

printf %s\\n    1 2 3 4 match  \
                match match 8  \
                9 10 11 12 13  \
                14 match match \
                17 18 19 20 21 |
sed -net -e'/match/{h;n;//h;//!H;G;s/\n/&/5;D;}' -ep

... thay thế 5 bằng số dòng (bao gồm cả dòng phù hợp) mà bạn muốn xóa ...


1
2
3
4
12
13
14
21
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.