sed hoặc awk: xóa n dòng sau một mẫu


105

Làm cách nào để kết hợp các mẫu và phạm vi số trong sed (hoặc bất kỳ công cụ tương tự nào - ví dụ như awk)? Những gì tôi muốn làm là khớp một số dòng nhất định trong một tệp và xóa n dòng tiếp theo trước khi tiếp tục và tôi muốn làm điều đó như một phần của đường dẫn.

Câu trả lời:


186

Tôi sẽ phải đi tại đây.

Để xóa 5 dòng sau một mẫu (bao gồm cả dòng có mẫu):

sed -e '/pattern/,+5d' file.txt

Để xóa 5 dòng sau một mẫu (trừ dòng có mẫu):

sed -e '/pattern/{n;N;N;N;N;d}' file.txt

14
Lưu ý rằng +Nmẫu là một phần mở rộng GNU. Thay đổi đầu tiên nthành một Ntrong ví dụ thứ hai của bạn để làm cho nó bao gồm đường với mẫu.
Tạm dừng cho đến khi có thông báo mới.

2
Làm thế nào để xóa tất cả các dòng sau khi mẫu được khớp? Tôi đang sử dụng sed -e '/ <! - # content end -> </div> /, $ d' out.txt nhưng nó báo lỗi: sed: -e expression # 1, char 24: extra character after lệnh Cảm ơn trước.
N mol

8
Những gì đang xảy ra tương tự nhưng hơi khác nhau trong mỗi trường hợp. Trong công thức đầu tiên, hãy /pattern/,+5xác định một phạm vi, bắt đầu bằng một dòng chứa "mẫu" ( /pattern/) và kết thúc 5 dòng sau đó ( +5). Ký tự cuối cùng dlà lệnh chạy trên mỗi dòng trong phạm vi đó, đó là "delete". Trong công thức thứ hai, thay vì khớp với một phạm vi, nó chỉ khớp tại dòng chứa mẫu ( /pattern/) và sau đó chạy một loạt lệnh:, {n;N;N;N;N;d}về cơ bản sẽ in dòng tiếp theo ( n), sau đó đọc và cuối cùng loại bỏ 4 dòng tiếp theo ( N;N;N;N;d).
pimlottc

18
Trên hệ thống Mac / OS X, bạn cần thêm dấu chấm phẩy trước dấu ngoặc đóng:sed -e '/pattern/{n;N;N;N;N;d;}' file.txt
AvL

1
Để có tính hoàn chỉnh: Để xóa tất cả các dòng theo một mẫu nhất định, something hãy thực hiện : sed -E '/^something$/,$d', -Eregex mở rộng khả năng di chuyển POSIX ở đâu .
not2qubit

7

Không có phần mở rộng GNU (ví dụ: trên macOS):

Để xóa 5 dòng sau một mẫu (bao gồm cả dòng có mẫu)

 sed -e '/pattern/{N;N;N;N;d;}'

Thêm -i ''để chỉnh sửa tại chỗ.


6

Các awkgiải pháp đơn giản :

Giả sử rằng biểu thức chính quy được sử dụng để tìm các dòng phù hợp được lưu trữ trong biến shell $regexvà số dòng cần bỏ qua $count.

Nếu dòng phù hợp cũng nên được bỏ qua ( $count + 1các dòng bị bỏ qua):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; next } --skip >= 0 { next } 1'

Nếu dòng đối sánh không được bỏ qua ( $countcác dòng sau khi đối sánh bị bỏ qua):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; print; next } --skip >= 0 { next } 1'

Giải trình:

  • -v regex="$regex" -v count="$count"định nghĩa awkcác biến dựa trên các biến shell cùng tên.
  • $0 ~ regex phù hợp với dòng quan tâm
    • { skip=count; next }khởi tạo số lần bỏ qua và chuyển sang dòng tiếp theo, bỏ qua dòng phù hợp một cách hiệu quả; trong giải pháp thứ 2, printtrước đó nextđảm bảo rằng nó không bị bỏ qua.
    • --skip >= 0 giảm số lần bỏ qua và thực hiện hành động nếu nó (vẫn)> = 0, ngụ ý rằng dòng ở tay nên được bỏ qua.
    • { next } chuyển sang dòng tiếp theo, bỏ qua dòng hiện tại một cách hiệu quả
  • 1là một cách viết tắt thường được sử dụng cho { print }; nghĩa là dòng hiện tại được in đơn giản
    • Chỉ các dòng không khớp và không bị bỏ qua mới đạt được lệnh này.
    • Lý do 1tương đương với { print }1được hiểu là một mẫu Boolean mà theo định nghĩa luôn đánh giá là true, có nghĩa là hành động liên quan (khối) của nó được thực thi vô điều kiện. Vì không có hành động liên quan nào trong trường hợp này, awkmặc định là in dòng.

3

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

cat <<! >pattern_number.txt
> 5 3
> 10 1
> 15 5
> !
sed 's|\(\S*\) \(\S*\)|/\1/,+\2{//!d}|' pattern_number.txt |
sed -f - <(seq 21)
1 
2
3
4
5
9
10
12
13
14
15
21

10
Chà, thật khó hiểu.
pimlottc

3
Một giải pháp thông minh (mặc dù dành riêng cho GNU-Sed), nhưng sẽ có ít người được hưởng lợi từ nó, trừ khi bạn thêm giải thích. pattern_number.txtlà một tệp 2 cột chứa mẫu để khớp ở cột thứ nhất và ở cột thứ hai là số dòng cần bỏ qua. Lệnh đầu tiên sedchuyển đổi tệp thành một sedtập lệnh thực hiện đối sánh và bỏ qua tương ứng; tập lệnh đó được cung cấp qua -fvà stdin ( -) cho sedlệnh thứ 2 . Lệnh thứ 2 sedhoạt động trên một tệp đầu vào đặc biệt mẫu được hình thành từ đầu ra của seq 21để chứng minh rằng nó hoạt động.
mklement0

Ngoài ra, giải pháp đi kèm với một lưu ý: phương pháp mà nó sử dụng không bỏ qua dòng đầu tiên (dòng phù hợp với mẫu) có tác dụng phụ là cũng không bỏ qua các dòng trùng lặp trong phạm vi.
mklement0

Đó là một công dụng ấn tượng của sed.
Travis Rodman

3

Sử dụng Perl

$ cat delete_5lines.txt
1
2
3
4
5 hello
6
7
8
9
10
11 hai
$ perl -ne ' BEGIN{$y=1} $y=$.  if /hello/ ; print if $y==1 or $.-$y > 5 ' delete_5lines.txt
1
2
3
4
11 hai
$

2

Giải pháp này cho phép bạn chuyển "n" làm tham số và nó sẽ đọc các mẫu của bạn từ một tệp:

awk -v n=5 '
    NR == FNR {pattern[$0]; next}
    {
        for (patt in pattern) {
            if ($0 ~ patt) {
                print # remove if you want to exclude a matched line
                for (i=0; i<n; i++) getline
                next
            }
        }
        print
    }
' file.with.patterns -

Tệp có tên "-" có nghĩa là stdin cho awk, vì vậy tệp này phù hợp với đường dẫn của bạn


2
awk có khả năng giống perl hơn tôi nhận ra!
Martin DeMello
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.