पूर्व-आदेश का उपयोग यह जांचने के लिए करें कि क्या दो लाइनें समान हैं?


9

मैं इस प्रश्न को देख रहा था और फिर सोच रहा था कि मैं अपने उत्तरsed को कैसे लागू कर सकता हूं जो विशुद्ध रूप से POSIX exका उपयोग करता है

चाल यह है कि जब तक sedमैं पकड़ स्थान की तुलना पैटर्न स्पेस के साथ कर सकता हूं यह देखने के लिए कि क्या वे बिल्कुल बराबर (साथ G;/^\(.*\)\n\1$/{do something}) हैं, मुझे इस तरह के परीक्षण करने का कोई तरीका नहीं पता है ex

मैं जानता हूँ कि विम में मैं कर सकता है कि Yपहली पंक्ति ANK और फिर टाइप :2,$g/<C-r>0/dकरने के लिए लगभग मैं क्या निर्दिष्ट करने-लेकिन कर रहा हूँ कर पहली पंक्ति कुछ भी होता है, लेकिन बहुत स्पष्ट अल्फ़ान्यूमेरिक पाठ इस के बाद से लाइन एक के रूप में में फेंक दिया जा रहा है वास्तव में सुखद हो जाता है, अगर regex , तुलना के लिए सिर्फ एक तार नहीं। (और यदि पहली पंक्ति में एक आगे की स्लैश है, तो शेष पंक्ति को एक कमांड के रूप में समझा जाएगा!)

इसलिए यदि मैं myfileउस सभी लाइनों को हटाना चाहता हूं जो पहली पंक्ति के समान हैं - लेकिन पहली पंक्ति को हटाना नहीं है - तो मैं उस उपयोग को कैसे कर सकता हूं ex? उस मामले के लिए, मैं इसका उपयोग कैसे कर सकता हूं vi?

क्या किसी पंक्ति को हटाने के लिए POSIX तरीका है यदि यह बिल्कुल दूसरी पंक्ति से मेल खाता है?

शायद इस काल्पनिक वाक्य रचना की तरह कुछ:

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

3
आप कमांड का निर्माण कर सकते हैं, लेकिन इसके लिए विम्सस्क्रिप्ट की थोड़ी आवश्यकता होगी और यह संभवतः POSIX तरीका नहीं होगा::execute '2,$g/\V' . escape(getline(1), '\') . '/d'
saginaw

1
@ सागिनॉ, धन्यवाद। अब तक मेरे पास जो एकमात्र POSIX दृष्टिकोण आया है, वह सिर्फ sedएक फिल्टर के रूप में उपयोग करना है ex, और sedपूरे बफर पर अपना पूरा उत्तर चलाना है ... जो काम करेगा , निश्चित रूप से (और वास्तव में इसके विपरीत पोर्टेबल है sed -i)।
वाइल्डकार्ड

आप सही हैं और मुझे आपका प्रारंभिक दृष्टिकोण <C-r>0बहुत अच्छा लगा। मुझे यकीन नहीं है कि आप केवल एक्स कमांड के साथ बेहतर कर सकते हैं क्योंकि आपको विशेष पात्रों की रक्षा करनी है। POSIX आज्ञाकारी बाधा के बिना मुझे लगता है कि आप बहुत ही नाममात्र स्विच का उपयोग करेंगे \Vऔर फिर आप बैकस्लैश की रक्षा करेंगे (क्योंकि यह अपने विशेष अर्थ को भी साथ रखता है \V) escape()फ़ंक्शन के साथ जिसका 2 तर्क एक स्ट्रिंग है जिसमें आप सभी पात्रों से बचना चाहते हैं / रक्षा करना चाहते हैं ।
सागिनॉ

हालाँकि, पिछली कमांड में मैं फॉरवर्ड स्लैश की सुरक्षा करना भी भूल गया था, क्योंकि इसका ग्लोबल कमांड के लिए एक विशेष अर्थ भी है, यह पैटर्न सीमांकक है। तो सही कमांड शायद कुछ इस तरह होगी: :execute '2,$g/\V' . escape(getline(1), '\/') . '/d'या आप एक अर्धविराम की तरह पैटर्न परिसीमन के लिए किसी अन्य चरित्र का उपयोग कर सकते हैं। इस मामले में आपको पैटर्न में फ़ॉरवर्ड स्लैश की रक्षा करने की आवश्यकता नहीं होगी। यह कुछ इस तरह देगा::execute '2,$g;\V' . escape(getline(1), '\') . ';d'
saginaw

1
मुझे आपका दूसरा दृष्टिकोण sedभी बहुत अच्छा लगा। विम के साथ, आप अक्सर अन्य कार्यक्रमों के लिए कुछ विशेष कार्य सौंपते हैं, और sedशायद इसका एक अच्छा उदाहरण है। वैसे, आपको sedअपने पूरे बफ़र पर नहीं चलना है । यदि आप इसे केवल बफर के एक हिस्से पर चलाना चाहते हैं, तो आप एक सीमा दे सकते हैं। उदाहरण के लिए, यदि आप केवल 50 और 100 के बीच की लाइनों को फ़िल्टर करना चाहते हैं, तो आप टाइप कर सकते हैं :50,100!<your sed command>:।
सागिनॉ

जवाबों:


3

शक्ति

विम में आप newline सहित किसी भी चरित्र के साथ मेल कर सकते हैं \_.। आप इसका उपयोग एक पैटर्न बनाने के लिए कर सकते हैं जो एक पूरी लाइन, किसी भी सामान की मात्रा से मेल खाता है, और फिर उसी लाइन:

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

अब आप एक फ़ाइल में सभी पंक्तियों को हटाना चाहते हैं जो पहले से मेल खाती है, पहले सहित नहीं। पहली से मेल खाने वाली अंतिम पंक्ति को हटाने का विकल्प है:

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

आप :globalयह सुनिश्चित करने के लिए उपयोग कर सकते हैं कि सभी लाइनों को हटाने के लिए प्रतिस्थापन को पर्याप्त बार दोहराया जाता है:

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

POSIX पूर्व

@saginaw आपके प्रश्न के लिए एक टिप्पणी में विम में ऐसा करने के लिए एक शून्य रास्ता दिखाता है, लेकिन हम POSIX पूर्व के लिए उपरोक्त तकनीक को अनुकूलित कर सकते हैं।

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। इसे निष्पादित करने से वर्तमान लाइन हट जाएगी। यदि यह डुप्लिकेट नहीं है, तो इसमें उपसर्ग होगा "जिसके साथ जब कोई " टिप्पणी की जाती है , तब निष्पादित किया जाता है, जो कि नो-ऑप है । मुझे नहीं पता कि क्या यह पूरा करने का सबसे साफ तरीका है, यह सिर्फ पहली बात है जो दिमाग में आई!

यह सिर्फ इतना होता है कि पहली पंक्ति को हटाया नहीं जा सकता है क्योंकि प्रतिलिपि बनाने की प्रक्रिया अस्थायी रूप से बदल जाती है जो पंक्ति 1 है। अगर ऐसा नहीं होता तो आप इसके बजाय :gकिसी 2,$रेंज के साथ उपसर्ग कर सकते थे ।

विम और पूर्व- 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 लाइनों पर चला सकते हैं, लेकिन पहले मामले में आप अनावश्यक फ़िल्टरिंग कर रहे हैं - कोई बड़ी बात नहीं है - और बाद वाले मामले में आपको इसका उपयोग करना होगा sed5 और 17 के बजाय आपकी आज्ञा में संख्या 1 और 13। भ्रमित करना। "

चूंकि 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 एक टिप्पणी में alluded , इस की विफलता नहीं है viया ex, वे कर रहे हैं के लिए बनाया गया यूनिक्स उपकरणों के साथ काम करने के लिए; यह एक बड़ी ताकत है।


बहुत, बहुत कठिन है: किसी फ़ाइल के अंत में एक पंक्ति सम्मिलित करना, अगर लाइन पहले से ही फ़ाइल में कहीं मौजूद नहीं है ।
वाइल्डकार्ड

मेरे जवाब के समान दृष्टिकोण के साथ यह उल्लेखनीय होना चाहिए। मुझे नहीं लगता कि यह हालांकि वन-लाइनर होगा!
एंटनी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.