ใช้ sed และ grep / egrep เพื่อค้นหาและแทนที่


97

ฉันใช้egrep -Rตามด้วยนิพจน์ทั่วไปที่มีประมาณ 10 สหภาพแรงงานเช่น: .jpg | .png | .gifฯลฯ สิ่งนี้ใช้ได้ดีตอนนี้ฉันต้องการแทนที่สตริงทั้งหมดที่พบด้วย.bmp

ฉันกำลังคิดบางอย่างเช่น

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

ดังนั้นปัญหาที่นี่คือฉันจะทำนิพจน์ทั่วไปของสหภาพที่คล้ายกันได้sedอย่างไรและฉันจะบอกให้บันทึกการเปลี่ยนแปลงในไฟล์ที่ได้รับอินพุตได้อย่างไร


2
ฉันพบคำถามนี้ขณะค้นหาวิธีค้นหาและแทนที่ไฟล์หลายไฟล์ในลำดับชั้นของไดเร็กทอรี สำหรับคนอื่น ๆ ในสถานการณ์ของฉันลองRPL
titaniumdecoy

ขอบคุณ rpl ใช้งานได้และง่ายต่อการจดจำ .. เพียงแค่ rpl old_string new_string target_files
cesarpachon

คำตอบ:


183

ใช้คำสั่งนี้:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep: ค้นหาบรรทัดที่ตรงกันโดยใช้นิพจน์ทั่วไปเพิ่มเติม

    • -l: แสดงเฉพาะชื่อไฟล์ที่ตรงกัน

    • -R: ค้นหาซ้ำผ่านไดเรกทอรีที่กำหนดทั้งหมด

    • -Z: ใช้\0เป็นตัวคั่นบันทึก

    • "\.jpg|\.png|\.gif": จับคู่สตริงใดสตริง".jpg"หนึ่ง".gif"หรือ".png"

    • .: เริ่มการค้นหาในไดเร็กทอรีปัจจุบัน

  • xargs: ดำเนินการคำสั่งด้วย stdin เป็นอาร์กิวเมนต์

    • -0: ใช้\0เป็นตัวคั่นบันทึก นี่เป็นสิ่งสำคัญในการจับคู่-Zของegrepและเพื่อหลีกเลี่ยงการถูกหลอกด้วยช่องว่างและบรรทัดใหม่ในชื่อไฟล์อินพุต

    • -l: ใช้หนึ่งบรรทัดต่อคำสั่งเป็นพารามิเตอร์

  • sed: the s tream ed itor

    • -i: แทนที่ไฟล์อินพุตด้วยเอาต์พุตโดยไม่ต้องสำรองข้อมูล

    • -e: ใช้อาร์กิวเมนต์ต่อไปนี้เป็นนิพจน์

    • 's/\.jpg\|\.gif\|\.png/.bmp/g': แทนที่เกิดขึ้นทั้งหมดของสตริง".jpg", ".gif"หรือ".png"กับ".bmp"


ทุกอย่างใช้งานได้ยกเว้น ในส่วน sed ฉันไม่เข้าใจว่าทำไมถึงสมเหตุสมผล ... ส่วน -l ของ xargs ทำให้ฉันมีข้อผิดพลาดดังนั้นฉันจึงเอามันออกมามันอาจเกี่ยวข้องกันได้หรือไม่?
Ori

ฉันพบว่าคำสั่งนี้เพิ่มขึ้นบรรทัดใหม่ต่อท้ายไฟล์ทั้งหมดที่ประมวลผล
titaniumdecoy

@titanumdecoy: ฉันไม่สามารถทำซ้ำพฤติกรรมนี้ได้ คุณใช้ sed รุ่นใดและคุณใช้ระบบปฏิบัติการใด
David Schmitt

1
@DavidSchmitt: คุณอาจต้องการใช้sed -rสำหรับการขยายนิพจน์ทั่วไป เมื่อถึงจุดนั้นรูปแบบจะตรงกับสิ่งที่ใช้ใน egrep และคุณอาจต้องการใส่ไว้ในตัวแปรเพื่อใช้ซ้ำ
bukzor

คำสั่งนี้ช่วยฉันประหยัดเวลาในการคัดลอกไฟล์ส่วนหัวออกจากแอปสำหรับไลบรารีที่ฉันสร้าง มันยอดเยี่ยมมาก :) นี่คือคำสั่งที่ฉันใช้ egrep -lRZ "\ .h $" | xargs -0 tar -cvf headers.tar | (cp headers.tar headers; cd headers; tar xf headers.tar;)
The Lazy Coder

11

จริงๆแล้วฉันชอบงานที่เหมาะสมมากเท่าที่ฉันชอบนี่เป็นงานสำหรับ perl - มันทรงพลังกว่าสำหรับหนึ่งสมุทรประเภทนี้โดยเฉพาะอย่างยิ่งการ "เขียนกลับไปว่ามันมาจากไหน" ( -iสวิตช์ของ perl ทำเพื่อ คุณและยังช่วยให้คุณสามารถเก็บเวอร์ชันเก่าไว้ได้เช่นมี. Bak ต่อท้ายเพียงใช้-i.bakแทน)

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

แทนที่จะทำงานที่ซับซ้อนใน sed (ถ้าเป็นไปได้ที่นั่น) หรือ awk ...


6
sed ใช้ -i เช่นเดียวกับ perl
Stobor

@Stobor - ฉันสาบานว่าฉันมีปัญหาที่การดำเนินการ perl เมื่อฉันป้อนสตริงการแทนที่ regex ทำในสิ่งที่ฉันต้องการไม่เหมือนกับ sed แม้ว่าฉันจะให้ตัวเลือก regex เป็นsed.. ฉันคิดว่าฉันลืมแฟล็กบางอย่างเพื่อ sed หรือ มันมีข้อ จำกัด
meder omuraliev

10

อีกวิธีหนึ่งในการทำเช่นนี้

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

ความช่วยเหลือเกี่ยวกับคำสั่งข้างต้น

การค้นหาจะทำการค้นหาให้คุณในไดเร็กทอรีปัจจุบันที่ระบุโดย .

-name ชื่อของไฟล์ในกรณีของฉัน pom.xml สามารถให้ไวลด์การ์ดได้

-exec ดำเนินการ

sed โปรแกรมแก้ไขสตรีม

-i ละเว้นกรณี

s ใช้สำหรับทดแทน

/4.6.0.../ สตริงที่จะค้นหา

/5.0.0.../ สตริงที่จะถูกแทนที่


อาจจะไม่ทรงพลังเท่า - แต่นี่เป็นเรื่องง่ายสำหรับฉันที่จะเข้าใจมากกว่าคำตอบที่ยอมรับ
icc97

5

ฉันไม่สามารถรับคำสั่งใด ๆ ในหน้านี้ให้ทำงานให้ฉันได้: โซลูชัน sed ได้เพิ่มบรรทัดใหม่ที่ส่วนท้ายของไฟล์ทั้งหมดที่ประมวลผลและโซลูชัน perlไม่สามารถยอมรับอาร์กิวเมนต์จากการค้นหาได้เพียงพอ ฉันพบโซลูชันนี้ซึ่งทำงานได้อย่างสมบูรณ์:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

นี้จะ recurse ลงต้นไม้ไดเรกทอรีปัจจุบันและแทนที่search_regexด้วยreplacement_stringในไฟล์ใด ๆ ที่ลงท้ายด้วยหรือ.h.m

ฉันยังใช้rplเพื่อจุดประสงค์นี้ในอดีต


0

ลองทำบางอย่างโดยใช้ for loop

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

ไม่สวย แต่ควรทำ


3
xargs เหมาะสมกว่าที่นี่
Nathan Fellman

1
และการใช้|while read iรูปแบบนี้จะเปิดใช้งานการสตรีมและหลีกเลี่ยงข้อ จำกัด ด้านความยาวเมื่อผลลัพธ์ของ egrep ยาวเกินไป
David Schmitt

0

กรณีการใช้งานของฉันคือฉันต้องการแทนที่ foo:/Drive_Letterด้วยfoo:/bar/baz/xyz ในกรณีของฉันฉันสามารถทำได้ด้วยรหัสต่อไปนี้ ฉันอยู่ในตำแหน่งไดเร็กทอรีเดียวกับที่มีไฟล์จำนวนมาก

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

หวังว่าจะช่วยได้

อัปเดต s | foo: / Drive_letter: | foo: / ba / baz / xyz | g


คุณสามารถใช้ตัวคั่นอื่น ๆ สำหรับคำสั่ง sed และการทำเช่นนั้นจะทำให้ pathnames มาก nicer:sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g'
เควิน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.