सेड के साथ पैटर्न के ऊपर की लाइनों को हटाएं (या awk)


28

मेरे पास निम्नलिखित कोड है जो पैटर्न के साथ लाइनों को हटा देगा bananaऔर इसके बाद 2 लाइनें:

sed '/banana/I,+2 d' file

अब तक सब ठीक है! लेकिन मुझे इससे पहले 2 लाइनों को हटाने की आवश्यकता है banana, लेकिन मैं इसे "माइनस साइन" या जो कुछ भी कर सकता हूं (जो grep -v -B2 banana fileकरना चाहिए या नहीं के समान है ):

teresaejunior@localhost ~ > LC_ALL=C sed '-2,/banana/I d' file
sed: invalid option -- '2'
teresaejunior@localhost ~ > LC_ALL=C sed '/banana/I,-2 d' file
sed: -e expression #1, char 16: unexpected `,'
teresaejunior@localhost ~ > LC_ALL=C sed '/banana/I,2- d' file
sed: -e expression #1, char 17: unknown command: `-'

1
सबसे आसान है कि सभी डेटा को एक सरणी में लोड करना, अवांछित लाइनों को छोड़ना और फिर आउटपुट जो रहता है awk '{l[m=NR]=$0}/banana/{for(i=NR-2;i<=NR;i++)delete l[i]}END{for(i=1;i<=m;i++)if(i in l)print l[i]}':। यह कुशल नहीं है, इसलिए यह सिर्फ एक संकेत है, समाधान नहीं है।
15

6
बस करो tac file | sed ... | tac। : पी
कोण

@angus मैंने इसके बारे में नहीं सोचा;)
टेरेसा ई जूनियर

1
आप ऐसा कर सकते थे जो sed '/banana/,+2d' file काम भी करेगा
अक्सस

1
यदि आप awk का उपयोग करने के लिए खुले हैं, तो यह बहुत आसान है: awk 'tolower($0)~/bandana/{print prev[!idx];print prev[idx]} {idx=!idx;prev[idx]=$0}' filein चूंकि यह एक टिप्पणी है और एक उत्तर नहीं है (पहले से ही अन्य उत्तर हैं), मैं बहुत अधिक विस्तार में नहीं जाऊंगा, लेकिन इसके बारे में आपको हमेशा पता चलता है पिछले दो रिकॉर्ड्स प्रचलित [0] और प्रचलित [1], "सबसे ताज़े" जिस पर निर्भर करता है prev[idx], लेकिन हमेशा अंदर रहता है , इसलिए जब आप प्रिंट करते हैं, तो आप !idxउस idxक्रम में प्रिंट करते हैं । भले ही, वैकल्पिक idxऔर वर्तमान रिकॉर्ड में डाल दिया prev[idx]
Luv2code

जवाबों:


22

एसड बैकट्रैक नहीं करता है: एक बार एक लाइन संसाधित होने के बाद, यह हो गया है। इसलिए "एक लाइन ढूंढें और पिछली एन लाइनों को प्रिंट करें" के रूप में काम नहीं करने वाला है, इसके विपरीत "एक लाइन ढूंढें और अगली एन लाइनों को प्रिंट करें" जो कि ग्राफ्ट करना आसान है।

यदि फ़ाइल बहुत लंबी नहीं है, क्योंकि आप GNU एक्सटेंशन के साथ ठीक लगते हैं, तो आप tacफ़ाइल की लाइनों को उलटने के लिए उपयोग कर सकते हैं ।

tac | sed '/banana/I,+2 d' | tac

हमले का एक और कोण जागरण जैसे टूल में स्लाइडिंग विंडो बनाए रखना है। से अपनाने का कोई विकल्प है grep के -A -B -C स्विच (कुछ लाइनों को पहले और बाद में प्रिंट करने के लिए)? (चेतावनी: न्यूनतम परीक्षण किया गया):

#!/bin/sh
{ "exec" "awk" "-f" "$0" "$@"; } # -*-awk-*-
# The array h contains the history of lines that are eligible for being "before" lines.
# The variable skip contains the number of lines to skip.
skip { --skip }
match($0, pattern) { skip = before + after }
NR > before && !skip { print NR h[NR-before] }
{ delete h[NR-before]; h[NR] = $0 }
END { if (!skip) {for (i=NR-before+1; i<=NR; i++) print h[i]} }

उपयोग: /path/to/script -v pattern='banana' -v before=2


2
sedस्लाइडिंग विंडो भी कर सकते हैं, लेकिन परिणामी स्क्रिप्ट आमतौर पर इतनी अपठनीय होती है कि इसका उपयोग करना आसान होता है awk
20 जून को jw013

@ गिल्स .. awkस्क्रिप्ट काफी सही नहीं है; जैसा कि यह रिक्त लाइनों को प्रिंट करता है और अंतिम लाइनों को याद करता है। यह इसे ठीक करने के लिए लगता है, लेकिन यह आदर्श या सही नहीं हो सकता है: if (NR-before in h) { print...; delete...; }... और ENDअनुभाग में: for (i in h) print h[i]... इसके अलावा, awk स्क्रिप्ट मिलान रेखा को प्रिंट tac/secकरता है , लेकिन संस्करण नहीं है; लेकिन सवाल इस पर थोड़ा अस्पष्ट है .. "मूल" awk स्क्रिप्ट, जिसके लिए आपने एक लिंक प्रदान किया है, ठीक काम करता है .. मुझे यह पसंद है ... मुझे यकीन नहीं है कि उपरोक्त 'mod' प्रिंट के बाद कैसे प्रभावित करता है तर्ज ...
पीटर

@ पीटर। धन्यवाद, जागृत स्क्रिप्ट अब बेहतर होनी चाहिए। और मुझे ६- years साल से भी कम समय लगा!
गिलेस एसओ- बुराई को रोकना '

19

यह पूर्व या विम-ई के साथ बहुत आसान है

    vim -e - $file <<@@@
g/banana/.-2,.d
wq
@@@

अभिव्यक्ति पढ़ती है: वर्तमान पंक्ति -2 से लेकर वर्तमान रेखा तक केला में प्रत्येक पंक्ति के लिए, हटाएं।

क्या शांत है कि सीमा में पीछे और आगे की खोज भी हो सकती है, उदाहरण के लिए यह फ़ाइल के सभी खंडों को हटा देगा जिसमें एक लाइन सेब से शुरू होती है और एक रेखा के साथ समाप्त होती है जिसमें नारंगी होती है और केले के साथ एक रेखा होती है:

    vim -e - $file <<@@@
g/banana/?apple?,/orange/d
wq
@@@

7

"स्लाइडिंग विंडो" का उपयोग करके perl:

perl -ne 'push @lines, $_;
          splice @lines, 0, 3 if /banana/;
          print shift @lines if @lines > 2
          }{ print @lines;'

6

आप इसे बहुत सरलता से कर सकते हैं sed:

printf %s\\n    1 2 3 4match 5match 6 \
                7match 8 9 10 11match |
sed -e'1N;$!N;/\n.*match/!P;D'

मुझे नहीं पता कि कोई भी अन्यथा क्यों कहेगा, लेकिन एक पंक्ति को खोजने और पिछली पंक्तियोंsed को प्रिंट करने के लिए अंतर्निहित रिंट Pप्रिमिटिव को शामिल किया गया है जो \nपैटर्न स्पेस में केवल पहली ईवलाइन वर्ण तक लिखता है। पूरक Dएलीट आदिम स्क्रिप्ट के पुनरावर्ती पुनर्चक्रण से पहले पैटर्न स्पेस के उसी खंड को हटा देता है। और इसे बंद करने के लिए, एक्सट्रा Nइनपुट लाइन को पैटर्न वाली जगह पर सम्मिलित करने के लिए एक \nप्राइमरी ईवलाइन कैरेक्टर का अनुसरण करने के लिए एक आदिम है ।

ताकि एक लाइन sedआप सभी की जरूरत हो। आप बस matchअपने regexp के साथ प्रतिस्थापित करते हैं और आप सुनहरे हैं। यह एक बहुत तेजी से समाधान के रूप में अच्छी तरह से होना चाहिए ।

यह भी ध्यान दें कि यह पिछले दो पंक्तियों के लिए आउटपुट को शांत करने और इसके प्रिंट को शांत करने के लिए ट्रिगर के रूप में एक matchदूसरे matchको तुरंत ठीक करेगा।


1
7match
8
11match

इसके लिए काम करने के लिए लाइनों की एक मनमानी संख्या के लिए, आपको केवल एक लीड प्राप्त करने की आवश्यकता है।

इसलिए:

    printf %s\\n     1 2 3 4 5 6 7match     \
                     8match 9match 10match  \
                     11match 12 13 14 15 16 \
                     17 18 19 20match       |
    sed -e:b -e'$!{N;2,5bb' -e\} -e'/\n.*match/!P;D'

1
11match
12
13
14
20match

... किसी भी मैच से पहले 5 लाइनों को हटाता है।


1

का उपयोग कर man 1 ed:

str='
1
2
3
banana
4
5
6
banana
8
9
10
'

# using Bash
cat <<-'EOF' | ed -s <(echo "$str")  | sed -e '1{/^$/d;}' -e '2{/^$/d;}'
H
0i


.
,g/banana/km\
'm-2,'md
,p
q
EOF
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.