จุดประสงค์ของการใช้เครื่องหมายอัศเจรีย์หลายอันใน sed คืออะไร?


12

POSIX เอกสาร sedกล่าวว่า:

ฟังก์ชั่นสามารถนำหน้าด้วย '!' หนึ่งรายการหรือมากกว่า อักขระซึ่งในกรณีนี้จะใช้ฟังก์ชันถ้าที่อยู่ไม่ได้เลือกพื้นที่รูปแบบ ต้องยอมรับอักขระ <blank> ศูนย์หรือมากกว่าก่อนอักษรตัวแรก '!' ตัวละคร ไม่มีการระบุว่าอักขระ <blank> สามารถตาม '!' ได้หรือไม่ อักขระและแอปพลิเคชันที่สอดคล้องจะต้องไม่เป็นไปตาม '!' อักขระที่มีอักขระ <blank>

ดังนั้นด้วย POSIX sed เราสามารถ:

sed -e '/pattern/!d' file

มันเหมือนกับการเขียน:

sed -e '/pattern/!!d' file

และ!!!dและnของเครื่องหมายอัศเจรีย์จะยังคงได้รับการปรับ (ทดสอบกับสามsedรุ่นจากมรดกตกทอด toolchest ) ฉันไม่เห็นประโยชน์ใด ๆ ระหว่างหลายรายการแทนที่จะแสดงเป็นอัศเจรีย์

ทำไมสเป็คอนุญาตให้ไวยากรณ์นั้นและมันมีประโยชน์ในการใช้งานจริง?


ดูเหมือนว่า GNU sed ไม่เข้ากันกับกรณีนี้มันจะบ่นถ้าเราใช้อุทานหลายอย่าง:

$ sed -e '/pattern/!!d' file
sed: -e expression #1, char 11: multiple `!'s

2
FWIW: บน OpenBSD !ทำหน้าที่เป็นตัวสลับ, /pattern/!!เป็นเช่นเดียวกับ/pattern/และเป็นเช่นเดียวกับ/pattern/!!! /pattern/!ใน FreeBSD หลาย ๆ ตัว!จะเหมือนกับตัวเดียว
lcd047

2
จุดสำคัญของข้อมูลในสเปคคือsedสคริปต์สามารถสร้างขึ้นได้ กำหนด POSIX sedก็ควรจะเป็นเรื่องง่ายที่จะ realtively สคริปต์เขียนเป็นsedสคริปต์ ดังนั้นหากคุณมีทริกเกอร์ในบางกรณีซึ่งควรทำเครื่องหมายที่อยู่ที่!ไม่เหมาะสมกับสิ่งที่คุณทำคุณอาจเรียกใช้หลายครั้งในกรณีเดียวกันและยังคงออกมาด้วยผลลัพธ์เดียวกัน
mikeserv

@cuonglm ไม่เพียง FreeBSD เท่านั้นคือ GNU, OpenBSD และ NetBSD sedไม่ใช่
lcd047

@ lcd047: ใช่แน่นอน ขอโทษด้วยสำหรับภาษาอังกฤษที่ไม่ดีของฉัน ฉันหมายความว่ามันไม่เข้ากันใช่ไหม เป็นเรื่องดีที่รู้ว่า แต่จุดหลักในคำถามของฉันคือวิธีไวยากรณ์ที่สามารถเป็นประโยชน์ในโลกจริงกับ POSIX sed?
cuonglm

1
FWIW: การแก้ไขนี้ได้รับการยอมรับใน OpenBSD- ปัจจุบัน
lcd047

คำตอบ:


5

sedAPI ของดั้งเดิมคือ - และนี่คือโดยการออกแบบ อย่างน้อยที่สุดมันก็ยังคงเป็นแบบดั้งเดิมโดยการออกแบบ - ไม่ว่ามันจะถูกออกแบบมาตั้งแต่แรกเริ่มฉันไม่สามารถพูดได้ ในกรณีส่วนใหญ่การเขียนsedสคริปต์ซึ่งเมื่อเรียกใช้จะส่งออกสคริปต์อื่นsedเป็นเรื่องง่ายแน่นอน sedมากมักจะนำไปใช้ในลักษณะนี้โดย preprocessors แมโครเช่นและm4 / หรือmake

(สิ่งต่อไปนี้เป็นกรณีการใช้งานสมมุติอย่างสูง: มันเป็นปัญหาที่ได้รับการออกแบบมาเพื่อให้เหมาะกับการแก้ปัญหาหากรู้สึกว่ายืดออกไปกับคุณนั่นอาจเป็นเพราะมันเป็นเช่นนั้น


พิจารณาไฟล์อินพุตต่อไปนี้:

cat <<"" >./infile
camel
cat dog camel
dog cat
switch
upper
lower

ถ้าเราอยากจะเขียนsedสคริปต์ซึ่งจะผนวกคำ-caseถึงหางของแต่ละเหมาะสมคำในแฟ้มใส่ข้างต้น แต่ถ้ามันอาจจะพบได้ในบรรทัดในบริบทที่เหมาะสมและเราต้องการที่จะทำเช่นนั้นได้อย่างมีประสิทธิภาพที่สุด( อย่างที่ควรจะเป็นเป้าหมายของเราเช่นในระหว่างการดำเนินการคอมไพล์)จากนั้นเราควรหลีกเลี่ยงการใช้/regexp /มากที่สุด

สิ่งหนึ่งที่เราอาจทำคือแก้ไขไฟล์ล่วงหน้าในระบบของเราทันทีและไม่ต้องโทรsedเลยระหว่างการรวบรวม แต่ถ้าคำใดคำหนึ่งในไฟล์ควรหรือไม่ควรรวมอยู่บนพื้นฐานของการตั้งค่าท้องถิ่นและ / หรือตัวเลือกเวลาคอมไพล์ดังนั้นการทำเช่นนั้นน่าจะไม่ใช่ทางเลือกที่ต้องการ

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

ตัวอย่างเช่น:

n=$(printf '\\\n\t')
grep -En 'camel|upper|lower' <infile |
sed "   1i${n%?}#!/usr/heirloom/bin/posix2001/sed -nf
        s/[^:]*/:&$n&!n;&!b&$n&/;s/://2;\$a${n%?}q"'
        s/ *cat/!/g;s/ *dog/!/g
        s| *\([cul][^ ]*\).*|s/.*/\1-case/p|'

... ซึ่งเขียนเอาต์พุตในรูปแบบของsedสคริปต์และดูเหมือน ...

#!/usr/heirloom/bin/posix2001/sed -nf
:1
    1!n;1!b1
    1s/.*/camel-case/p
:2
    2!n;2!b2
    2!!s/.*/camel-case/p
:5
    5!n;5!b5
    5s/.*/upper-case/p
:6
    6!n;6!b6
    6s/.*/lower-case/p
q

เมื่อเอาต์พุตนั้นถูกบันทึกลงในไฟล์ข้อความที่รันได้บนเครื่องของฉันที่ชื่อ./bang.sedและรันเหมือน./bang.sed ./infileเอาต์พุตคือ:

camel-case
upper-case
lower-case

ตอนนี้คุณอาจถามฉัน ... ทำไมฉันต้องทำอย่างนั้น? ทำไมฉันถึงไม่ยึดเหนี่ยวgrepแมตช์? ใครใช้อูฐเคสด้วยล่ะ และสำหรับคำถามแต่ละข้อที่ฉันสามารถตอบได้เท่านั้นฉันไม่มีความคิด ...เพราะฉันไม่มี ก่อนที่จะอ่านคำถามนี้ฉันไม่เคยสังเกตเห็นmulti-! การแยกความต้องการในสเป็ค - ฉันคิดว่ามันเป็นระเบียบสวย

หลาย! สิ่งที่ไม่ได้ทันทีทำให้รู้สึกถึงฉันแม้ว่า - มากของsedสเปคจะมุ่งเน้นเพียงแค่แยกกันและเพียงแค่สร้าง sedสคริปต์ คุณอาจพบว่า\nตัวคั่น ewline ที่จำเป็นสำหรับการ[wr:bt{]ทำความเข้าใจให้มากขึ้นในบริบทนั้นและหากคุณคำนึงถึงความคิดนั้นคุณอาจเข้าใจถึงแง่มุมอื่น ๆ ของข้อมูลจำเพาะได้ดีขึ้น(เช่น:ไม่ยอมรับที่อยู่และqปฏิเสธที่จะ ยอมรับใด ๆ เพิ่มเติมกว่า 1)

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

ฉันคิดว่าหลาย! ที่อยู่อาจมีประโยชน์มากกว่าในบริบทนั้นมากกว่าในที่อื่นบางแห่ง แต่โดยความจริงแล้วฉันไม่สามารถนึกถึงกรณีใดกรณีหนึ่งที่ฉันใช้เพื่อการใช้งานที่ดีsedมาก ผมยังคิดว่ามันน่าสังเกตว่า GNU / BSD seds ทั้งล้มเหลวในการจัดการกับมันตามที่ระบุไว้ - นี้อาจจะไม่เป็นลักษณะของสเปคที่อยู่ในความต้องการมากและดังนั้นหากการดำเนินการมองเห็นมันฉันสงสัยอย่างจริงจังมากข้อบกพร่อง @กล่องจะประสบ ผลก็คือชะมัด

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


1
ตอนนี้ได้รับการแก้ไขใน OpenBSD- ปัจจุบัน
lcd047

1
หลาย!กำลังจะถูกถอดออกในสเปคต่อไปสิ่งที่เกิดขึ้นที่นี่!
cuonglm

@conglm - สายเกินไปฉันเดา บางทีฉันอาจใกล้เครื่องหมายมากกว่าที่ฉันคิด
mikeserv

@cuonglm - ก็โอเค แต่นั่นอะไร ... ได้รับการยอมรับว่าทำเครื่องหมายว่าหมายถึงอะไร
mikeserv

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