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


คำตอบ:


2759

หากต้องการลบบรรทัดและพิมพ์เอาต์พุตไปที่มาตรฐาน:

sed '/pattern to match/d' ./infile

หากต้องการแก้ไขไฟล์โดยตรง - ใช้ไม่ได้กับ BSD sed:

sed -i '/pattern to match/d' ./infile

เหมือนกัน แต่สำหรับ BSD sed (Mac OS X และ FreeBSD) - ไม่ทำงานกับ GNU sed:

sed -i '' '/pattern to match/d' ./infile

หากต้องการแก้ไขไฟล์โดยตรง (และสร้างข้อมูลสำรอง) - ทำงานกับ BSD และ GNU sed:

sed -i.bak '/pattern to match/d' ./infile

13
ขอบคุณ แต่ดูเหมือนจะไม่ลบออกจากไฟล์ แต่พิมพ์เนื้อหาไฟล์ข้อความโดยไม่ใช้สตริงนั้น
Clockwork Orange

115
@A ลาน: ใช่คุณจะต้องเปลี่ยนเส้นทางออกอย่างใดอย่างหนึ่งไปยังแฟ้มใหม่กับสิ่งที่ต้องการsed '/pattern to match/d' ./infile > ./newfileหรือถ้าคุณต้องการที่จะทำแก้ไขในสถานที่แล้วคุณสามารถเพิ่ม-iธง sed sed -i '/pattern to match/d' ./infileในขณะที่ โปรดทราบว่าการ-iตั้งค่าสถานะต้องใช้ GNU sed และไม่สามารถพกพาได้
SiegeX

16
สำหรับบางส่วนของรสชาติของ sed; แฟล็ก "-i" ของ sed ต้องการส่วนขยายที่จะจัดเตรียม (เช่นsed -i.backup '/pattern to match/d' ./infile) ที่ทำให้ฉันเจอกับการแก้ไขแบบแทนที่
avelis

9
@SiegeX ยังดีกว่าอย่าใช้คำสั่งเหมือนกับsedไฟล์ที่ไม่ได้ควบคุมเวอร์ชัน
MatrixFrog

84
หนึ่งทราบมากขึ้นสำหรับผู้ใช้ Mac OS X: ด้วยเหตุผลบางอย่างธง -i sed -i '' '/pattern/d' ./infileต้องโต้แย้งที่จะผ่านถึงแม้ว่ามันจะเป็นเพียงสตริงที่ว่างเปล่าเช่น
geerlingguy

631

มีวิธีอื่น ๆ ในการลบบรรทัดด้วยสตริงที่ระบุนอกเหนือจากsed:

AWK

awk '!/pattern/' file > temp && mv temp file

ทับทิม (1.9+)

ruby -i.bak -ne 'print if not /test/' file

Perl

perl -ni.bak -e "print unless /pattern/" file

เชลล์ (bash 3.2 ขึ้นไป)

while read -r line
do
  [[ ! $line =~ pattern ]] && echo "$line"
done <file > o
mv o file

greu GNU

grep -v "pattern" file > temp && mv temp file

และแน่นอนsed(การพิมพ์ผกผันนั้นเร็วกว่าการลบจริง):

sed -n '/pattern/!p' file

4
วิธีการลบบรรทัดโดยเฉพาะอย่างยิ่งกับรูปแบบและยังบรรทัดด้านบนทันทีหรือไม่ ฉันมีข้อมูลที่ดีกับหลายพันบรรทัดในข้อมูลที่แตกต่างกัน
oortcloud_domicile

1
บน OS / X รูปแบบของเชลล์ไม่ได้รักษาช่องว่างนำหน้า แต่รูปแบบ grep -v ทำงานได้ดีสำหรับฉัน
Paul Beusterien

13
sedตัวอย่างมีพฤติกรรมที่แตกต่างกันก็เพียง greps! sed -n -i '/pattern/!p' fileมันควรจะเป็นสิ่งที่ชอบ
caesarsol

8
เวอร์ชัน grep ไม่ทำงานเมื่อทุกบรรทัดตรงกับรูปแบบ ทำได้ดีกว่า: grep -v "pattern" file > temp; mv temp fileสิ่งนี้อาจใช้กับตัวอย่างอื่น ๆ บางอย่างขึ้นอยู่กับค่าส่งคืน
Chris Maes

1
"การพิมพ์ผกผันนั้นเร็วกว่าการลบจริง" - ไม่ได้อยู่ในเครื่องของฉัน (2012 MacBook Air, OS X 10.13.2) สร้างไฟล์: seq -f %f 10000000 >foo.txt. sed d: time sed -i '' '/6543210/d' foo.txtจริง 0m9.294s sed! p: time sed -i '' -n '/6543210/!p' foo.txtจริง 0m13.671s (สำหรับไฟล์ขนาดเล็กความแตกต่างก็ใหญ่กว่า)
jcsahnwaldt พูดว่า GoFundMonica

252

คุณสามารถใช้ sed เพื่อแทนที่บรรทัดในไฟล์ อย่างไรก็ตามดูเหมือนว่าจะช้ากว่าการใช้ grep มากสำหรับ inverse ไปเป็นไฟล์ที่สองแล้วย้ายไฟล์ที่สองไปที่ต้นฉบับ

เช่น

sed -i '/pattern/d' filename      

หรือ

grep -v "pattern" filename > filename2; mv filename2 filename

คำสั่งแรกใช้เวลานานกว่าเครื่องของฉัน 3 เท่า


19
โหวตคำตอบของคุณด้วยเช่นกันเพราะคุณลองเปรียบเทียบประสิทธิภาพ!
anuragw

4
+1 สำหรับตัวเลือกการเสนอเพื่อเขียนทับไฟล์ปัจจุบันด้วยบรรทัด grep
Rhyuk

2
โซลูชัน 'grep' ตัวที่สองนั้นดีกว่าสำหรับไฟล์ขนาดใหญ่ด้วย
simoes

3
ฉันอยากรู้ว่าความแตกต่างของการแสดงจะเป็นอย่างไรsed '/pattern/d' filename > filename2; mv filename2 filename
Pete

8
(ใช้ ubuntu / usr / share / dict / words) grep และ mv: 0.010s | อยู่ในสถานที่: 0.197s | sed และ mv: 0.031s
ReactiveRaven

77

วิธีง่ายๆในการทำกับ GNU sed:

sed --in-place '/some string here/d' yourfile

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

3
+1 สำหรับ --in-place flag ฉันต้องการทดสอบไฟล์ที่ได้รับอนุญาต (ต้องทำการขัดถูผู้ใช้บางคน)
Bee Kay

8
โปรดทราบว่าตัวเลือกแบบยาวนั้นมีเฉพาะใน GNU เท่านั้น ผู้ใช้ Mac และ BSD จะต้องติดตั้ง gsed เพื่อทำเช่นนี้
Matt

เคล็ดลับอื่น: ถ้า regex ของคุณไม่ตรงกันลอง-rตัวเลือก (หรือ-Eขึ้นอยู่กับรุ่นของคุณ) ซึ่งจะช่วยให้การใช้งานของ metacharacters regex ไม่+, ?, และ{...} (...)
rjh

นี่เป็นคำตอบที่ถูกต้องเมื่อดิสก์ของคุณไม่มีพื้นที่เหลือและคุณไม่สามารถคัดลอกข้อความไปยังไฟล์อื่น คำสั่งนี้ทำในสิ่งที่ถูกสอบสวน?
ferreirabraga

38

คุณอาจพิจารณาใช้ex(ซึ่งเป็นตัวแก้ไขตามคำสั่ง Unix มาตรฐาน):

ex +g/match/d -cwq file

ที่อยู่:

  • +ดำเนินการให้คำสั่ง Ex ( man ex) เช่นเดียวกับ-cที่ดำเนินการwq(เขียนและออก)
  • g/match/d- คำสั่ง Ex เพื่อลบบรรทัดที่กำหนดให้matchดู: Power of g

ตัวอย่างข้างต้นเป็นวิธีการที่สอดคล้องกับ POSIX สำหรับในสถานที่การแก้ไขไฟล์ตามนี้โพสต์ที่ Unix.SEและPOSIX exข้อกำหนดสำหรับ


ข้อแตกต่างsedคือ:

sedเป็นS Tream ED itor ไม่ใช่ตัวแก้ไขไฟล์ BashFAQ

หากคุณไม่ได้รับรหัสที่ไม่สามารถแปลได้โอเวอร์เฮดของ I / O และผลข้างเคียงอื่น ๆ ดังนั้นโดยทั่วไปพารามิเตอร์บางตัว (เช่นในตำแหน่ง / -i) เป็นส่วนขยาย FreeBSD ที่ไม่ได้มาตรฐานและอาจไม่สามารถใช้ได้กับระบบปฏิบัติการอื่น


5
มันยอดเยี่ยมมาก ... เมื่อฉันman exทำให้คนvimนั้นดูเหมือนว่ามันexเป็นส่วนหนึ่งของกลุ่ม Vim ... ถ้าฉันเข้าใจถูกต้องนั่นหมายความว่ารูปแบบของไวยากรณ์matchคือvimregex.comซึ่งคล้ายกัน แต่แตกต่างจากรสชาติ POSIX และ PCRE หรือไม่
Anentropic

1
:g เป็นPOSIX ที่สอดคล้องกับคำสั่งที่มีบางส่วนที่แตกต่างกันเล็กน้อย ฉันคิดว่า PCRE ขึ้นอยู่กับมัน
kenorb

16

ฉันกำลังดิ้นรนกับสิ่งนี้ใน Mac นอกจากนี้ฉันต้องใช้การเปลี่ยนตัวแปร

ดังนั้นฉันจึงใช้:

sed -i '' "/$pattern/d" $file

โดยที่$fileเป็นไฟล์ที่ต้องการลบและ$patternเป็นรูปแบบที่จะจับคู่สำหรับการลบ

ฉันเลือก''จากความคิดเห็นนี้

สิ่งที่ต้องทราบที่นี่คือการใช้คำพูดสอง"/$pattern/d"ใน ตัวแปรจะไม่ทำงานเมื่อเราใช้เครื่องหมายคำพูดเดี่ยว


3
Mac sedต้องการพารามิเตอร์หลังจาก-iนั้นดังนั้นหากคุณไม่ต้องการสำรองข้อมูลคุณยังต้องเพิ่มสตริงว่าง:-i ''
wisbucky

sed -i "/$pattern/d" $fileสำหรับการใช้เปลือก ขอบคุณสำหรับคำตอบ.
ashwaqar

14

ฉันสร้างเกณฑ์มาตรฐานขนาดเล็กที่มีไฟล์ซึ่งมีประมาณ 345,000 บรรทัด วิธีที่grepดูเหมือนจะเร็วกว่าsedวิธีประมาณ 15 เท่าในกรณีนี้

ฉันลองทั้งโดยใช้และไม่มีการตั้งค่า LC_ALL = C ดูเหมือนว่าจะไม่เปลี่ยนการกำหนดเวลาอย่างมีนัยสำคัญ สตริงการค้นหา (CDGA_00004.pdbqt.gz.tar) อยู่ในตำแหน่งกึ่งกลางของไฟล์

นี่คือคำสั่งและเวลา:

time sed -i "/CDGA_00004.pdbqt.gz.tar/d" /tmp/input.txt

real    0m0.711s
user    0m0.179s
sys     0m0.530s

time perl -ni -e 'print unless /CDGA_00004.pdbqt.gz.tar/' /tmp/input.txt

real    0m0.105s
user    0m0.088s
sys     0m0.016s

time (grep -v CDGA_00004.pdbqt.gz.tar /tmp/input.txt > /tmp/input.tmp; mv /tmp/input.tmp /tmp/input.txt )

real    0m0.046s
user    0m0.014s
sys     0m0.019s

คุณอยู่บนแพลตฟอร์มใด คุณใช้ sed / perl / grep รุ่นใด
hagello

แพลตฟอร์มที่ฉันใช้คือ Linux (Gentoo) รุ่น sed คือ GNU sed v 4.2.2, รุ่น perl perl 5 (ฉันไม่สามารถบอกได้ว่าการแก้ไขใดที่ฉันใช้ในขณะที่ทำการทดสอบ) และ grep (GNU) คือรุ่น 3.0
Jadzia

14

คุณยังสามารถใช้สิ่งนี้:

 grep -v 'pattern' filename

ที่นี่-vจะพิมพ์เฉพาะที่นอกเหนือจากรูปแบบของคุณ (นั่นหมายถึงการสลับกลับกัน)


ฉันจะลบบรรทัดในไดเรกทอรีที่มีสตริงเฉพาะได้อย่างไร
namannimmo

13

ในการรับผลลัพธ์แบบ inplace เช่นเดียวกับgrepคุณสามารถทำได้:

echo "$(grep -v "pattern" filename)" >filename

4
สิ่งนี้ดีสำหรับbashเชลล์หรือคล้ายกันเท่านั้น (ไม่ใช่tcsh)
Esmit


4
perl -i    -nle'/regexp/||print' file1 file2 file3
perl -i.bk -nle'/regexp/||print' file1 file2 file3

คำสั่งแรกแก้ไขไฟล์ inplace (-i)

คำสั่งที่สองทำสิ่งเดียวกัน แต่เก็บสำเนาหรือสำรองข้อมูลของไฟล์ดั้งเดิมโดยเพิ่ม. bk ลงในชื่อไฟล์ (.bk สามารถเปลี่ยนเป็นอะไรก็ได้)



2

ในกรณีที่มีคนต้องการทำเพื่อจับคู่สตริงคุณสามารถใช้การ-wตั้งค่าสถานะใน grep - w ทั้งหมด นั่นคือตัวอย่างเช่นถ้าคุณต้องการลบบรรทัดที่มีหมายเลข 11 แต่เก็บบรรทัดด้วยหมายเลข 111:

-bash-4.1$ head file
1
11
111

-bash-4.1$ grep -v "11" file
1

-bash-4.1$ grep -w -v "11" file
1
111

นอกจากนี้ยังทำงานร่วมกับการ-fตั้งค่าสถานะถ้าคุณต้องการที่จะแยกรูปแบบที่แน่นอนหลายรายการในครั้งเดียว หาก "บัญชีดำ" เป็นไฟล์ที่มีหลายรูปแบบในแต่ละบรรทัดที่คุณต้องการลบออกจาก "ไฟล์":

grep -w -v -f blacklist file

ทำให้เข้าใจผิดเล็กน้อย -w, --word-regexp Select only those lines containing matches that form whole words.vs.-x, --line-regexp Select only those matches that exactly match the whole line. For a regular expression pattern, this is like parenthesizing the pattern and then surrounding it with ^ and $.
Sai


0

เพื่อแสดงข้อความที่ได้รับการรักษาในคอนโซล

cat filename | sed '/text to remove/d' 

เพื่อบันทึกข้อความที่ถือว่าเป็นไฟล์

cat filename | sed '/text to remove/d' > newfile

เพื่อผนวกข้อมูลข้อความที่ถือว่าเป็นไฟล์ที่มีอยู่

cat filename | sed '/text to remove/d' >> newfile

เพื่อจัดการกับข้อความที่ถือว่าอยู่แล้วในกรณีนี้ให้ลบบรรทัดที่ถูกลบออกไปมากขึ้น

cat filename | sed '/text to remove/d' | sed '/remove this too/d' | more

| moreจะแสดงข้อความในชิ้นหนึ่งหน้าในเวลา


0

คุณสามารถใช้ดีเก่าedที่จะแก้ไขไฟล์ในลักษณะคล้ายกับคำตอบexที่ใช้ ความแตกต่างที่สำคัญในกรณีนี้คือการedใช้คำสั่งผ่านอินพุตมาตรฐานไม่ใช่อาร์กิวเมนต์บรรทัดคำสั่งอย่างที่exสามารถทำได้ เมื่อใช้ในสคริปต์วิธีปกติในการรองรับสิ่งนี้คือการใช้printfไพพ์คำสั่ง:

printf "%s\n" "g/pattern/d" w | ed -s filename

หรือด้วย heredoc:

ed -s filename <<EOF
g/pattern/d
w
EOF
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.