sed บน OSX insert ที่บรรทัดหนึ่ง


24

ดังนั้นฉันจึงใช้ 'sed' บน linux มาระยะหนึ่งแล้ว แต่มีปัญหาเล็กน้อยในการพยายามใช้กับ OSX ตั้งแต่ 'POSIX sed' และ 'GNU sed' มีความแตกต่างกันเล็กน้อย ขณะนี้ฉันกำลังดิ้นรนกับวิธีการแทรกบรรทัดข้อความหลังจากหมายเลขบรรทัดที่แน่นอน (ในกรณีนี้สาย 4)

บน linux ฉันจะทำสิ่งนี้:

sed --in-place "4 a\  mode '0755'" file.txt

ดังนั้นใน OSX ฉันลองนี้:

sed -i "" "4 a\ mode '0755'" file.txt

อย่างไรก็ตามสิ่งนี้ทำให้ฉันมีข้อผิดพลาด 'ตัวละครพิเศษหลังจาก \ ที่ส่วนท้ายของคำสั่ง' ความคิดใดที่นี่มีอะไรผิดปกติ? ฉันพิมพ์ผิดหรือไม่? หรือฉันไม่เข้าใจความแตกต่างระหว่างรุ่นของ sed อีกไหม


2
สามารถยืนยันสิ่งนี้ไม่ให้ทำงานบน MacOS 10.6.8 ทำงานได้ดีบนเดเบียน 6.0.3
ทิงค์

คำตอบ:


24

พูดอย่างเคร่งครัดในสเปคสำหรับ POSIXsedต้องมีการขึ้นบรรทัดใหม่หลังจากที่a\:

[1addr]a\
text

เขียนข้อความไปยังเอาต์พุตมาตรฐานตามที่อธิบายไว้ก่อนหน้า

นี้จะทำให้การเขียนหนึ่งสมุทรบิตของความเจ็บปวดซึ่งอาจเป็นเหตุผลสำหรับการดังต่อไปนี้ส่วนขยายของ GNUไปa, iและcคำสั่ง:

ในฐานะที่เป็นส่วนขยาย GNU ถ้าระหว่างaและขึ้นบรรทัดใหม่มีอื่น ๆ นอกเหนือจากช่องว่าง\ตามลำดับแล้วข้อความของบรรทัดนี้เริ่มต้นที่ตัวละครที่ไม่ใช่ช่องว่างแรกหลังจากที่aจะถูกนำมาเป็นบรรทัดแรกของบล็อกข้อความ (สิ่งนี้ทำให้การทำให้เข้าใจง่ายขึ้นในการเพิ่มสคริปต์แบบบรรทัดเดียว) ส่วนขยายนี้ยังใช้งานได้กับคำสั่งiและc

ดังนั้นเพื่อให้พกพาได้ด้วยsedไวยากรณ์ของคุณคุณจะต้องมีการขึ้นบรรทัดใหม่a\อย่างใด วิธีที่ง่ายที่สุดคือการแทรก newline ที่ยกมา:

$ sed -e 'a\
> text'

(ที่ไหน$และ>เป็นเปลือกแจ้ง) หากคุณพบว่ามีความเจ็บปวดbash[1] มี$' 'ไวยากรณ์การอ้างอิงสำหรับการแทรกการหลีกเลี่ยงสไตล์ C ดังนั้นให้ใช้

sed -e 'a\'$'\n''text'

[1] และ mksh (r39b +) และเชลล์ที่ไม่ใช่ bash bourne (เช่น / bin / sh ใน FreeBSD 9+)


คำตอบที่ให้ข้อมูลมาก ขอขอบคุณสำหรับคำแนะนำในการอ้างอิงไวยากรณ์ของ bash
cjackson

14

ตามที่อธิบายไว้ในคำตอบนี้จะเป็นประโยชน์เมื่อใช้คำสั่ง sed เช่นiและaใช้หลาย-e "..."clauses แต่ละประโยคเหล่านี้จะถูกเข้าใจให้คั่นด้วยการขึ้นบรรทัดใหม่ iและaคำสั่งที่ยากที่จะใช้ในแบบอินไลน์ sed สคริปต์อย่างอื่น (ที่พวกเขากำลังได้รับการออกแบบสำหรับการใช้งานในหลายไฟล์สคริปต์ sed เรียกใช้sed -f file ...) ดูเหมือนว่าคุณไม่สามารถใช้การขึ้นบรรทัดใหม่โดยนัยที่นำมาใช้ในตอนท้ายของส่วน-eคำสั่งเพื่อแยกa\และบรรทัดข้อความที่จะต่อท้าย แต่คุณสามารถใช้มันเพื่อยุติบรรทัดข้อความที่จะต่อท้าย

ในกรณีเฉพาะนี้สิ่งที่คุณพยายามทำจริง ๆ แล้วสามารถทำได้ด้วย-e ...ประโยคเดียว คุณเพียงแค่ต้องใช้aคำสั่งอย่างถูกต้อง โดยมาตรฐาน POSIX aจะต้องตามด้วย a \จากนั้นจะต้องตามด้วยบรรทัดใหม่จากนั้นส่วนที่เหลือของบรรทัดถัดไปจะถูกแทรก (จนกว่าจะมีการขึ้นบรรทัดใหม่หรือสิ้นสุด-eประโยค) ดังนั้นคุณสามารถทำได้:

sed -i "" -e $'4 a\\\n'"mode '0755'" file.txt

2
หากคุณต้องการที่จะสังคายนาว่าคุณสมบัติที่คุณได้รับการอาศัยอยู่ Gnu-ISMS หน้านี้อาจช่วยwiki.alpinelinux.org/wiki/Regex มัน จำกัด เพียงคุณสมบัติ regex แต่; ไม่ใช่คำสั่ง sed อื่น ๆ
dubiousjim

5

ดูเหมือนว่า GNU sed จะใช้บ่อยกว่า POSIX sed ตามตัวอย่าง / แบบฝึกหัดที่ฉันใช้อยู่แล้ว

ฉันพบว่ามันง่ายกว่าเพียงแค่ติดตั้ง GNU บนเครื่อง OSX ใด ๆ ที่ฉันใช้ หากคุณสนใจวิธีที่ดีที่สุดที่จะทำนี้คือการติดตั้งมันผ่านHomebrew

คุณสามารถเลือก$ brew install gnu-sedหรือรับยูทิลิตี้ GNU ทั่วไปทั้งหมดด้วย$ brew install coreutilsก็ได้

จากนั้นเมื่อคุณพบปัญหาเกี่ยวกับไวยากรณ์ด้วยdateหรือโปรแกรมอื่นคุณสามารถใช้รุ่น GNU ได้ ในที่สุดผมก็ตัดสินใจว่ามันเป็นเรื่องง่ายที่จะมักจะใช้รุ่น GNU PATHและทำให้พวกเขาก่อนหน้านี้กว่ารุ่นในระบบของฉัน


5

Perl ไม่ต้องทนทุกข์ทรมานจากแพลตฟอร์มดังกล่าวขึ้นอยู่กับ GNU กับที่ไม่ใช่ GNU เทียบกับนิสัย "กรรมสิทธิ์" คุณสามารถทำได้:

perl -ni.old -e 'print;if ($.==4) {print "mode 0755\n"}' file

-n' option creates a loop that reads every line of the input file. Unlike its cousin-p (not used here) it doesn't automatically print every line read. The-i invokes in-place replacement. The argument to-i (viz. ".old") can be dropped or changed. It leaves a backup of the unmodified file. The -eสัญญาณเริ่มต้นของสคริปต์

$.หมายถึงจำนวนเส้นเริ่มต้นด้วยสาย 1 ดังนั้นบรรทัดคำสั่งจะอ่าน 'ไฟล์' และเมื่อหมายเลขบรรทัดเท่ากับ 4 มันจะพิมพ์บรรทัดนั้นตามด้วยสิ่งที่คุณต้องการแทรก

ฉันจะรีบเพิ่มว่า Perl เป็นหนี้รากของมันเพื่อsedAWK และ C ดังนั้นไวยากรณ์สำหรับการทดแทนง่าย ฯลฯ ไม่ได้เป็นเส้นโค้งการเรียนรู้ที่สูงชันมาก


คำตอบที่ดี! แต่คุณสามารถทำให้มันง่ายขึ้นเล็กน้อย perl -wlpi.old -e 'print "mode 0755" if $. == 5' file.txtลองนี้: -pสวิตช์ทำงานได้ดีนี่ (เนื่องจากเราต้องการพิมพ์ทุกบรรทัด); เราเพียงแค่ต้องเปลี่ยนตรรกะจาก "การพิมพ์หลังจากบรรทัดที่ 4" เป็น "การพิมพ์ก่อนบรรทัดที่ 5" และ-lสวิทช์ทำให้มันเป็น "\ n" ถูกพิมพ์โดยอัตโนมัติทุก ๆprintดังนั้นคุณไม่ต้องพิมพ์ด้วยตัวเอง
JL
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.