ใช้คำสั่ง ex เพื่อตรวจสอบว่าสองบรรทัดเหมือนกันหรือไม่


9

ผมกำลังมองไปที่คำถามนี้แล้วสงสัยว่าผมอาจจะใช้คำตอบของฉันซึ่งใช้ sedใช้อย่างหมดจด POSIX ex

เคล็ดลับคือในขณะที่sedฉันสามารถเปรียบเทียบพื้นที่พักกับพื้นที่รูปแบบเพื่อดูว่าพวกเขาจะเทียบเท่า (กับ G;/^\(.*\)\n\1$/{do something}) ฉันรู้วิธีที่จะทำแบบทดสอบexไม่

ฉันรู้ว่าในกลุ่มที่ฉันสามารถYank บรรทัดแรกแล้วพิมพ์ :2,$g/<C-r>0/dไปเกือบจะทำในสิ่งที่ผมระบุ แต่ถ้าบรรทัดแรกมีอะไร แต่ข้อความตัวเลขและตรงไปตรงมามากนี้จะกลายเป็นไม่แน่นอนแน่นอนเนื่องจากสายจะถูกทิ้งในเป็น regexไม่ใช่แค่สตริงสำหรับการเปรียบเทียบ (และหากบรรทัดแรกมีเครื่องหมายทับหน้าส่วนที่เหลือของบรรทัดจะถูกตีความเป็นคำสั่ง!)

ดังนั้นหากฉันต้องการลบทุกบรรทัดในmyfileที่เหมือนกันกับบรรทัดแรก - แต่ไม่ลบบรรทัดแรก - ฉันจะทำเช่นนั้นได้exอย่างไรโดยใช้? สำหรับเรื่องนั้นฉันจะใช้มันได้viอย่างไร

มีวิธี POSIX ในการลบบรรทัดหรือไม่หากตรงกับอีกบรรทัดหนึ่ง?

บางทีบางอย่างเช่นไวยากรณ์ในจินตนาการ:

:2,$g/**lines equal to "0**/d

3
คุณสามารถสร้างคำสั่ง แต่มันจะต้องมีนิด ๆ หน่อย ๆ ของ vimscript และมันอาจจะไม่เป็นวิธีที่ POSIX::execute '2,$g/\V' . escape(getline(1), '\') . '/d'
อว์

1
@ saginaw ขอบคุณ จนถึงวิธี POSIX เดียวที่เกิดขึ้นกับฉันคือการใช้sedเป็นตัวกรองจากภายในexและเรียกใช้sedคำตอบทั้งหมดของฉันในบัฟเฟอร์ทั้งหมด ... ซึ่งจะทำงานแน่นอน (และแตกต่างจากแบบพกพาจริง ๆsed -i)
สัญลักษณ์แทน

คุณพูดถูกและฉันพบว่าการเข้าใกล้ครั้งแรกของคุณ<C-r>0ดีมาก ฉันไม่แน่ใจว่าคุณทำได้ดีกว่าด้วยคำสั่ง Ex เพียงอย่างเดียวเพราะคุณต้องปกป้องตัวละครพิเศษ หากไม่มีข้อ จำกัด ที่เป็นไปตาม POSIX ฉันคิดว่าคุณจะใช้สวิตช์ที่น่าสมเพชมาก\Vและจากนั้นคุณจะป้องกันแบ็กสแลช (เพราะมันยังคงความหมายพิเศษไว้ด้วย\V) กับescape()ฟังก์ชั่นที่อาร์กิวเมนต์ 2 คือสตริงที่มีอักขระทั้งหมดที่คุณต้องการหลบหนี .
saginaw

อย่างไรก็ตามในคำสั่งก่อนหน้านี้ฉันลืมที่จะปกป้องเครื่องหมายสแลชด้วยเช่นกันเพราะมันมีความหมายพิเศษสำหรับคำสั่งทั่วโลกมันเป็นตัวคั่นรูปแบบ ดังนั้นคำสั่งที่ถูกต้องอาจเป็นดังนี้: :execute '2,$g/\V' . escape(getline(1), '\/') . '/d'หรือคุณสามารถใช้อักขระอื่นสำหรับตัวคั่นรูปแบบเหมือนเครื่องหมายอัฒภาค ในกรณีนี้คุณไม่จำเป็นต้องปกป้องเครื่องหมายสแลชในรูปแบบ มันจะให้อะไรเช่น::execute '2,$g;\V' . escape(getline(1), '\') . ';d'
saginaw

1
ฉันค้นหาแนวทางที่สองของคุณด้วยsedดีมาก ด้วย Vim คุณมักจะมอบหมายงานพิเศษบางอย่างให้กับโปรแกรมอื่น ๆ และsedอาจเป็นตัวอย่างที่ดีของสิ่งนั้น อย่างไรก็ตามคุณไม่จำเป็นต้องใช้sedบัฟเฟอร์ทั้งหมด หากคุณต้องการเรียกใช้เฉพาะในส่วนของบัฟเฟอร์คุณสามารถกำหนดช่วงได้ ตัวอย่างเช่นถ้าคุณต้องการที่จะกรองเพียงเส้นแบ่งระหว่าง 50 และ 100 :50,100!<your sed command>คุณสามารถพิมพ์:
saginaw

คำตอบ:


3

เป็นกลุ่ม

ในกลุ่มคุณสามารถจับคู่ตัวอักษรใด ๆ \_.รวมถึงการขึ้นบรรทัดใหม่ด้วย คุณสามารถใช้สิ่งนี้เพื่อสร้างรูปแบบที่ตรงกับทั้งบรรทัดจำนวนสิ่งของและจากนั้นบรรทัดเดียวกันนั้น:

/\(^.*$\)\_.*\n\1$/

ตอนนี้คุณต้องการลบบรรทัดทั้งหมดในไฟล์ที่ตรงกับบรรทัดแรกไม่รวมบรรทัดแรก การทดแทนเพื่อลบบรรทัดสุดท้ายที่ตรงกับบรรทัดแรกคือ:

:1 s/\(^.*$\)\_.*\zs\n\1$//

คุณสามารถใช้:globalเพื่อให้แน่ใจว่าการแทนที่ซ้ำหลายครั้งพอที่จะลบบรรทัดทั้งหมด:

:g/^/ 1s/\(^.*$\)\_.*\zs\n\1$//

ตัวอย่าง POSIX

@saginaw แสดงให้เห็นถึงวิธีในการทำสิ่งนี้ใน Vim ในความคิดเห็นสำหรับคำถามของคุณ แต่เราสามารถปรับใช้เทคนิคด้านบนสำหรับ POSIX ex

เมื่อต้องการทำเช่นนี้ในวิธีที่เข้ากันได้กับ POSIX คุณต้องไม่อนุญาตให้ใช้การจับคู่หลายบรรทัด แต่คุณยังคงสามารถใช้ประโยชน์จากการอ้างอิงย้อนกลับได้ ต้องมีงานพิเศษ:

:g/^/ t- | s/^/@@@/ | 1t- | s/^/"/ | j! | s/^"\(.*\)@@@\1$/d/ | d x | @x

นี่คือรายละเอียด:

:g/^/                   for each line

t- |                    copy it above

s/^/@@@/ |              prefix it with something unique (@@@)
                        (do a search in the buffer first to make
                        sure it really is unique)

1t- |                   copy the first line above this one

s/^/"/ |                prefix with "

j! |                    join those two lines (no spaces)

s/^"\(.*\)@@@\1$/d/ |   if the part after the " and before the @@@
                        matches the part after the @@@, replace the line
                        with d

d x |                   delete the line into register x

@x                      execute it

ดังนั้นถ้าบรรทัดปัจจุบันเป็นซ้ำของเส้นที่ 1 ลงทะเบียน x dจะมี การดำเนินการจะลบบรรทัดปัจจุบัน ถ้ามันไม่ซ้ำกันมันจะมีคำนำหน้าไร้สาระ"ซึ่งเมื่อดำเนินการเป็นแบบไม่มี op, ตั้งแต่" เริ่มแสดงความคิดเห็น ฉันไม่รู้ว่านี่เป็นวิธีที่เรียบร้อยที่สุดในการทำสิ่งนี้หรือไม่เป็นเพียงสิ่งแรกที่นึกถึง!

มันเกิดขึ้นจนไม่สามารถลบบรรทัดแรกเนื่องจากกระบวนการคัดลอกเปลี่ยนบรรทัดที่ 1 ชั่วคราว หากนี่ไม่ใช่กรณีที่คุณสามารถนำหน้า:gด้วย2,$ช่วงแทน

ทดสอบในกลุ่ม Vim และรุ่น ex-vi 4.0

แก้ไข

และวิธีที่ง่ายกว่าซึ่งใช้อักขระพิเศษเพื่อสร้างรูปแบบการค้นหา (พร้อม'nomagic'ชุด) สร้าง:globalคำสั่งจากนั้นเรียกใช้งาน:

:set nomagic
:1t1 | .g/^/ s#\[$^\/]#\\\&#g | s#\.\*#2,$g/^\&$/d# | d x
:@x
:set magic

คุณไม่สามารถทำสิ่งนี้เป็นหนึ่งซับเพราะคุณมีซ้อน :globalซึ่งไม่ได้รับอนุญาต


2

ก็ปรากฏว่าเพียงวิธี POSIX sedการทำเช่นนี้คือการใช้ตัวกรองภายนอกเช่น

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

:1,17!sed '5h;17{G;/^\(.*\)\n\1$/d;s/\n.*$//;}'

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

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


การพัฒนา - คุณสามารถทำได้เช่น:

:17t 5
:5,5+!sed '1N;/^\(.*\)\n\1$/d;s/\n.*$//'

นี่เป็นวิธีการทั่วไปที่มากกว่า มันสามารถถูกใช้เพื่อให้ผลลัพธ์เดียวกันกับคำสั่งแรก (และลบบรรทัดที่ 17 เฉพาะเมื่อมันเหมือนกับบรรทัดที่ 5) ดังนี้:

:5t 17
:17,17+!sed '1N;/^\(.*\)\n\1$/d;s/\n.*$//'

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

:37,$!sed '1{h;n;};G;/^\(.*\)\n\1$/d;s/\n.*$//'
:37t 0
:1,37!sed '1{h;d;};G;/^\(.*\)\n\1$/d;s/\n.*$//'

สรุปนี่คือสำหรับการตรวจสอบว่าทั้งสองสายเป็นเหมือนเครื่องมือที่ดีที่สุดคือ ไม่sed exแต่ดังที่DevSolar พูดถึงในความคิดเห็นนี่ไม่ใช่ความล้มเหลวviหรือex- พวกเขาถูกออกแบบมาเพื่อทำงานกับเครื่องมือ Unix; นั่นคือจุดแข็งที่สำคัญ


สิ่งที่ยากกว่านั้นคือการแทรกบรรทัดที่ท้ายไฟล์เฉพาะเมื่อบรรทัดนั้นไม่มีอยู่ที่ใดที่หนึ่งในไฟล์
สัญลักษณ์แทน

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