sed หรือ awk: ลบ n บรรทัดตามรูปแบบ


106

ฉันจะผสมรูปแบบและช่วงตัวเลขใน sed (หรือเครื่องมือที่คล้ายกัน - awk เป็นต้น) ได้อย่างไร สิ่งที่ฉันต้องการทำคือจับคู่บางบรรทัดในไฟล์และลบ n บรรทัดถัดไปก่อนที่จะดำเนินการต่อและฉันต้องการทำเช่นนั้นเป็นส่วนหนึ่งของไปป์ไลน์

คำตอบ:


189

ฉันจะไปที่นี้

ในการลบ 5 บรรทัดหลังรูปแบบ (รวมถึงเส้นที่มีรูปแบบ):

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

ในการลบ 5 บรรทัดหลังรูปแบบ (ไม่รวมบรรทัดที่มีรูปแบบ):

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

14
โปรดทราบว่า+Nรูปแบบนี้เป็นส่วนขยาย GNU เปลี่ยนรูปแบบแรกnเป็นNในตัวอย่างที่สองของคุณเพื่อให้มีเส้นที่มีรูปแบบ
หยุดชั่วคราวจนกว่าจะมีประกาศอีกครั้ง

2
จะลบเส้นทั้งหมดหลังจากจับคู่รูปแบบได้อย่างไร? ฉันใช้ sed -e '/ <! - # content end -> </div> /, $ d' out.txt แต่มีข้อผิดพลาดว่า: sed: -e expression # 1, char 24: อักขระพิเศษหลัง คำสั่งขอบคุณล่วงหน้า
N MOL

8
สิ่งที่เกิดขึ้นคล้ายกัน แต่แตกต่างกันเล็กน้อยในแต่ละกรณี ในสูตรแรก/pattern/,+5กำหนดช่วงซึ่งเริ่มต้นด้วยบรรทัดที่มี "รูปแบบ" ( /pattern/) และสิ้นสุด 5 บรรทัดในภายหลัง ( +5) อักขระสุดท้ายdคือคำสั่งสำหรับเรียกใช้ในแต่ละบรรทัดในช่วงนั้นซึ่งก็คือ "ลบ" ในสูตรที่สองแทนที่จะจับคู่ช่วงจะจับคู่เฉพาะที่บรรทัดที่มีรูปแบบ ( /pattern/) จากนั้นเรียกใช้ชุดคำสั่ง: {n;N;N;N;N;d}ซึ่งโดยทั่วไปจะพิมพ์บรรทัดถัดไป ( n) จากนั้นอ่านและทิ้ง 4 บรรทัดถัดไป ( N;N;N;N;d).
pimlottc

18
บนระบบ Mac / OS X คุณต้องเพิ่มอัฒภาคก่อนวงเล็บปิด:sed -e '/pattern/{n;N;N;N;N;d;}' file.txt
AvL

1
เพื่อความสมบูรณ์: หากต้องการลบบรรทัดทั้งหมดตามรูปแบบที่กำหนดให้ somethingทำ: regex ที่ขยายการพกพา POSIX อยู่sed -E '/^something$/,$d'ที่ไหน -E
not2qubit

7

ไม่มีส่วนขยาย GNU (เช่นบน macOS):

หากต้องการลบ 5 บรรทัดหลังรูปแบบ (รวมถึงเส้นที่มีรูปแบบ)

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

เพิ่ม-i ''เพื่อแก้ไขในสถานที่


6

awkวิธีแก้ปัญหาง่ายๆ:

สมมติว่าการแสดงออกปกติจะใช้สำหรับการหาเส้นจับคู่จะถูกเก็บไว้ในตัวแปรเปลือกและนับจากบรรทัดที่จะข้ามไปใน$regex$count

หากควรข้ามบรรทัดที่ตรงกันด้วย ( ข้าม$count + 1เส้น):

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

หากไม่ควรข้าม$countเส้นที่ตรงกัน( เส้นหลังการแข่งขันถูกข้ามไป):

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

คำอธิบาย:

  • -v regex="$regex" -v count="$count"กำหนดawkตัวแปรตามตัวแปรเชลล์ที่มีชื่อเดียวกัน
  • $0 ~ regex ตรงกับสายงานที่สนใจ
    • { skip=count; next }เริ่มต้นการนับการข้ามและดำเนินการต่อไปยังบรรทัดถัดไปโดยข้ามบรรทัดที่ตรงกันอย่างมีประสิทธิภาพ ในโซลูชันที่ 2 printก่อนหน้านี้จะnextช่วยให้แน่ใจว่าไม่มีการข้าม
    • --skip >= 0 ลดจำนวนการข้ามและดำเนินการหากเป็น (นิ่ง)> = 0 หมายความว่าควรข้ามเส้นที่อยู่ในมือ
    • { next } ไปยังบรรทัดถัดไปโดยข้ามบรรทัดปัจจุบันอย่างมีประสิทธิภาพ
  • 1เป็นชวเลขที่ใช้กันทั่วไปสำหรับ{ print }; นั่นคือบรรทัดปัจจุบันถูกพิมพ์อย่างเรียบง่าย
    • เฉพาะบรรทัดที่ไม่ตรงกันและไม่ข้ามเท่านั้นที่เข้าถึงคำสั่งนี้
    • เหตุผลที่1เทียบเท่ากับ{ print }นั้น1ถูกตีความว่าเป็นรูปแบบบูลีนที่ตามนิยามมักจะประเมินว่าเป็นจริงซึ่งหมายความว่าการดำเนินการที่เกี่ยวข้อง (บล็อก) จะถูกดำเนินการโดยไม่มีเงื่อนไข เนื่องจากไม่มีการดำเนินการที่เกี่ยวข้องในกรณีนี้awkค่าเริ่มต้นคือการพิมพ์บรรทัด

3

สิ่งนี้อาจได้ผลสำหรับคุณ:

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
ว้าวมันคลุมเครือ
pimlottc

3
โซลูชันที่ชาญฉลาด (แม้ว่าจะมีเฉพาะ GNU-Sed) แต่มีเพียงไม่กี่คนที่จะได้รับประโยชน์จากมันเว้นแต่คุณจะเพิ่มคำอธิบาย pattern_number.txtคือไฟล์ 2 คอลัมน์ที่มีรูปแบบที่จะจับคู่ในคอลัมน์ที่ 1 และในลำดับที่ 2 จำนวนบรรทัดที่จะข้าม sedคำสั่งแรกแปลงไฟล์เป็นsedสคริปต์ที่ดำเนินการจับคู่และข้ามที่สอดคล้องกัน สคริปต์นั้นถูกจัดเตรียมผ่าน-fและ stdin ( -) ไปยังsedคำสั่งที่2 sedคำสั่งที่2 ดำเนินการกับไฟล์อินพุต ad-hoc ตัวอย่างที่สร้างขึ้นจากเอาต์พุตของseq 21เพื่อแสดงให้เห็นว่าทำงานได้
mklement0

นอกจากนี้วิธีแก้ปัญหายังมาพร้อมกับข้อแม้หนึ่งข้อ: วิธีที่ใช้ไม่ข้ามบรรทัดแรก (วิธีที่ตรงกับรูปแบบ) มีผลข้างเคียงจากการไม่ข้ามบรรทัดที่ซ้ำกันในช่วง
mklement0

นั่นคือการใช้ sed.
Travis Rodman

3

ใช้ 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

โซลูชันนี้ช่วยให้คุณส่ง "n" เป็นพารามิเตอร์และจะอ่านรูปแบบของคุณจากไฟล์:

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 -

ไฟล์ชื่อ "-" หมายถึง stdin สำหรับ awk ดังนั้นจึงเหมาะสำหรับไปป์ไลน์ของคุณ


2
awk มีความสามารถในการเป็นเหมือน perl มากกว่าที่ฉันรู้!
Martin DeMello
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.