ลบบรรทัดส่วนหัวพิเศษออกจากไฟล์ยกเว้นบรรทัดแรก


18

ฉันมีไฟล์ที่ดูเหมือนตัวอย่างของเล่นนี้ ไฟล์จริงของฉันมี 4 ล้านบรรทัดซึ่งต้องลบประมาณ 10 บรรทัด

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
ID  Data1  Data2
4    100    100
ID  Data1  Data2
5    200    200

ฉันต้องการลบบรรทัดที่มีลักษณะส่วนหัวยกเว้นบรรทัดแรก

ไฟล์สุดท้าย:

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
4    100    100
5    200    200

ฉันจะทำสิ่งนี้ได้อย่างไร

คำตอบ:


26
header=$(head -n 1 input)
(printf "%s\n" "$header";
 grep -vFxe "$header" input
) > output
  1. คว้าบรรทัดส่วนหัวจากไฟล์อินพุตเข้าสู่ตัวแปร
  2. พิมพ์ส่วนหัว
  3. ประมวลผลไฟล์ด้วยgrepเพื่อละเว้นบรรทัดที่ตรงกับส่วนหัว
  4. จับเอาท์พุทจากสองขั้นตอนข้างต้นเป็นไฟล์เอาต์พุต

2
หรือบางที{ IFS= read -r head; printf '%s\n' "$head"; grep -vF "$head" ; } <file
iruvar

ทั้งการเพิ่มที่ดี ขอบคุณ don_crissti สำหรับการชี้ทางอ้อมว่า posix เพิ่งลบ -1 ไวยากรณ์จากหัวเพื่อสนับสนุน -n 1
Jeff Schaller

3
@JeffSchaller เมื่อไม่นานมานี้ใน 12 ปีที่ผ่านมา และhead -1เคยล้าสมัยมานานหลายทศวรรษก่อนหน้านั้น
Stéphane Chazelas

36

คุณสามารถใช้ได้

sed '2,${/ID/d;}'

การทำเช่นนี้จะลบบรรทัดที่มี ID เริ่มต้นจากบรรทัดที่ 2


3
ดี; หรือจะเจาะจงมากขึ้นด้วยการจับคู่รูปแบบsed '2,${/^ID Data1 Data2$/d;}' file(โดยใช้จำนวนช่องว่างที่เหมาะสมระหว่างคอลัมน์แน่นอน)
Jeff Schaller

หืมฉันคิดว่าคุณสามารถตัดเครื่องหมายอัฒภาคเพียงคำสั่งเดียวได้ แต่ก็โอเค
bkmoney

ไม่ใช่ด้วยสติsedไม่มี
mikeserv

aaaand -i สำหรับการแก้ไขแบบ in-place win
user2066657

4
หรือsed '1!{/ID/d;}'
Stéphane Chazelas

10

สำหรับคนที่ไม่ชอบวงเล็บปีกกา

sed -e '1n' -e '/^ID/d'
  • nหมายถึงpassบรรทัดที่1
  • d ลบบรรทัดที่ตรงกันทั้งหมดที่ขึ้นต้นด้วย ^ID

5
สิ่งนี้สามารถย่อให้สั้นลงสำหรับsed '1n;/^ID/d'ชื่อไฟล์ เพียงข้อเสนอแนะ
Valentin Bajrami

โปรดทราบว่านี่จะพิมพ์บรรทัดIDfooที่ไม่เหมือนกันกับส่วนหัว (ไม่น่าจะสร้างความแตกต่างในกรณีนี้ แต่คุณไม่มีทางรู้)
terdon

6

นี่คือความสนุก คุณสามารถใช้sedโดยตรงเพื่อดึงสำเนาทั้งหมดของบรรทัดแรกออกและปล่อยให้ทุกอย่างอยู่ในตำแหน่ง (รวมถึงบรรทัดแรกเอง)

sed '1{h;n;};G;/^\(.*\)\n\1$/d;s/\n.*$//' input

1{h;n;}วางบรรทัดแรกลงในพื้นที่พักพิมพ์และอ่านในบรรทัดถัดไปโดยข้ามsedคำสั่งที่เหลือสำหรับบรรทัดแรก (นอกจากนี้ยังข้าม1การทดสอบครั้งแรกสำหรับบรรทัดที่สองแต่ไม่สำคัญว่าการทดสอบนั้นจะไม่นำไปใช้กับบรรทัดที่สอง)

G ต่อท้ายบรรทัดใหม่ตามด้วยเนื้อหาของพื้นที่พักไว้กับพื้นที่รูปแบบ

/^\(.*\)\n\1$/dลบเนื้อหาของพื้นที่รูปแบบ (เช่นการข้ามไปที่บรรทัดถัดไป) หากส่วนหลัง newline (เช่นสิ่งที่ถูกต่อท้ายจากพื้นที่พัก) ตรงกับส่วนก่อนหน้าขึ้นบรรทัดใหม่ นี่คือที่บรรทัดที่ทำซ้ำส่วนหัวจะถูกลบ

s/\n.*$//ลบส่วนของข้อความที่เพิ่มโดยGคำสั่งเพื่อให้สิ่งที่ได้รับการพิมพ์เป็นเพียงบรรทัดของข้อความจากไฟล์

อย่างไรก็ตามเนื่องจาก regex มีราคาแพงวิธีที่เร็วกว่าเล็กน้อยคือการใช้เงื่อนไขเดียวกัน (เมื่อตะกี้) และPวิ่งขึ้นบรรทัดใหม่หากส่วนหลัง newline (เช่นสิ่งที่ต่อท้ายจากพื้นที่พัก) ไม่ตรงกับส่วนที่แน่นอน ก่อนขึ้นบรรทัดใหม่แล้วลบพื้นที่รูปแบบโดยไม่มีเงื่อนไข

sed '1{h;n;};G;/^\(.*\)\n\1$/!P;d' input

เอาต์พุตเมื่อกำหนดอินพุตของคุณคือ:

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
4    100    100
5    200    200

ที่เกี่ยวข้อง: vi.stackexchange.com/q/6269/4676
Wildcard

@don_crissti การเพิ่มที่น่าสนใจ; ขอบคุณ! ผมก็อาจจะเลือกใช้อีกต่อไป แต่เทียบเท่าsed '1{h;n;};G;/^\(.*\)\n\1$/d;P;d' input; ฉันอ่านได้ง่ายกว่า :)
Wildcard

ยังเกี่ยวข้องกับ: unix.stackexchange.com/a/417736/135943
Wildcard

5

ต่อไปนี้เป็นตัวเลือกเพิ่มเติมสองสามตัวที่ไม่ต้องการให้คุณรู้ล่วงหน้าก่อนบรรทัด:

perl -ne 'print unless $_ eq $k; $k=$_ if $.==1; 

-nธงบอก Perl $_ห่วงมากกว่าแฟ้มใส่มันประหยัดแต่ละบรรทัดเป็น $k=$_ if $.==1;บันทึกบรรทัดแรก ( $.คือหมายเลขบรรทัดเพื่อ$.==1จะเป็นจริงสำหรับเส้นที่ 1) $kในฐานะ พิมพ์บรรทัดปัจจุบันถ้ามันไม่ได้เป็นแบบเดียวกันกับที่บันทึกไว้ในprint unless $k eq $_$k

อีกวิธีหนึ่งคือawk:

awk '$0!=x;(NR==1){x=$0}' file 

ที่นี่เราทดสอบว่าบรรทัดปัจจุบันเหมือนกับที่บันทึกไว้ในตัวแปรxหรือไม่ หากการทดสอบ$0!=xประเมินค่าเป็นจริง (หากบรรทัดปัจจุบัน$0ไม่เหมือนกันx) บรรทัดนั้นจะถูกพิมพ์เนื่องจากการกระทำเริ่มต้นสำหรับ awk บนนิพจน์ที่แท้จริงคือการพิมพ์ บรรทัดแรก ( NR==1) xจะถูกบันทึกเป็น เนื่องจากสิ่งนี้จะทำหลังจากตรวจสอบว่าบรรทัดปัจจุบันตรงกันหรือไม่xซึ่งจะทำให้แน่ใจได้ว่าบรรทัดแรกจะถูกพิมพ์ด้วย


ฉันไม่ต้องการรู้แนวคิดบรรทัดแรกเนื่องจากมันทำให้เป็นสคริปต์ทั่วไปสำหรับกล่องเครื่องมือของคุณ
Mark Stewart

1
เมธอด awk นั้นสร้างรายการอาร์เรย์ว่าง / เท็จต่อบรรทัดที่ต่างกัน สำหรับบรรทัด 4M หากแตกต่างกันทั้งหมด (ไม่ชัดเจนจาก Q) และค่อนข้างสั้น (ปรากฏดังนั้น) นี่อาจโอเค แต่ถ้ามีเส้นมากขึ้นหรือนานกว่านี้อาจทำให้แทรชหรือตายได้ !($0 in a)การทดสอบโดยไม่ต้องสร้างและหลีกเลี่ยงสิ่งนี้หรือ awk สามารถทำตรรกะแบบเดียวกับที่คุณมีสำหรับ perl: '$0!=x; NR==1{x=$0}'หรือถ้าบรรทัดส่วนหัวสามารถว่างเปล่า'NR==1{x=$0;print} $0!=x'
dave_thompson_085

1
@ dave_thompson_085 สร้างอาร์เรย์ต่อบรรทัดที่ไหน คุณหมายถึง!a[$0]อะไร ทำไมที่จะสร้างรายการในa?
terdon

1
เพราะนั่นเป็นวิธีการทำงานของ awk; ดูgnu.org/software/gawk/manual/html_node/…โดยเฉพาะ "NOTE"
dave_thompson_085

1
@ dave_thompson_085 ดีฉันจะถูกสาป! ขอบคุณฉันไม่ทราบว่า แก้ไขแล้ว
terdon

4

AWK เป็นเครื่องมือที่ค่อนข้างดีสำหรับวัตถุประสงค์ดังกล่าวเช่นกัน นี่คือตัวอย่างการใช้งานโค้ด:

$ awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt | head -n 10                                
ID  Data1  Data2
1    100    100
     100    200
3    200    100
1    100    100
     100    200
3    200    100
1    100    100
     100    200
3    200    100

ทำลายลง :

  • NR == 1 {print} บอกให้เราพิมพ์ไฟล์บรรทัดแรก
  • NR != 1 && $0!~/ID Data1 Data2/ ผู้ประกอบการตรรกะ&&บอก AWK พิมพ์เส้นที่ไม่เท่ากับ 1 ID Data1 Data2และไม่ได้มี สังเกตการขาด{print}ส่วนหนึ่ง; ใน awk หากประเมินเงื่อนไขการทดสอบเป็นจริงจะถือว่าเป็นบรรทัดที่จะพิมพ์
  • | head -n 10เป็นเพียงส่วนเพิ่มเติมเล็ก ๆ เพื่อ จำกัด เอาต์พุตให้เหลือเพียง 10 บรรทัดแรก ไม่เกี่ยวข้องกับชิ้นAWKส่วนนั้นใช้เพื่อการสาธิตเท่านั้น

หากคุณต้องการสิ่งนั้นในไฟล์ให้เปลี่ยนทิศทางเอาต์พุตของคำสั่งโดยผนวก> newFile.txtท้ายคำสั่งดังนี้:

awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt > newFile.txt

มันเป็นยังไงบ้าง ค่อนข้างดีจริงๆ:

$ time awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt > /dev/null                            
    0m3.60s real     0m3.53s user     0m0.06s system

ข้อความด้านข้าง

ไฟล์ตัวอย่างที่สร้างขึ้นนั้นใช้สำหรับการวนลูปจากหนึ่งถึงล้านและการพิมพ์สี่บรรทัดแรกของไฟล์ของคุณ (ดังนั้น 4 บรรทัดคูณล้านเท่ากับ 4 ล้านบรรทัด) ซึ่งใช้เวลา 0.09 วินาที

awk 'BEGIN{ for(i=1;i<=1000000;i++) printf("ID  Data1  Data2\n1    100    100\n     100    200\n3    200    100\n");  }' > rmLines.txt

โปรดทราบว่านี่จะพิมพ์บรรทัดID Data1 Data2 fooที่ไม่เหมือนกันกับส่วนหัว (ไม่น่าจะสร้างความแตกต่างในกรณีนี้ แต่คุณไม่มีทางรู้)
terdon

@terdon ใช่ถูกต้องแล้ว OP ระบุเพียงรูปแบบเดียวที่พวกเขาต้องการลบและตัวอย่างของเขาดูเหมือนจะสนับสนุนนั้น
Sergiy Kolodyazhnyy

3

อ๊ะปรับไปที่ส่วนหัวโดยอัตโนมัติ:

awk '( FNR == 1) {header=$0;print $0;}
     ( FNR > 1) && ($0 != header) { print $0;}'  file1  file2 ....

เช่นในบรรทัดแรกรับส่วนหัวและพิมพ์และบรรทัดต่อมาแตกต่างจากส่วนหัวที่ได้รับการพิมพ์

FNR = จำนวนระเบียนในไฟล์ปัจจุบันเพื่อให้คุณสามารถมีหลายไฟล์และมันจะทำเหมือนกันในแต่ละไฟล์


2

เพื่อความสมบูรณ์โซลูชัน Perl IMO จะดูสง่างามกว่า @terdon เล็กน้อย:

perl -i -p -e 's/^ID.*$//s if $. > 1' file

1
อ่า แต่จุดทั้งหมดของฉันคือการหลีกเลี่ยงความจำเป็นในการระบุรูปแบบและแทนที่จะอ่านจากบรรทัดแรก วิธีการของคุณก็จะลบเส้นใด ๆ IDที่เริ่มต้นด้วย คุณไม่รับประกันว่าจะไม่ลบบรรทัดที่ควรเก็บไว้ เนื่องจากคุณนำขึ้นสง่างามgไม่มีจุดหมายถ้าคุณใช้และ^ $อันที่จริงตัวเลือกทั้งหมดของคุณm///ไม่มีประโยชน์ที่นี่ยกเว้นs; พวกเขาเปิดใช้งานคุณสมบัติที่คุณไม่ได้ใช้ ดังนั้นเป็น$, s/^ID.*//sจะทำในสิ่งเดียวกัน
terdon

@terdon ยุติธรรมพอ คุณเป็นสากลมากขึ้น!
KWubbufetowicz

2

เพียงย้อนกลับไปถามคำถามนิดหน่อย ... ดูเหมือนว่าข้อมูลที่คุณป้อนอาจเป็นผลมาจากการรวมไฟล์ TSV หลายไฟล์เข้าด้วยกัน หากคุณสามารถสำรองขั้นตอนหนึ่งในขั้นตอนการประมวลผลของคุณ (ถ้าคุณเป็นเจ้าของหรือสามารถพูดคุยกับคนที่ทำ) คุณสามารถใช้เครื่องมือที่มีส่วนหัวรู้จักเพื่อเชื่อมข้อมูลในตอนแรกและทำให้ปัญหาที่ต้อง ลบบรรทัดส่วนหัวพิเศษ

ตัวอย่างเช่นการใช้มิลเลอร์ :

$ cat f1.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
$ cat f2.tsv
ID  Data1 Data2
4 100 100
$ cat f3.tsv
ID  Data1 Data2
5 200 200

$ cat f1.tsv f2.tsv  f3.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
ID  Data1 Data2
4 100 100
ID  Data1 Data2
5 200 200

$ mlr --tsvlite cat f1.tsv f2.tsv  f3.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
4 100 100
5 200 200

1
ขอบคุณสำหรับการเพิ่มชิ้นอาหารอันโอชะนี้ สิ่งนี้จะมีประโยชน์อย่างมากในอนาคตเนื่องจากท่อส่วนใหญ่ของฉันต้องการเข้าร่วมและรวมไฟล์จากตัวอย่างแต่ละตัวอย่าง
ออกุสตุสออกัสตัส
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.