bash स्ट्रिंग से शुरू होने वाली रेखाओं को ढूंढता है


10

मेरे पास फाइलों का एक गुच्छा है और मैं यह खोजना चाहता हूं कि किसमें अनुक्रमिक लाइनें हैं जो एक निश्चित स्ट्रिंग से शुरू होती हैं।

निम्न फ़ाइल के लिए उदाहरण के लिए:

Aaaaaaaaaaaa
Baaaaaaaaaaa
Cxxxxxxxxx
Cyyyyyyyyy
Czzzzzzzzz
Abbbbbbbbbbb
Bbbbbbbbbbbb
Caaaaaa
Accccccccccc
Bccccccccccc
Cdddddd
Ceeeeee

'C' से शुरू होने वाली एक से अधिक लाइन है, इसलिए मैं चाहता हूं कि यह फाइल कमांड से मिल जाए।
निम्न फ़ाइल के लिए उदाहरण के लिए:

Aaaaaaaaaaaa
Baaaaaaaaaaa
Cxxxxxxxxx
Abbbbbbbbbbb
Bbbbbbbbbbbb
Caaaaaa
Accccccccccc
Bccccccccccc
Cdddddd

हमेशा 'C' से शुरू होने वाली एक लाइन होती है, मुझे यह फाइल नहीं चाहिए। मैं एक grepया एक का उपयोग करने के बारे में सोचा, sedलेकिन मुझे नहीं पता कि यह कैसे करना है। शायद एक regexp ^C.*$^Cया ऐसा कुछ का उपयोग कर । कोई उपाय ?


Cआपके दूसरे उदाहरण से शुरू होने वाली दो लाइनें हैं ।
cuonglm

5
यह प्रश्न स्पष्ट नहीं है। क्या आप उन फ़ाइलों की तलाश में हैं जिनके साथ एक से अधिक लगातार लाइन शुरू होती है C?
ग्रीम

हाँ यही तो मैं चाहता हूँ। गलतफहमी के लिए खेद है।
जेरामेई

2
@terdon, ऐसा लगता है कि बहु-पंक्ति खोजों के साथ -P ने 2.5.4 तक काम किया और उसके बाद अब और नहीं, हालांकि मुझे चैंज में कुछ भी नहीं मिल रहा है जो समझाएगा कि क्यों।
स्टीफन चेज़लस

1
@Gememe आप अपने उत्तर को हटाना चाहते हैं, स्टीफन की टिप्पणी देखें, जाहिर है यह कुछ पुराने grepसंस्करणों के लिए काम करता है ।
terdon

जवाबों:


5

के साथ pcregrep:

pcregrep -rMl '^C.*\nC' .

POSIXly:

find . -type f -exec awk '
  FNR==1 {last=0; printed=0; next}
  printed {next}
  /^C/ {if (last) {print FILENAME; printed=1; nextfile} else last=1; next}
  {last=0}' {} +

(हालांकि इसका मतलब है कि उन सभी फाइलों को पूरी तरह से उन awkकार्यान्वयनों के साथ पढ़ना जो समर्थन नहीं करते हैं nextfile)।


grep2.5.4 तक GNU के संस्करणों के साथ :

grep -rlP '^C.*\nC' .

काम करने के लिए प्रकट होता है, लेकिन यह दुर्घटना से है और यह काम करने की गारंटी नहीं है।

इससे पहले कि यह 2.6 ( इस प्रतिबद्ध द्वारा ) में तय किया गया था , जीएनयू grepने अनदेखी की थी कि जो पीसीआर खोज फ़ंक्शन का उपयोग कर रहा था वह वर्तमान में संसाधित किए गए पूरे बफर पर मेल खाएगा grep, जिससे सभी प्रकार के आश्चर्यजनक व्यवहार होंगे। उदाहरण के लिए:

grep -P 'a\s*b'

युक्त फ़ाइल पर मेल खाएगा:

bla
bla

यह मैच होगा:

printf '1\n2\n' | grep -P '1\n2'

लेकिन यह:

(printf '1\n'; sleep 1; printf '2\n') | grep -P '1\n2'

या:

(yes | head -c 32766; printf '1\n2\n') > file; grep -P '1\n2' file

नहीं होगा (जैसा कि 1\n2\nदो बफर्स ​​द्वारा संसाधित है grep)।

हालांकि यह व्यवहार समाप्त हो गया है:

15- मैं लाइनों में कैसे मेल कर सकता हूं?

मानक grep ऐसा नहीं कर सकता, क्योंकि यह मौलिक रूप से लाइन-आधारित है। इसलिए, '[: space:]' वर्ण वर्ग का उपयोग करने से आप जिस तरह की उम्मीद कर सकते हैं, उसमें नई कहानियों से मेल नहीं खाता है। हालाँकि, यदि आपके grep को पर्ल पैटर्न के साथ संकलित किया गया है, तो पर्ल के मॉडिफायर (जो बनाता है। 'मैच की नई संख्या) का उपयोग किया जा सकता है।

     printf 'foo\nbar\n' | grep -P '(?s)foo.*?bar'

2.6 में तय होने के बाद, दस्तावेज में संशोधन नहीं किया गया था (मैंने एक बार इसकी रिपोर्ट की थी )।


क्या इसका कोई कारण नहीं है exitऔर -exec \;इसके बजाय नेक्स्टइल का उपयोग नहीं करना है ?
terdon

@terdon, इसका मतलब होगा कि awkप्रति फ़ाइल एक रनिंग । आप ऐसा करना चाहते हैं, केवल अगर आपका awkसमर्थन नहीं करता है nextfileऔर आपको बड़ी मात्रा में ऐसी फाइलें मिली हैं जो बड़ी हैं और फाइल की शुरुआत के लिए मेल खाने वाली लाइनें हैं।
स्टीफन चेज़लस

इस grep तकनीक के बारे में कैसे पता चलता है (मैं GNU grep के अधिक हाल के संस्करणों के साथ अनुमान लगाता हूं) जो एनयूएल को लाइन टर्मिनेटर सेट करके पूरी फाइल को एकल स्ट्रिंग की तरह बनाते हुए मल्टीलाइन मैचों की सुविधा देता है - क्या आप जानते होंगे कि इसकी कोई सीमाएं हैं?
इरुवर

1
@ 1_CR, यह पूरी फ़ाइल को मेमोरी में लोड कर देगा यदि वहाँ कोई NUL वर्ण नहीं है और यह मानता है कि लाइनों में NUL वर्ण नहीं हैं। यह भी ध्यान रखें कि जीएनयू ग्रेप (ओ पी है) के पुराने संस्करणों का उपयोग नहीं कर सकते हैं -zके साथ -P। इसके \Nबिना कोई नहीं है -P, आपको इसे लिखने की आवश्यकता $'[\01-\011\013-\0377]'होगी जो केवल सी स्थानों में काम करेगा (देखें थ्रेड। gmane.org/gmane.comp.gnu.grep.bugs/5187 )
स्टीफन चेज़लस

@StephaneChazelas, बहुत उपयोगी विवरण, धन्यवाद
iruvar

2

के साथ awk:

awk '{if (p ~ /^C/ && $1 ~ /^C/) print; p=$1}' afile.txt

यह फ़ाइल की सामग्री को प्रिंट करेगा अगर वहाँ लगातार लाइनें एक के साथ शुरू कर रहे हैं C। अभिव्यक्ति (p ~ /^C/ && $1 ~ /^C/)फ़ाइल में क्रमिक लाइनों में दिखाई देगी और दोनों मैच में पहला चरित्र होने पर सच का मूल्यांकन करेगी C। अगर ऐसा है, तो लाइन प्रिंट की जाएगी।

ऐसी सभी फ़ाइलों को खोजने के लिए, जिनमें एक पैटर्न है, आप एक findकमांड के माध्यम से उपरोक्त awk चला सकते हैं :

find /your/path -type f -exec awk '{if (p ~ /^C/ && $1 ~ /^C/) {print FILENAME; exit;} p=$1}' {} \;

इस कमांड में, find+ execप्रत्येक फाइल के माध्यम से जाएगा और awkप्रत्येक फाइल पर समान फ़िल्टरिंग करेगा और इसके माध्यम से उसका नाम प्रिंट करेगा FILENAMEयदि awk एक्सप्रेशन का मूल्यांकन सही है। FILENAMEएक ही फाइल के लिए कई बार प्रिंटिंग से बचने के लिए कई मैचों के साथ exitस्टेटमेंट का उपयोग किया जाता है (धन्यवाद @terdon)।


मेरा प्रश्न पर्याप्त रूप से स्पष्ट नहीं था, मैं एक से अधिक लगातार लाइन के साथ फाइलों का नाम जानना चाहता हूंC
Jérémie

@ Jérémie मैंने अपना उत्तर अपडेट किया।
mkc

क्या आप इस बात की व्याख्या कर सकते हैं कि यह कैसे काम करता है? इसके अलावा, इसके बजाय flag, कोई ज़रूरत नहीं है exit। इस तरह, आपको मैच देखने के बाद प्रोसेसिंग फाइल रखने की जरूरत नहीं है।
terdon

2

जीएनयू के साथ एक और विकल्प sed:

एक फ़ाइल के लिए:

sed -n -- '/^C/{n;/^C/q 1}' "$file" || printf '%s\n' "$file"

(हालांकि यह उन फ़ाइलों की भी रिपोर्ट करेगा जो इसे पढ़ नहीं सकती हैं)।

के लिए find:

find . -type f ! -exec sed -n '/^C/{n;/^C/q 1}' {} \; -print

अपठनीय फ़ाइलों के साथ समस्या को मुद्रित किया जा सकता है इसे लिखने से बचा जा सकता है:

find . -type f -size +2c -exec sed -n '$q1;/^C/{n;/^C/q}' {} \; -print

क्या आप कृपया विस्तार से बता सकते हैं sed -n '$q1;/^C/{n;/^C/q}'?
Jérémie

कोई मुझे समझाए?
जेरीमे

@ Jérémie $q1- अगर पैटर्न नहीं मिला है, तो एक त्रुटि के साथ छोड़ने के लिए sed मजबूर करता है। यह त्रुटि के साथ भी समाप्त होगा यदि फ़ाइल में कुछ गड़बड़ है (यह अपठनीय या टूटा हुआ है)। तो यह 0 एग्जिट स्टेटस के साथ ही चलेगा जब केस पैटर्न मिल जाएगा और इसे प्रिंट करने के लिए पास कर दिया जाएगा। साथ भाग /^C/{n;/^C/qबहुत सरल है। यदि यह स्ट्रिंग पाता है जो C से शुरू होता है तो यह अगली पंक्ति को पढ़ेगा और यदि यह C से शुरू होता है तो यह शून्य निकास स्थिति के साथ छोड़ देगा।
भीड़

1

अपनी फ़ाइलों को स्मृति में पढ़ने के लिए पर्याप्त छोटा मान लें:

perl -000ne 'print "$ARGV\n" if /^C[^\n]*\nC/sm' *

स्पष्टीकरण:

  • - - रिकॉर्ड विभाजक के रूप में 000सेट करें \n\n, यह पैराग्राफ मोड को चालू करता है जो पैराग्राफ (लगातार न्यूलाइन द्वारा अलग) को एकल लाइनों के रूप में व्यवहार करेगा।
  • -ne: -eइनपुट फ़ाइल (एस) की प्रत्येक पंक्ति के तर्क के रूप में दी गई स्क्रिप्ट को लागू करें ।
  • $ARGV : वर्तमान में संसाधित की जा रही फ़ाइल है
  • /^C[^\n]*\nC/: Cएक पंक्ति की शुरुआत में मेल करें ( smयह काम क्यों करता है, इसके लिए नीचे दिए गए संशोधक का विवरण देखें ) 0 या उससे अधिक गैर-न्यूलाइन वर्ण, एक नई पंक्ति और फिर एक और सी। दूसरे शब्दों में, लगातार लाइनों के साथ शुरू करें C। * //sm: ये मैच संशोधक हैं (जैसा कि [यहाँ] प्रलेखित है):

    • m : स्ट्रिंग को कई लाइनों के रूप में मानते हैं। यही है, स्ट्रिंग के भीतर कहीं भी मिलान करने के लिए स्ट्रिंग के बाएं और दाएं छोर पर लाइन के प्रारंभ या अंत के मिलान से "^" और "$" बदलें।

    • s : स्ट्रिंग को सिंगल लाइन मानते हैं। यही है, परिवर्तन "।" किसी भी चरित्र से मेल खाने के लिए, यहां तक ​​कि एक नई पंक्ति, जो आम तौर पर यह मेल नहीं खाती।

आप कुछ बदसूरत भी कर सकते हैं जैसे:

for f in *; do perl -pe 's/\n/%%/' "$f" | grep -q 'C[^%]*%%C' && echo "$f"; done

इधर, perlकोड के साथ नई-पंक्तियों की जगह %%ऐसा है, तो यह सोचते हैं आप कोई है %%अपने इनपुट फ़ाइल में (बड़े अगर निश्चित रूप से), grepके साथ शुरू लगातार लाइनों से मेल खाएगी C


1

समाधान:

( set -- *files ; for f ; do (
set -- $(printf %c\  `cat <$f`)
while [ $# -ge 1 ] ;do [ -z "${1#"$2"}" ] && {
    echo "$f"; break ; } || shift
done ) ; done )

डेमो:

सबसे पहले, हम एक परीक्षण आधार बनाएंगे:

abc="a b c d e f g h i j k l m n o p q r s t u v w x y z" 
for l in $abc ; do { i=$((i+1)) h= c= ;
    [ $((i%3)) -eq 0 ] && c="$l" h="${abc%"$l"*}"
    line="$(printf '%s ' $h $c ${abc#"$h"})"
    printf "%s$(printf %s $line)\n" $line >|/tmp/file${i}
} ; done

उपरोक्त /tmpनाम में 26 फाइलें बनाता है file1-26प्रत्येक फ़ाइल में अक्षरों के साथ शुरू होने वाली 27 या 28 लाइनें होती हैं a-zऔर उसके बाद बाकी अक्षर होते हैं। हर तीसरी फ़ाइल में दो लगातार लाइनें होती हैं जिसमें पहला वर्ण डुप्लिकेट होता है।

नमूना:

cat /tmp/file12
...
aabcdefghijkllmnopqrstuvwxyz
babcdefghijkllmnopqrstuvwxyz
cabcdefghijkllmnopqrstuvwxyz
...
kabcdefghijkllmnopqrstuvwxyz
labcdefghijkllmnopqrstuvwxyz
labcdefghijkllmnopqrstuvwxyz
mabcdefghijkllmnopqrstuvwxyz
...

और जब मैं बदलता हूं:

set -- *files

सेवा:

set -- /tmp/file[0-9]*

मुझे मिला...

उत्पादन:

/tmp/file12
/tmp/file15
/tmp/file18
/tmp/file21
/tmp/file24
/tmp/file3
/tmp/file6
/tmp/file9

तो, संक्षेप में, समाधान इस तरह काम करता है:

setरों subshell अपनी सभी फ़ाइलों को positionals, और प्रत्येक के लिए

setएस प्रत्येक फ़ाइल के रूप में लूप में प्रत्येक पंक्ति के पहले अक्षर के लिए एक नेस्टेड subshell के positionals।

[ tests ]यदि मैच का संकेत देने वाले $1नेगेट करते हैं $2, और यदि ऐसा है

echoesफ़ाइल नाम तो breakहै वर्तमान पाश यात्रा

बाकी shiftरों अगले एकल वर्ण स्थितीय के लिए फिर से कोशिश करने


0

यह स्क्रिप्ट का उपयोग करता है grep और cutकिसी भी लगातार दो नंबरों के लिए मिलान लाइनों की लाइन नंबर, और चेक प्राप्त करने के लिए। फ़ाइल को एक मान्य फ़ाइल नाम माना जाता है जो स्क्रिप्ट के पहले तर्क के रूप में दिया गया है:

#!/bin/bash

checkfile () {
 echo checking $1
 grep -n -E "^C.*$" $1 | cut -d: -f1 | while read linenum
     do
        : $[ ++PRV ] 
        if [ $linenum == $PRV ]; then return 1; fi
        PRV=$linenum
     done
     return 0
}

PRV="-1"
checkfile $1
if [ $? == 0 ]; then
   echo Consecutive matching lines found in file $1
fi
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.