2012-02-25 42 views
8
sed -e '/XXXX/,+4d' fv.out 

Tôi phải tìm một mẫu cụ thể trong một tệp và xóa 5 dòng ở trên và 4 dòng bên dưới cùng một lúc. Tôi phát hiện ra rằng dòng bên trên loại bỏ dòng có chứa mẫu và bốn dòng bên dưới nó.Xóa n1 dòng trước và dòng n2 sau đối với một dòng có chứa một mẫu

sed -e '/XXXX/,~5d' fv.out 

Trong hướng dẫn sử dụng sed, nó được biểu diễn ~ đại diện cho các dòng được theo sau bởi mẫu. Nhưng khi tôi thử nó, đó là những dòng theo mẫu đã bị xóa.

Vì vậy, làm cách nào để xóa 5 dòng ở trên và 4 dòng bên dưới một dòng chứa mẫu đó cùng một lúc?

Trả lời

5

Một cách sử dụng sed, giả định rằng các mô hình không đóng đủ nhau:

Nội dung script.sed:

## If line doesn't match the pattern... 
/pattern/ ! { 

    ## Append line to 'hold space'. 
    H 

    ## Copy content of 'hold space' to 'pattern space' to work with it. 
    g 

    ## If there are more than 5 lines saved, print and remove the first 
    ## one. It's like a FIFO. 
    /\(\n[^\n]*\)\{6\}/ { 

     ## Delete the first '\n' automatically added by previous 'H' command. 
     s/^\n// 
     ## Print until first '\n'. 
     P 
     ## Delete data printed just before. 
     s/[^\n]*// 
     ## Save updated content to 'hold space'. 
     h 
    } 

### Added to fix an error pointed out by potong in comments. 
### ======================================================= 
    ## If last line, print lines left in 'hold space'. 
    $ { 
     x 
     s/^\n// 
     p 
    } 
### ======================================================= 


    ## Read next line. 
    b 
} 

## If line matches the pattern... 
/pattern/ { 

    ## Remove all content of 'hold space'. It has the five previous 
    ## lines, which won't be printed. 
    x 
    s/^.*$// 
    x 

    ## Read next four lines and append them to 'pattern space'. 
    N ; N ; N ; N 

    ## Delete all. 
    s/^.*$// 
} 

Run như:

sed -nf script.sed infile 
+0

Cảm ơn, chính xác những gì tôi đang tìm kiếm (Tôi có sự xuất hiện của mẫu phân cách bởi ít nhất 20 dòng). –

1

Ý tưởng là đọc 5 dòng mà không cần in chúng. Nếu bạn tìm thấy mẫu, xóa các dòng chưa in và 4 dòng dưới đây. Nếu bạn không tìm thấy mẫu, hãy nhớ dòng hiện tại và in dòng chưa in đầu tiên. Cuối cùng, in những gì chưa được in ra.

sed -n -e '/XXXX/,+4{x;s/.*//;x;d}' -e '1,5H' -e '6,${H;g;s/\n//;P;s/[^\n]*//;h}' -e '${g;s/\n//;p;d}' fv.out 

Tất nhiên, điều này chỉ hoạt động nếu bạn có một sự xuất hiện của mô hình của bạn trong file. Nếu bạn có nhiều, bạn cần phải đọc 5 dòng mới sau khi tìm mẫu của bạn, và nó sẽ phức tạp nếu bạn lại có mẫu của bạn trong những dòng đó. Trong trường hợp này, tôi nghĩ sed không phải là công cụ phù hợp.

+0

Cảm ơn. Nó hoạt động cho lần xuất hiện đầu tiên. Nhưng, tôi có rất nhiều lần xuất hiện của mô hình. Có thể tôi sẽ phải đặt nó trong một vòng lặp để grep cho thấy không có sự xuất hiện của mô hình. Những gì bạn đề nghị khác hơn là sử dụng sed? –

+0

Tôi đã làm việc trên các giải pháp awk dưới đây. Theo tôi, nó tốt hơn nhiều vì dễ hiểu và dễ bảo trì hơn. – jfg956

1

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

sed 'H;$!d;g;s/\([^\n]*\n\)\{5\}[^\n]*PATTERN\([^\n]*\n\)\{5\}//g;s/.//' file 

hay này:

awk --posix -vORS='' -vRS='([^\n]*\n){5}[^\n]*PATTERN([^\n]*\n){5}' 1 file 

một giải pháp sed hiệu quả hơn:

sed ':a;/PATTERN/,+4d;/\([^\n]*\n\)\{5\}/{P;D};$q;N;ba' file 
+0

Cảm ơn. Nó hoạt động hoàn hảo. Nhưng, tôi có một số tệp lớn và mất rất nhiều thời gian. –

+0

@PopulationXplosive Tôi đã thêm một giải pháp awk. Nó có thể nhanh hơn. – potong

+0

Cảm ơn. Các giải pháp awk cũng mất rất nhiều thời gian. Nhưng giải pháp sed mới là khá nhanh. Đẹp một lót thực sự. –

2

Một giải pháp sử dụng awk:

awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; } 
    nlines == 5 { print lines[NR%5]; nlines-- } 
    lines2del == 0 { lines[NR%5] = $0; nlines++ } 
    lines2del > 0 { lines2del-- } 
    END { while (nlines-- > 0) { print lines[(NR - nlines) % 5] } }' fv.out 

Cập nhật:

Đây là sc ript giải thích:

  • Tôi nhớ 5 dòng cuối cùng trong mảng lines sử dụng chỉ số xoay vòng (NR% 5; NR là số bản ghi; trong trường hợp này).
  • Nếu tôi tìm ra mô hình trong dòng hiện tại ($0 ~ "XXXX; $0 là kỷ lục hiện tại: trong trường hợp này một dòng; và ~Extended Regular Expression trận đấu điều hành), tôi thiết lập lại số dòng đọc và lưu ý rằng Tôi có 5 dòng để xóa (bao gồm cả dòng hiện tại).
  • Nếu tôi đã đọc 5 dòng, tôi in dòng hiện tại.
  • Nếu tôi không có dòng để xóa (điều này cũng đúng nếu tôi đã đọc 5 dòng, tôi đặt dòng hiện tại vào bộ đệm và tăng số dòng. Lưu ý số lượng các dòng bị giảm đi và sau đó tăng lên nếu
  • Nếu dòng cần xóa, tôi không in bất cứ thứ gì và giảm số dòng cần xóa.
  • Ở cuối tập lệnh, tôi in tất cả các dòng nằm trong mảng.

Phiên bản gốc của tập lệnh là như sau, nhưng tôi đã tối ưu hóa nó lên phiên bản trên:

awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; } 
    lines2del == 0 && nlines == 5 { print lines[NR%5]; lines[NR%5] } 
    lines2del == 0 && nlines < 5 { lines[NR%5] = $0; nlines++ } 
    lines2del > 0 { lines2del-- } 
    END { while (nlines-- > 0) { print lines[(NR - nlines) % 5] } }' fv.out 

awk là một công cụ tuyệt vời! Tôi khuyên bạn nên tìm một hướng dẫn trên mạng và đọc nó. Một điều quan trọng: awk hoạt động với Biểu thức chính quy mở rộng (ERE). Cú pháp của chúng hơi khác một chút so với Biểu thức chính quy chuẩn (RE) được sử dụng trong sed, nhưng tất cả những gì có thể được thực hiện với RE có thể được thực hiện bằng ERE.

+0

Cảm ơn. Nó đang làm việc. Nhưng, tôi là một người mới bắt đầu tuyệt đối để lúng túng. Vì vậy, bạn có thể vui lòng giải thích kịch bản? Ngoài ra, làm thế nào để bạn sửa chữa n1 và n2 một cách riêng biệt? Dường như với tôi rằng ở đây bạn đã thực hiện n1 = n2. –

+0

Tôi không hiểu câu hỏi n1/n2 của bạn. Giải thích kịch bản rất tiến lên, tôi đang cập nhật bài đăng. – jfg956

+0

Cảm ơn. Tôi đã tự hỏi rằng làm thế nào để bạn xóa 4 dòng dưới đây (n2) mẫu. Vì tôi không có kinh nghiệm trong vụng về, tôi đã nghĩ rằng bạn đặt số lượng các dòng sau (n2) và trước (n1) mẫu giống nhau. Tôi đã rất bối rối. Tôi nghĩ rằng nó sẽ là tuyệt vời nếu tôi học awk quá. –

1

Nếu bạn đang hạnh phúc để sản xuất các kết quả vào một tập tin thay vì stdout, vim thể làm điều đó khá hiệu quả:

vim -c 'g/pattern/-5,+4d' -c 'w! outfile|q!' infile 

hoặc

vim -c 'g/pattern/-5,+4d' -c 'x' infile 

để chỉnh sửa các tập tin tại chỗ.

Các vấn đề liên quan