ค้นหาและแทนที่ไฟล์และไฟล์เขียนทับไม่ทำงาน แต่จะลบไฟล์


604

ฉันต้องการเรียกใช้ค้นหาและแทนที่ไฟล์ HTML ผ่านบรรทัดคำสั่ง

คำสั่งของฉันมีลักษณะเช่นนี้:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html

เมื่อฉันเรียกใช้และดูไฟล์ในภายหลังมันว่างเปล่า มันลบเนื้อหาของไฟล์ของฉัน

เมื่อฉันเรียกใช้หลังจากเรียกคืนไฟล์อีกครั้ง:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

stdoutเป็นเนื้อหาของแฟ้มและค้นหาและแทนที่ได้รับการดำเนินการ

ทำไมสิ่งนี้จึงเกิดขึ้น


13
ทางเลือก Perl:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Gjorgji Tashkovski

sedคำสั่งที่เกี่ยวข้องมากในการค้นหาสตริงและแทนที่ทั้งบรรทัด: stackoverflow.com/questions/11245144/…
cregox

คำตอบ:


917

เมื่อเชลล์เห็น > index.htmlในบรรทัดคำสั่งจะเปิดไฟล์index.htmlสำหรับการเขียนเช็ดออกเนื้อหาก่อนหน้านี้ทั้งหมด

ในการแก้ไขปัญหานี้คุณต้องผ่าน-iตัวเลือกเพื่อsedทำการเปลี่ยนแปลงแบบอินไลน์และสร้างการสำรองข้อมูลของไฟล์ต้นฉบับก่อนทำการเปลี่ยนแปลงแบบแทนที่:

sed -i.bak s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

หากไม่มี. bak คำสั่งจะล้มเหลวในบางแพลตฟอร์มเช่น Mac OSX


20
การพูดtruncates the fileแทนที่จะopens the fileทำให้ชัดเจนขึ้น
Mikel

12
อย่างน้อยบน mac ของฉันคำแนะนำแรกใช้ไม่ได้ ... ถ้าคุณกำลังทำการแทนที่ไฟล์คุณต้องระบุนามสกุล อย่างน้อยคุณสามารถส่งผ่านส่วนขยายที่มีความยาวเป็นศูนย์ได้: sed -i 's / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html ของ sed -i' s
Tom Lianza

5
สำหรับตัวแปร sed -i.bak 's /' $ search '/' $ แทนที่ '/ g' index.html
Fatima Zohra

33
บน osx ให้ใช้สตริงว่าง '' เป็นพารามิเตอร์สำหรับ -i เช่น:sed -i '' 's/blah/xx/g'
Pierre Houston

4
แต่.bakหลังจากคุณเป็นsed -iอะไร
Patrizio Bertoni

210

รูปแบบอื่นที่มีประโยชน์คือ:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html

ซึ่งมีผลเหมือนกันมากโดยไม่ใช้-iตัวเลือกและยังหมายความว่าหากสคริปต์ sed ล้มเหลวด้วยเหตุผลบางอย่างไฟล์อินพุตจะไม่ถูกอุดตัน นอกจากนี้หากการแก้ไขประสบความสำเร็จจะไม่มีไฟล์สำรองวางอยู่ สำนวนแบบนี้มีประโยชน์ใน Makefiles

seds ค่อนข้างมากมี-iตัวเลือก แต่ไม่ใช่ทั้งหมด posix sed เป็นสิ่งที่ไม่ทำ หากคุณตั้งเป้าหมายที่จะพกพาได้ดีควรหลีกเลี่ยง


9
+1 สำหรับไฟล์ที่ไม่มีข้อมูลสำรองวางอยู่รอบ ๆ และไม่อุดตันไฟล์อินพุตหากการแก้ไขล้มเหลว ทำงานได้อย่างไม่มีที่ติบน mac
Mike Grace

ทำงานให้ฉันอย่างสมบูรณ์แบบ ขอบคุณ! (สำหรับ Mac)
สนใจ

1
สิ่งนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับฉันที่อยู่บน Ubuntu Server 14.04 sed -i ทำให้ไฟล์ไม่มี
Chris Giddings

2
การปรับปรุงเล็กน้อยมาก:... && mv index.html{.tmp,}
EdwardGarson

5
@EdwardGarson แน่นอนว่าอาจเป็นสิ่งที่ฉันจะใช้ถ้าฉันพิมพ์มัน - ฉันยอมรับว่ามันเป็น neater - แต่sh(ถ้าฉันจำได้อย่างถูกต้อง) ไม่ได้มี{...}การขยายตัวที่ ใน Makefile คุณอาจกำลังใช้งานshมากกว่าbashดังนั้นหากคุณต้องการพกพา (หรือความเป็นไปได้) คุณจะต้องหลีกเลี่ยงการสร้างสิ่งนั้น
Norman Gray

95
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

นี่เป็นการแทนที่แบบทั่วโลกในไฟล์ index.html การอ้างอิงสตริงป้องกันปัญหาช่องว่างในแบบสอบถามและการแทนที่


57

ใช้ตัวเลือก sed's -i เช่น

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html

สิ่งนี้หมายความว่า? sed: -i ไม่สามารถใช้กับ stdin
sheetal

2
อย่าลืมล้อมรูปแบบของคุณด้วยเครื่องหมายคำพูดหากมีช่องว่าง's/STRING_TO_REPLACE/REPLACE_WITH/g'
Doug Thompson

@ sheetal: -iทำการแก้ไขไฟล์แบบแทนที่ดังนั้นจึงไม่มีเหตุผลที่จะรวมเข้ากับอินพุตstdin
mklement0

สิ่งนี้อาจใช้งานได้กับ macOS แต่ไม่ได้อยู่ใน Arch Linux สำหรับฉัน
xdevs23

หากไม่มี -e คำตอบที่ยอมรับจะใช้ไม่ได้กับ MacOS, Catalina ด้วย -e มันใช้งานได้
cwhiii

18

วิธีเปลี่ยนหลายไฟล์ (และบันทึกการสำรองข้อมูลแต่ละไฟล์เป็น * .bak):

perl -p -i -e "s/\|/x/g" *  

จะใช้ไฟล์ทั้งหมดในไดเรกทอรีและแทนที่|ด้วยx สิ่งนี้เรียกว่า "พายเพิร์ล" (ง่ายเหมือนพาย)


1
ดีที่เห็นบางคนเต็มใจที่จะดูคำแถลงปัญหาไม่ใช่เพียงแค่แท็ก OP ไม่ได้ระบุsedว่าเป็นข้อกำหนดเพียงใช้เป็นเครื่องมือที่ได้ลองแล้ว
user7412956


6
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

หากคุณมีลิงค์ที่จะเพิ่มลองสิ่งนี้ ค้นหา URL ข้างต้น (เริ่มต้นด้วย https และลงท้ายด้วย com ที่นี่) และแทนที่ด้วยสตริง URL ฉันใช้ตัวแปร$pub_urlที่นี่ sที่นี่หมายถึงการค้นหาและgหมายถึงการแทนที่ทั่วโลก

มันได้ผล !


6

คำเตือน: นี่เป็นวิธีที่อันตราย! มันละเมิดบัฟเฟอร์ i / o ใน linux และด้วยตัวเลือกเฉพาะของบัฟเฟอร์ที่จัดการเพื่อทำงานกับไฟล์ขนาดเล็ก มันเป็นความอยากรู้ที่น่าสนใจ แต่อย่าใช้มันในสถานการณ์จริง!

นอกจากนี้-iตัวเลือกของsed คุณสามารถใช้ยูทิลิตี้tee

จากman:

ที - อ่านจากอินพุตมาตรฐานและเขียนไปยังเอาต์พุตและไฟล์มาตรฐาน

ดังนั้นการแก้ปัญหาจะเป็น:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee | tee index.html

- ที่นี่teeมีการทำซ้ำเพื่อให้แน่ใจว่ามีการบัฟเฟอร์ จากนั้นคำสั่งทั้งหมดในไปป์ไลน์จะถูกบล็อกจนกว่าจะได้รับอินพุตบางส่วนเพื่อใช้งาน แต่ละคำสั่งในไปป์ไลน์เริ่มต้นเมื่อคำสั่งอัพสตรีมได้เขียน 1 บัฟเฟอร์ของไบต์ (ขนาดถูกกำหนดไว้ที่ใดที่หนึ่ง ) ไปยังอินพุตของคำสั่ง ดังนั้นคำสั่งสุดท้ายtee index.htmlที่เปิดไฟล์สำหรับการเขียนและทำให้มันว่างเปล่ามันทำงานหลังจากขั้นตอนการอัพสตรีมเสร็จสิ้นและเอาต์พุตอยู่ในบัฟเฟอร์ภายในไพพ์ไลน์

เป็นไปได้มากว่าสิ่งต่อไปนี้จะไม่ทำงาน:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html

- มันจะรันทั้งสองคำสั่งของไพพ์ไลน์ในเวลาเดียวกันโดยไม่มีการบล็อคใด ๆ (โดยไม่ปิดกั้นท่อควรผ่านเส้นไบต์โดยสายแทนของบัฟเฟอร์โดยบัฟเฟอร์. เช่นเดียวกับเมื่อคุณเรียกใช้cat | sed s/bar/GGG/. โดยไม่ปิดกั้นมันโต้ตอบมากขึ้นและมักจะท่อเพียง 2 คำสั่งทำงานโดยไม่ต้องบัฟเฟอร์และการปิดกั้น. ท่ออีกต่อไปเป็น buffered.) The tee index.htmlประสงค์ เปิดไฟล์สำหรับเขียนและมันจะถูกทำให้ว่างเปล่า อย่างไรก็ตามหากคุณเปิดบัฟเฟอร์เสมอรุ่นที่สองจะทำงานเช่นกัน


3
ไฟล์เอาต์พุตของทีก็จะเปิดขึ้นทันทีทำให้เกิด index.html ที่ว่างสำหรับคำสั่งทั้งหมด
sjngm

3
นี้จะเสียหายแฟ้มใส่ใด ๆ ที่มีขนาดใหญ่กว่าบัฟเฟอร์ท่อ (ซึ่งโดยปกติจะ 64KB) (@sjngm: ไฟล์จะไม่ถูกตัดทอนทันทีเช่น>กัน แต่ประเด็นก็คือมันเป็นวิธีการแก้ปัญหาที่ขาดซึ่งมีแนวโน้มที่จะทำให้ข้อมูลสูญหาย)
mklement0

4

มีปัญหากับคำสั่ง

sed 'code' file > file

คือว่าfileถูกตัดทอนโดยเชลล์ก่อนที่ sed จะได้รับการประมวลผลจริง เป็นผลให้คุณได้รับไฟล์ว่างเปล่า

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

tmp=$(sed 'code' file); echo -n "$tmp" > file

ดีกว่าการใช้printfแทนechoเนื่องจากechoมีแนวโน้มที่จะประมวลผล\\เหมือน\ในบางเชลล์ (เช่นเส้นประ):

tmp=$(sed 'code' file); printf "%s" "$tmp" > file

1
+1 สำหรับการสงวนลิงก์ นอกจากนี้ยังใช้งานได้กับไฟล์ชั่วคราว:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
dashohoxha

3

และedคำตอบ:

printf "%s\n" '1,$s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' w q | ed index.html

หากต้องการย้ำคำตอบของ codaddict ใดๆ เชลล์จะจัดการการเปลี่ยนเส้นทางก่อนโดยลบไฟล์ "input.html" จากนั้นเชลล์จะเรียกคำสั่ง "sed" ผ่านไฟล์ที่ว่างเปล่าในขณะนี้


2
คำถามที่รวดเร็วว่าทำไมผู้คนถึงให้คำตอบed"รุ่น" ต่อไป sedมันทำงานได้เร็วขึ้น?
cregox

6
บางคนsedไม่ได้ใช้งาน-iเพื่อแก้ไขในสถานที่ edแพร่หลายและให้คุณบันทึกการแก้ไขของคุณลงในไฟล์ต้นฉบับ นอกจากนี้ยังเป็นเรื่องดีที่จะมีเครื่องมือมากมายในชุดของคุณ
เกล็นแจ็คแมน

โอเคดี. ดังนั้นประสิทธิภาพที่ชาญฉลาดพวกเขาเป็นแบบเดียวกันกับที่ฉันคิดเอาไว้ ขอบคุณ!
cregox


0

ฉันกำลังค้นหาตัวเลือกที่ฉันสามารถกำหนดช่วงของเส้นและพบคำตอบ ตัวอย่างเช่นฉันต้องการเปลี่ยน host1 เป็น host2 จากบรรทัด 36-57

sed '36,57 s/host1/host2/g' myfile.txt > myfile1.txt

คุณสามารถใช้ตัวเลือก gi เช่นกันเพื่อละเว้นกรณีตัวอักษร

sed '30,40 s/version/story/gi' myfile.txt > myfile1.txt

0

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

เพียงแค่ให้สคริปต์ของคุณกระจายเอาต์พุตไปที่บรรทัดรับคำสั่งแทนที่จะเขียนลงในไฟล์ตัวอย่างเช่น:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

หรือ

less index.html | sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g 

วิธีนี้คุณสามารถดูและตรวจสอบผลลัพธ์ของคำสั่งโดยไม่ทำให้ไฟล์ของคุณถูกตัดทอน

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