วิธีลบคำเฉพาะจากบรรทัดของไฟล์ข้อความ?


13

ไฟล์ข้อความของฉันมีลักษณะเช่นนี้:

Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341

ตอนนี้ฉันต้องการลบLiquid penetration 95% mass (m)จากบรรทัดของฉันเพื่อรับค่าเท่านั้น ฉันควรทำอย่างไร


3
อย่างง่ายดายgrep -o '[^[:space:]]\+$' file
Avinash Raj

@AvinashRaj: จนถึงตอนนี้วิธีนี้จะได้รับ'putty medal' :)
pa4080

2
@ pa4080 อย่างน้อยที่สุดสำหรับอินพุตที่ฉันทดสอบ (เส้น 10M) วิธีการทั่วไปของ Avinash Raj สามารถทำให้ลำดับความสำคัญเร็วขึ้นโดยใช้ PCRE (ฉันสามารถยืนยันได้ว่าเครื่องยนต์ไม่ใช่รูปแบบมีความรับผิดชอบเนื่องจาก grep ของ GNU ยอมรับ\S+$ด้วย-Eหรืออย่างใดอย่างหนึ่ง-P) ดังนั้นโซลูชันประเภทนี้จึงไม่ช้าอย่างแท้จริง แต่ฉันยังคงไม่สามารถเข้าถึงได้ทุกที่ใกล้กับวิธีการของαғsнιηcutซึ่งได้รับรางวัลมาตรฐานของคุณเช่นกัน
Eliah Kagan

คำตอบ:


22

หากมีเพียง=เครื่องหมายเดียวคุณสามารถลบทุกอย่างก่อนหน้าและรวมถึง=สิ่งนี้:

$ sed -r 's/.* = (.*)/\1/' file
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

หากคุณต้องการเปลี่ยนไฟล์ต้นฉบับให้ใช้-iตัวเลือกหลังจากทดสอบ:

sed -ri 's/.* = (.*)/\1/' file

หมายเหตุ

  • -rใช้ ERE ดังนั้นเราไม่ต้องหลบหนี(และ)
  • s/old/newแทนที่oldด้วยnew
  • .* จำนวนอักขระใด ๆ
  • (things)บันทึกthingsการ backreference ในภายหลังด้วย\1, \2ฯลฯ

ขอบคุณมันใช้งานได้ ฉันใช้คำสั่งนี้เพื่อเขียนทับไฟล์ที่มีอยู่: sed -i -r 's /.*= (. *) / \ 1 /' time.txt คุณช่วยอธิบายได้มั้ย
OE

ทำไมไม่หลีกเลี่ยงการกลับมา? s/^.*= //จะทำงานได้ดีเท่าเทียมกันเนื่องจากค่าที่ถูกต้องอยู่ที่จุดสิ้นสุดของบรรทัด
jpaugh

@jpaugh ส่วนหนึ่งเป็นเพราะมันสายเกินไปที่จะเปลี่ยนคำตอบของฉันซึ่งเป็นคำตอบแรกที่โพสต์ - คนอื่นได้ให้วิธีการที่คุณพูดถึงและวิธีอื่น ๆ ที่มีประสิทธิภาพมากขึ้นสำหรับกรณีนี้ :) แต่อาจจะแสดงวิธีใช้\1ฯลฯ จอดที่คำถามนี้เมื่อค้นหาผู้ที่ไม่มีปัญหาง่ายๆเช่นนี้
Zanna

@Zanna มันเป็นเรื่องธรรมดามากกว่าอย่างน้อย
jpaugh

21

นี่คืองานสำหรับawk; สมมติว่าค่าเกิดขึ้นในเขตข้อมูลสุดท้ายเท่านั้น (ตามตัวอย่างของคุณ):

awk '{print $NF}' file.txt
  • NFเป็นawkตัวแปรขยายจำนวนฟิลด์ในเร็กคอร์ด (บรรทัด) ดังนั้น$NF(หมายเหตุ$ด้านหน้า) มีค่าของฟิลด์สุดท้าย

ตัวอย่าง:

% cat temp.txt 
Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341

% awk '{print $NF}' temp.txt
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

13

ฉันตัดสินใจที่จะเปรียบเทียบโซลูชันต่าง ๆ ที่ระบุไว้ที่นี่ เพื่อจุดประสงค์นี้ฉันได้สร้างไฟล์ขนาดใหญ่ขึ้นอยู่กับเนื้อหาที่ได้จาก OP:

  1. ฉันสร้างไฟล์ง่าย ๆ ชื่อinput.file:

    $ cat input.file
    Liquid penetration 95% mass (m) = 0.000205348
    Liquid penetration 95% mass (m) = 0.000265725
    Liquid penetration 95% mass (m) = 0.000322823
    Liquid penetration 95% mass (m) = 0.000376445
    Liquid penetration 95% mass (m) = 0.000425341
    
  2. จากนั้นฉันก็ทำลูปนี้

    for i in {1..100}; do cat input.file | tee -a input.file; done
    
  3. หน้าต่างเทอร์มินัลถูกบล็อก ฉันดำเนินการkillall teeจากสถานีอื่น แล้วฉันจะตรวจสอบเนื้อหาของไฟล์โดยคำสั่งนี้และless input.file cat input.fileมันดูดียกเว้นบรรทัดสุดท้าย ดังนั้นฉันจึงลบบรรทัดสุดท้ายและสร้างสำเนาสำรอง: cp input.file{,.copy}(เพราะคำสั่งที่ใช้ตัวเลือกinplace )

  4. นับเป็นครั้งสุดท้ายของสายเป็นไฟล์ที่input.fileเป็น2 192 473 ฉันได้รับหมายเลขนั้นโดยคำสั่งwc:

    $ cat input.file | wc -l
    2192473
    

นี่คือผลลัพธ์ของการเปรียบเทียบ:

  • grep -o '[^[:space:]]\+$'

    $ time grep -o '[^ [: space:]] \ + $' input.file> output.file
    
    จริง 0m58.539s
    ผู้ใช้ 0m58.416s
    sys 0m0.108s
    
  • sed -ri 's/.* = (.*)/\1/'

    $ time sed -ri 's /.* = (. *) / \ 1 /' input.file
    
    จริง 0m26.936s
    ผู้ใช้ 0m22.836s
    sys 0m4.092s
    

    หรือถ้าเราเปลี่ยนเส้นทางไปยังไฟล์ใหม่คำสั่งจะเร็วกว่า:

    $ time sed -r 's /.* = (. *) / \ 1 /' input.file> output.file
    
    จริง 0m19.734 วินาที
    ผู้ใช้ 0m19.672s
    sys 0m0.056s
    
  • gawk '{gsub(".*= ", "");print}'

    $ time gawk '{gsub (". * =", ""); print}' input.file> output.file
    
    จริง 0m5.644s
    ผู้ใช้ 0m5.568s
    sys 0m0.072s
    
  • rev | cut -d' ' -f1 | rev

    $ time rev input.file | ตัด -d '' -f1 | rev> output.file
    
    จริง 0m3.703s
    ผู้ใช้ 0m2.108s
    sys 0m4.916s
    
  • grep -oP '.*= \K.*'

    $ time grep -oP '. * = \ K. *' input.file> output.file
    
    จริง 0m3.328s
    ผู้ใช้ 0m3.252s
    sys 0m0.072s
    
  • sed 's/.*= //' (ตามลำดับ-iตัวเลือกทำให้คำสั่งช้าลงสองสามครั้ง)

    $ time sed 's /.*= //' input.file> output.file
    
    จริง 0m3.310s
    ผู้ใช้ 0m3.212s
    sys 0m0.092s
    
  • perl -pe 's/.*= //' ( -iตัวเลือกไม่ได้สร้างความแตกต่างอย่างมากในการผลิตที่นี่)

    $ time perl -i.bak -pe 's /.*= //' input.file
    
    จริง 0m3.187s
    ผู้ใช้ 0m3.128s
    sys 0m0.056s
    
    $ time perl -pe 's /.*= //' input.file> output.file
    
    จริง 0m3.138s
    ผู้ใช้ 0m3.036s
    sys 0m0.100s
    
  • awk '{print $NF}'

    $ time awk '{print $ NF}' input.file> output.file
    
    จริง 0m1.251s
    ผู้ใช้ 0m1.164s
    sys 0m0.084s
    
  • cut -c 35-

    $ time cut -c 35- input.file> output.file
    
    จริง 0m0.352s
    ผู้ใช้ 0m0.284s
    sys 0m0.064s
    
  • cut -d= -f2

    $ time cut -d = -f2 input.file> output.file
    
    จริง 0m0.328s
    ผู้ใช้ 0m0.260s
    sys 0m0.064s
    

ที่มาของความคิด


2
ดังนั้นทางออกของฉันcut -d= -f2ชนะ ฮ่าฮ่า
αғsнιη

คุณสามารถให้ข้อมูลเพิ่มเติมเกี่ยวกับวิธีสร้างไฟล์นี้ได้หรือไม่? นอกจากนี้วิธีการที่wc -lเอาท์พุทสามตัวเลข? เมื่อไม่มีตัวเลือกอื่นถูกส่ง-lตัวเลือกควรระงับทุกอย่างยกเว้นการนับบรรทัด
Eliah Kagan

@EliahKagan เสร็จแล้ว ฉันได้อัพเดตคำตอบแล้ว
pa4080

อ่าฉันเข้าใจแล้ว - ช่องว่างเป็นตัวคั่นกลุ่มหลัก (เคยwcแสดงช่องว่างเหล่านั้นจริง ๆ หรือยังมีการตั้งค่าภาษาที่จะทำเช่นนั้น?) ขอบคุณสำหรับการอัปเดต!
Eliah Kagan

@EliahKagan: ในที่สุดฉันก็อ่านคำถามของคุณwcอีกครั้ง ฉันไม่ทราบว่าวันนี้ปัญญาของฉันอยู่ที่ใด แต่ฉันไม่เข้าใจพวกเขาจริงๆ ดังนั้นช่องว่างจึงเป็นตัวคั่นกลุ่มหลักและwcไม่ได้เพิ่ม :)
pa4080

12

ด้วยgrepและ-PสำหรับการมีPCRE(ตีความรูปแบบเป็นP erl- C ompatible R egular E XPRESSION) และ-oการพิมพ์รูปแบบการจับคู่อยู่คนเดียว การ\Kแจ้งเตือนจะไม่สนใจส่วนที่จับคู่มาก่อนหน้าตัวเอง

$ grep -oP '.*= \K.*' infile
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

หรือคุณสามารถใช้cutคำสั่งแทน

cut -d= -f2 infile

2
นอกเหนือจากการทำงานที่เร็วที่สุดของทุกวิธีการทดสอบในมาตรฐานของ pa4080 , วิธีการในคำตอบนี้ก็ยังเป็นผู้ชนะที่ชัดเจนในมาตรฐานขนาดเล็กฉันวิ่งทดสอบว่าวิธีการที่น้อยลง แต่ใช้ใส่ไฟล์ที่มีขนาดใหญ่ มันเร็วกว่าสิบเท่าของวิธีที่ฉันชอบ (และคำตอบของฉันส่วนใหญ่) cut
Eliah Kagan

11

เนื่องจากส่วนนำหน้าของบรรทัดมีความยาวเท่ากัน (34 อักขระ) คุณจึงสามารถใช้cut:

cut -c 35- < input.txt > output.txt

6

ย้อนกลับเนื้อหาของไฟล์ด้วยrevไพพ์เอาท์พุทลงในcutช่องว่างเป็นตัวคั่นและ 1 เป็นฟิลด์เป้าหมายจากนั้นย้อนกลับอีกครั้งเพื่อรับหมายเลขเดิม:

$ rev your_file | cut -d' ' -f1 | rev
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

5

นี่คือง่ายสั้นและง่ายต่อการเขียนเข้าใจและตรวจสอบและฉันชอบมัน:

grep -oE '\S+$' file

grepใน Ubuntuเมื่อเรียกใช้-Eหรือ-Pใช้ย่อ \sเพื่อหมายถึงอักขระช่องว่าง (ในทางปฏิบัติมักจะมีช่องว่างหรือแท็บ) และ\Sหมายถึงสิ่งที่ไม่ใช่ ใช้ปริมาณ+และยึดปลายของเส้น$ , รูปแบบ\S+$ตรงกับหนึ่งหรือมากกว่าไม่ใช่ช่องว่างที่ท้ายบรรทัด คุณสามารถใช้-Pแทน-E; ความหมายในกรณีนี้จะเหมือนกัน แต่แตกต่างกันเครื่องยนต์แสดงออกปกติจะใช้เพื่อให้พวกเขาอาจจะมีลักษณะการทำงานที่แตกต่างกัน

นี่เทียบเท่ากับวิธีแก้ปัญหาที่คอมเม้นต์ของ Avinash Raj (ด้วยไวยากรณ์ที่กระชับและง่ายกว่า):

grep -o '[^[:space:]]\+$' file

วิธีการเหล่านี้จะไม่ทำงานหากมีช่องว่างต่อท้ายหลังจำนวน พวกเขาสามารถปรับเปลี่ยนได้ดังนั้นพวกเขาก็ทำได้ แต่ฉันไม่เห็นจุดที่จะเข้าไปที่นี่ แม้ว่าบางครั้งก็ให้คำแนะนำที่จะพูดคุยแก้ปัญหาในการทำงานภายใต้กรณีอื่น ๆ ก็ไม่ได้ในทางปฏิบัติจะทำเช่นนั้นเกือบได้บ่อยเท่าที่คนมักจะคิดเพราะหนึ่งมักจะมีวิธีการที่จะรู้ว่าไม่มีซึ่งวิธีที่เข้ากันไม่ได้แตกต่างกันมากปัญหาอาจต้องในท้ายที่สุด ทั่วไป


ประสิทธิภาพเป็นบางครั้งการพิจารณาที่สำคัญ คำถามนี้ไม่ได้ระบุว่าอินพุตมีขนาดใหญ่มากและเป็นไปได้ว่าทุกวิธีที่โพสต์ที่นี่รวดเร็วพอ อย่างไรก็ตามในกรณีที่ต้องการความเร็วนี่เป็นเกณฑ์มาตรฐานขนาดเล็กในไฟล์อินพุตสิบล้านบรรทัด:

$ perl -e 'print((<>) x 2000000)' file > bigfile
$ du -sh bigfile
439M    bigfile
$ wc -l bigfile
10000000 bigfile
$ TIMEFORMAT=%R
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
819.565
$ time grep -oE '\S+$' bigfile > bigfile.out
816.910
$ time grep -oP '\S+$' bigfile > bigfile.out
67.465
$ time cut -d= -f2 bigfile > bigfile.out
3.902
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
815.183
$ time grep -oE '\S+$' bigfile > bigfile.out
824.546
$ time grep -oP '\S+$' bigfile > bigfile.out
68.692
$ time cut -d= -f2 bigfile > bigfile.out
4.135

ฉันวิ่งมันสองครั้งในกรณีที่คำสั่งสำคัญ (บางครั้งก็ทำเพื่องาน I / O หนัก) และเพราะฉันไม่มีเครื่องจักรที่ไม่ได้ทำสิ่งอื่นในพื้นหลังที่อาจบิดเบือนผลลัพธ์ จากผลลัพธ์เหล่านั้นฉันสรุปสิ่งต่อไปนี้อย่างน้อยเป็นการชั่วคราวและสำหรับไฟล์อินพุตที่ฉันใช้:

  • ว้าว! การส่งต่อ-P(เพื่อใช้PCRE ) แทน-G(ค่าเริ่มต้นเมื่อไม่ได้ระบุภาษา) หรือ-Eทำได้grepเร็วกว่าตามลำดับความสำคัญ ดังนั้นสำหรับไฟล์ขนาดใหญ่อาจใช้คำสั่งนี้ดีกว่าคำสั่งที่แสดงด้านบน:

    grep -oP '\S+$' file
  • ว้าว!! cutวิธีการในคำตอบของαғsнιη , เป็นมากกว่าลำดับความสำคัญได้เร็วกว่าแม้รุ่นเร็วขึ้นจากทางของฉัน! มันเป็นผู้ชนะในเกณฑ์มาตรฐานของ pa4080เช่นกันซึ่งครอบคลุมวิธีการมากกว่านี้ แต่มีการป้อนข้อมูลที่น้อยกว่า - และนี่คือเหตุผลที่ฉันเลือกวิธีอื่นรวมไว้ในการทดสอบของฉัน หากประสิทธิภาพมีความสำคัญหรือไฟล์มีขนาดใหญ่ฉันคิดว่าควรใช้วิธีของαғsнιηcut -d= -f2 filecut

    นอกจากนี้ยังทำหน้าที่เป็นเตือนว่าง่ายcutและpasteสาธารณูปโภคไม่ควรลืมและบางทีอาจจะควรจะแนะนำเมื่อบังคับแม้มีเครื่องมือที่มีความซับซ้อนมากขึ้นเช่นgrepที่มักจะมีการเสนอเป็นโซลูชั่นบรรทัดแรก (และที่ผมเองคุ้นเคยมากขึ้น ที่จะใช้)


4

perl- sแทนรูปแบบ/.*= /ด้วยสตริงว่าง//:

perl -pe 's/.*= //' input.file > output.file
perl -i.bak -pe 's/.*= //' input.file
  • จากperl --help:

    -e program        one line of program (several -e's allowed, omit programfile)
    -p                assume loop like -n but print line also, like sed
    -i[extension]     edit <> files in place (makes backup if extension supplied)
    

sed - แทนที่รูปแบบด้วยสตริงว่าง:

sed 's/.*= //' input.file > output.file

หรือ(แต่ช้ากว่าด้านบน) :

sed -i.bak 's/.*= //' input.file
  • ฉันพูดถึงวิธีการนี้เพราะมันเป็นไม่กี่ครั้งเร็วกว่าผู้ที่อยู่ใน Zanna ของคำตอบ

gawk- แทนที่รูปแบบ".*= "ด้วยสตริงว่าง"":

gawk '{gsub(".*= ", "");print}' input.file > output.file
  • จากman gawk:

    gsub(r, s [, t]) For each substring matching the regular expression r in the string t,
                     substitute the string s, and return the number of substitutions. 
                     If t is not supplied, use $0...
    
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.