แทรกบรรทัดหลังการแข่งขันครั้งแรกโดยใช้ sed


245

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

CLIENTSCRIPT="foo"
CLIENTFILE="bar"

และฉันต้องการแทรกบรรทัดหลังCLIENTSCRIPT=บรรทัดส่งผลให้ ...

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

คำตอบ:


379

ลองทำสิ่งนี้โดยใช้ GNU sed:

sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

หากคุณต้องการแทนที่ในสถานที่ให้ใช้

sed -i '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

เอาท์พุต

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

คุณหมอ


ขอบคุณ! และเพื่อให้เขียนกลับไปที่ไฟล์โดยไม่พิมพ์เนื้อหาของไฟล์?
user2150250

11
โปรดทราบว่าทั้งสองถือว่า sedGNU นั่นไม่ใช่sedไวยากรณ์มาตรฐานและจะไม่ทำงานกับsedการใช้งานอื่น ๆ
Stephane Chazelas

ฉันมีปัญหาในการทำให้สิ่งนี้ใช้งานได้สำหรับฉันเพราะฉันใช้ตัวคั่นอื่น หลังจาก RTFM'ing ฉันรู้ว่าฉันต้องเริ่มต้น regex ด้วย \ จากนั้นตัวคั่น
Christy

10
นำไปใช้กับการแข่งขันนัดแรกได้อย่างไร เป็นที่ชัดเจนว่าจะต่อท้ายข้อความหลังการแข่งขัน แต่จะรู้ได้อย่างไรเฉพาะคู่แรก
Mohammed Noureldin

7
สิ่งนี้จะเพิ่มข้อความหลังการแข่งขันทุกครั้งสำหรับฉัน
schoppenhauer

49

บันทึกsedไวยากรณ์มาตรฐาน(เช่นเดียวกับ POSIX ดังนั้นสนับสนุนโดยsedการนำไปปฏิบัติที่สอดคล้องกันทั้งหมด(GNU, OS / X, BSD, Solaris ... ):

sed '/CLIENTSCRIPT=/a\
CLIENTSCRIPT2="hello"' file

หรือหนึ่งบรรทัด:

sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file

(-e xpressions (และเนื้อหาของ-files) เข้าร่วมกับการขึ้นบรรทัดใหม่เพื่อประกอบการsedตีความสคริปต์ sed )

-iตัวเลือกสำหรับการแก้ไขในสถานที่ยังเป็นส่วนขยายของ GNU บางการใช้งานอื่น ๆ (เช่นของ FreeBSD) การสนับสนุน-i ''สำหรับการที่

หรือเพื่อความสะดวกในการพกพาคุณสามารถใช้ perlแทน:

perl -pi -e '$_ .= qq(CLIENTSCRIPT2="hello"\n) if /CLIENTSCRIPT=/' file

หรือคุณสามารถใช้edหรือex:

printf '%s\n' /CLIENTSCRIPT=/a 'CLIENTSCRIPT2="hello"' . w q | ex -s file

2
ฉันอาจจะผิด แต่ปัจจุบันsed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' fileหนีคำพูดที่ท้ายพารามิเตอร์แรกและแบ่งคำสั่ง
รถเข็นที่ถูกทิ้งร้าง

1
@AbandonedCart ในเชลล์ของ Bourne cshหรือrcตระกูล'...'เป็นเครื่องหมายคำพูดที่แข็งแกร่งซึ่งแบ็กสแลชไม่ได้เป็นพิเศษ ข้อยกเว้นเดียวที่ฉันรู้คือfishเปลือก
Stephane Chazelas

1
เทอร์มินัลบน MacOS (และต่อมาสคริปต์รันในเทอร์มินัล) ก็เป็นข้อยกเว้นเช่นกัน ฉันพบไวยากรณ์ทางเลือก แต่ก็ขอขอบคุณ
รถเข็นที่ถูกทิ้งร้าง

1
@AbandonedCart นั่นเป็นอย่างอื่น นั่นคือ macOS ที่sedไม่เป็นไปตาม POSIX ที่นี่ นั่นทำให้คำสั่งของฉันเกี่ยวกับการพกพาที่ไม่ถูกต้อง (สำหรับตัวแปรหนึ่งบรรทัด) ฉันจะขอ opengroup เพื่อยืนยันว่าเป็นความไม่สอดคล้องหรือการตีความมาตรฐานที่ผิดในส่วนของฉัน
Stephane Chazelas

19

POSIX ที่เข้ากันได้กับsคำสั่ง:

sed '/CLIENTSCRIPT="foo"/s/.*/&\
CLIENTSCRIPT2="hello"/' file

1
... ที่ยังรองรับการแทรกบรรทัดใหม่ ฉันรักมัน!
Stephan Henningsen

1
ดูเพิ่มเติมsed -e '/.../s/$/\' -e 'CLI.../'ที่จะหลีกเลี่ยงปัญหาเกี่ยวกับบรรทัดที่มีลำดับไบต์ที่ไม่สร้างอักขระที่ถูกต้อง
Stephane Chazelas

14

คำสั่ง Sed ที่ทำงานบน MacOS (อย่างน้อย, OS 10) และ Unix เหมือนกัน (เช่น. ไม่ต้องการ gnu sed เช่น Gilles '(ปัจจุบันยอมรับ))

sed -e '/CLIENTSCRIPT="foo"/a\'$'\n''CLIENTSCRIPT2="hello"' file

สิ่งนี้ใช้ได้ใน bash และอาจเป็น shell อื่น ๆ ด้วยที่รู้สไตล์การเสนอราคา $ '\ n' ทุกอย่างสามารถอยู่ในหนึ่งบรรทัดและทำงานในคำสั่งเก่า / POSIX sed หากอาจมีหลายบรรทัดที่ตรงกับ CLIENTSCRIPT = "foo" (หรือเทียบเท่าของคุณ) และคุณต้องการเพิ่มบรรทัดพิเศษเฉพาะในครั้งแรกคุณสามารถทำใหม่ได้ดังต่อไปนี้:

sed -e '/^ *CLIENTSCRIPT="foo"/b ins' -e b -e ':ins' -e 'a\'$'\n''CLIENTSCRIPT2="hello"' -e ': done' -e 'n;b done' file

(สิ่งนี้จะสร้างลูปหลังจากรหัสการแทรกบรรทัดที่วนรอบส่วนที่เหลือของไฟล์ไม่ต้องกลับไปที่คำสั่ง sed แรกอีกครั้ง)

คุณอาจสังเกตเห็นว่าฉันเพิ่ม '^ *' ในรูปแบบการจับคู่ในกรณีที่บรรทัดปรากฏขึ้นในความคิดเห็นพูดหรือเยื้อง มันไม่ได้สมบูรณ์แบบ 100% แต่ครอบคลุมสถานการณ์อื่น ๆ ที่น่าจะเกิดขึ้น ปรับตามต้องการ ...

สองคนนี้ยังได้รับการแก้ปัญหารอบปัญหา (สำหรับการแก้ปัญหาทั่วไปเพื่อเพิ่มบรรทัด) ว่าถ้าสายแทรกใหม่ของคุณมีเครื่องหมายที่ไม่ใช้ Escape หรือเครื่องหมายที่พวกเขาจะได้รับการตีความโดย sed และมีแนวโน้มที่ไม่ออกมาเหมือนกันเช่นเดียวกับ\nเป็น - เช่น\0จะจับคู่บรรทัดแรก มีประโยชน์เป็นพิเศษหากคุณกำลังเพิ่มบรรทัดที่มาจากตัวแปรที่คุณต้องหลีกเลี่ยงทุกอย่างก่อนใช้ $ {var //} มาก่อนหรือใช้คำสั่ง sed อื่นเป็นต้น

วิธีแก้ปัญหานี้มีความยุ่งเล็กน้อยในสคริปต์ (นั่นคือการอ้างถึงและ \ n ไม่ใช่เรื่องง่ายที่จะอ่าน) เมื่อคุณไม่ต้องการใส่ข้อความแทนคำสั่งที่จุดเริ่มต้นของบรรทัดถ้าพูดในฟังก์ชั่น มีเส้นที่เยื้อง ฉันใช้ประโยชน์จากการที่ $ '\ n' ถูกประเมินเป็นบรรทัดใหม่โดยเชลล์ไม่ใช่ค่าที่อยู่ในเครื่องหมายอัญประกาศ '\ n' แบบปกติ

มันใช้เวลานานพอถึงแม้ว่าฉันคิดว่า perl / awk อาจชนะเพราะอ่านง่ายขึ้น


1
ขอบคุณสำหรับคำตอบ! อันแรกทำงานได้อย่างมีเสน่ห์บน AIX OS เช่นกัน
abhishek

คุณจะทำเช่นนี้ได้อย่างไรสำหรับการแทรกหลายบรรทัด
tatsu

1
POSIX sed รองรับการขึ้นบรรทัดใหม่ตามตัวอักษรในสตริงการแทนที่หากคุณ "หลบหนี" ด้วยแบ็กสแลช ( ซอร์ส ) ดังนั้น: [Enter]s/CLIENTSCRIPT="foo"/&\ CLIENTSCRIPT2="hello"/เครื่องหมายแบ็กสแลชต้องเป็นอักขระตัวสุดท้ายในบรรทัด (ไม่มีช่องว่างหลังจากนั้น) ตรงกันข้ามกับลักษณะที่ปรากฏในความคิดเห็นนี้
TheDudeAbides

1
@tatsu Newlines ในสตริงการแทนที่ของs///รวมถึงคำสั่งในaและiคำสั่งสามารถ "หนี" โดยใช้วิธีเดียวกับที่ฉันอธิบายในความคิดเห็นของฉันด้านบน
TheDudeAbides

4

อาจจะสายในการโพสต์คำตอบสำหรับเรื่องนี้ แต่ฉันพบว่าวิธีการแก้ปัญหาข้างต้นค่อนข้างยุ่งยาก

ฉันลองเปลี่ยนสตริงแบบง่าย ๆ ใน sed และใช้งานได้:

sed 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

& sign แสดงถึงสตริงที่ตรงกันจากนั้นคุณเพิ่ม \ n และบรรทัดใหม่

ดังที่กล่าวไว้ถ้าคุณต้องการทำในสถานที่:

sed -i 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

อีกสิ่งหนึ่งที่. คุณสามารถจับคู่โดยใช้นิพจน์:

sed -i 's/CLIENTSCRIPT=.*/&\nCLIENTSCRIPT2="hello"/' file

หวังว่านี่จะช่วยใครซักคน


ใช้งานได้ดีบน Linux โดย GNU sed เป็นค่าเริ่มต้นเพราะเข้าใจ\nในสตริงการแทนที่ วานิลลาธรรมดา (POSIX) sedไม่ไม่เข้าใจ\nในสตริงทดแทนอย่างไรเช่นนี้จะไม่ทำงานบน BSD หรือ MacOS เว้นแต่คุณจะติดตั้ง GNU sed อย่างใด ด้วย vanilla sed คุณสามารถฝังบรรทัดใหม่โดย "escaping" พวกเขานั่นคือสิ้นสุดบรรทัดด้วยแบ็กสแลชแล้วกด[Enter] ( อ้างอิงภายใต้หัวเรื่อง[2addr]s/BRE/replacement/flags) นี่หมายความว่าคำสั่ง sed ของคุณต้องขยายหลายบรรทัดในเทอร์มินัล
TheDudeAbides

ตัวเลือกที่จะได้รับการขึ้นบรรทัดใหม่ตัวอักษรในสตริงทดแทนก็คือการใช้ข้อความ ANSI-Cคุณลักษณะในทุบตีที่กล่าวไว้ในหนึ่งในคำตอบอื่น
TheDudeAbides


1

ฉันมีงานที่คล้ายกันและไม่สามารถรับโซลูชัน Perl ด้านบนให้ทำงานได้

นี่คือทางออกของฉัน:

perl -i -pe "BEGIN{undef $/;} s/^\[mysqld\]$/[mysqld]\n\ncollation-server = utf8_unicode_ci\n/sgm" /etc/mysql/my.cnf

คำอธิบาย:

ใช้นิพจน์ทั่วไปเพื่อค้นหาบรรทัดในไฟล์ /etc/mysql/my.cnf ของฉันที่มีเฉพาะ[mysqld]และแทนที่ด้วย

[mysqld] collation-server = utf8_unicode_ci

เพิ่มcollation-server = utf8_unicode_ciบรรทัดหลังบรรทัดที่มี[mysqld]อย่างมีประสิทธิภาพ


0

ฉันต้องทำเช่นนี้เมื่อเร็ว ๆ นี้เช่นกันสำหรับทั้ง Mac และ Linux OS และหลังจากเรียกดูผ่านหลาย ๆ โพสต์และลองหลายอย่างในความเห็นของฉันฉันไม่เคยไปที่ที่ฉันต้องการซึ่งก็คือ: ง่ายพอที่จะเข้าใจวิธีการแก้ปัญหา และคำสั่งมาตรฐานที่มีลวดลายที่เรียบง่ายซับในหนึ่งพกพาสามารถขยายได้เพื่อเพิ่มข้อ จำกัด มากขึ้น จากนั้นฉันพยายามมองด้วยมุมมองที่แตกต่างนั่นคือเมื่อฉันรู้ว่าฉันสามารถทำได้โดยไม่มีตัวเลือก "หนึ่งซับ" ถ้า "2 ซับ" ตรงตามเกณฑ์ที่เหลือของฉัน ในตอนท้ายฉันมาด้วยวิธีนี้ฉันชอบที่ทำงานได้ทั้งใน Ubuntu และ Mac ซึ่งฉันต้องการแบ่งปันกับทุกคน:

insertLine=$(( $(grep -n "foo" sample.txt | cut -f1 -d: | head -1) + 1 ))
sed -i -e "$insertLine"' i\'$'\n''bar'$'\n' sample.txt

ในคำสั่งแรก grep ค้นหาหมายเลขบรรทัดที่ประกอบด้วย "foo", cut / head เลือกการเกิดขึ้นครั้งที่ 1 และการเพิ่มค่าเลขคณิต op ที่เกิดขึ้นครั้งแรกที่หมายเลขบรรทัด 1 โดยฉันต้องการแทรกหลังจากการเกิดขึ้น ในคำสั่งที่สองเป็นการแก้ไขไฟล์แบบ "i" สำหรับการแทรก: ansi-c quoting บรรทัดใหม่ "bar" และบรรทัดใหม่อีกบรรทัด ผลลัพธ์คือการเพิ่มบรรทัดใหม่ที่มี "บาร์" หลังบรรทัด "foo" แต่ละคำสั่ง 2 คำสั่งเหล่านี้สามารถขยายไปยังการดำเนินการและการจับคู่ที่ซับซ้อนยิ่งขึ้น

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