अनुक्रमिक सूचकांक के साथ स्ट्रिंग बदलें


10

किसी को यह पूरा करने के लिए एक सुंदर तरीका सुझा सकते हैं?

इनपुट:

test  instant  ()

test  instant  ()

...
test  instant  ()    //total 1000 lines

आउटपुट होना चाहिए:

test      instant1  ()

test      instant2  ()

test      instant1000()

खाली लाइनें मेरे इनपुट फ़ाइलों में हैं और एक ही निर्देशिका के तहत कई फाइलें हैं जिन्हें मुझे एक बार संसाधित करने की आवश्यकता है।

मैंने एक ही dir में कई फाइलों को बदलने की कोशिश की और काम नहीं किया।

for file in ./*; do perl -i -000pe 's/instance$& . ++$n/ge' "$file"; done

त्रुटियों:

Substitution replacement not terminated at -e line 1.
Substitution replacement not terminated at -e line 1.

और मैंने यह भी कोशिश की:

perl -i -pe 's/instant/$& . ++$n/ge' *.vs

यह काम किया लेकिन सूचकांक सिर्फ एक से दूसरे फाइल में वृद्धि करता रहा। मैं एक नई फ़ाइल में परिवर्तन करने के लिए उसे 1 पर रीसेट करना चाहूंगा। कोई अच्छा सुझाव?

find . -type f -exec perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' {} +

काम करता है लेकिन इसे अन्य सभी फ़ाइलों को प्रतिस्थापित नहीं किया जाना चाहिए। मैं *.txtकेवल फाइलों को केवल के साथ बदलना पसंद करता हूं ।


और क्या वे सभी विशेष रूप से या तो रिक्त लाइनों से युक्त होते हैं या test instant ()?
terdon

मैंने दोहरी स्थान रेखाएँ वापस डाल दीं, वे अक्सर नए उपयोगकर्ताओं का संकेत होते हैं कि वे इस साइट के मार्कअप का उपयोग करने का तरीका नहीं जानते हैं, इसीलिए टेर्डन ने आपकी फ़ाइल सामग्री ब्लॉक को ठीक से इंडेंट करते समय उन्हें हटा दिया ताकि यह फ़ाइल सामग्री के रूप में दिखाई दे। उम्मीद है कि अब ठीक है।
तिमो

जवाबों:


14
perl -pe 's/instant/$& . ++$n/ge'

या GNU के साथ awk:

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

फ़ाइलों को जगह में संपादित -iकरने के लिए, perlनिम्न विकल्प जोड़ें :

perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' ./*.vs

या पुनरावर्ती:

find . -name '*.vs' -type f -exec perl -pi -e '
  s/instant/$& . ++$n{$ARGV}/ge' {} +

स्पष्टीकरण

perl -pe 's/instant/$& . ++$n/ge'

-pइनपुट लाइन को लाइन से प्रोसेस करना है, -eप्रत्येक लाइन के लिए दिए गए एक्सप्रेशन का मूल्यांकन करें और उसे प्रिंट करें। प्रत्येक पंक्ति के लिए, हम स्वयं ( ) के लिए ( s/re/repl/flagsऑपरेटर का उपयोग करके ) और एक चर के बढ़े हुए मूल्य को प्रतिस्थापित करते हैं । झंडा विश्व स्तर पर (सिर्फ एक बार नहीं) प्रतिस्थापन बनाने के लिए है, और इतना है कि प्रतिस्थापन के लिए पर्ल कोड के रूप में व्याख्या की है का मूल्यांकन (नहीं एक निश्चित स्ट्रिंग)।instant$&++$nge

इन-प्लेस एडिटिंग के लिए जहां एक perl मंगलाचरण एक से अधिक फ़ाइल प्रोसेस करता है, हम $nप्रत्येक फ़ाइल पर रीसेट करना चाहते हैं । इसके बजाय, हम उपयोग करते हैं $n{$ARGV}(जहां $ARGVवर्तमान में संसाधित फ़ाइल है)।

awkएक स्पष्टीकरण का एक सा योग्य है।

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

हम awkमनमाने ढंग से तार (यहां तक ​​कि regexps) पर अलग रिकॉर्ड के लिए GNU की क्षमता का उपयोग कर रहे हैं । के साथ -vRS=instant, हम r̲ecord s̲eparator को सेट करते हैं instantRTवैरिएबल है जो कि किस चीज से मेल खाता था RS, इसलिए आमतौर पर, instantपिछले रिकॉर्ड को छोड़कर जहां यह खाली स्ट्रिंग होगा। रिकॉर्ड के ऊपर इनपुट में ( $0) और रिकॉर्ड टर्मिनेटर ( RT) हैं [$0|RT]:

[test  |instant][  ()
test  |instant][  ()
...
test  |instant][  ()    //total 1000 lines|]

इसलिए हमें केवल पहले रिकॉर्ड को छोड़कर हर रिकॉर्ड की शुरुआत में एक इंक्रीमेंट नंबर डालना होगा।

जो हम ऊपर करते हैं। पहले रिकॉर्ड के लिए, nखाली होगा। हम ORS ( o̲utput r ORecord sareparator ) को RT पर सेट करते हैं, ताकि awk प्रिंट हो n $0 RT। यह दूसरी अभिव्यक्ति पर करता है ( ++n) जो एक ऐसी स्थिति है जो हमेशा सच (एक गैर-शून्य संख्या) का मूल्यांकन करती है, और इसलिए $0 ORSहर रिकॉर्ड के लिए डिफ़ॉल्ट कार्रवाई (मुद्रण की ) की जाती है।



5

sedवास्तव में नौकरी के लिए सबसे अच्छा साधन नहीं है, आप बेहतर स्क्रिप्टिंग क्षमताओं के साथ कुछ चाहते हैं। यहाँ कुछ विकल्प हैं:

  • पर्ल

    perl -00pe 's/instant/$& . $./e' file 

    -pजो भी स्क्रिप्ट दी गई है उसे लागू करने के बाद "हर लाइन को प्रिंट करें" का मतलब है -e-00"पैरा मोड" तो रिकॉर्ड (लाइनें) पर बारी-बारी से लगातार न्यू लाइन द्वारा परिभाषित कर रहे ( \n) वर्ण, इस इसे सही ढंग से डबल दूरी पर लाइनों के साथ सौदा कर सकते हैं। $&पिछले पैटर्न से मेल खाता है और $.इनपुट फ़ाइल की वर्तमान पंक्ति संख्या है। eमें s///eमुझे प्रतिस्थापन ऑपरेटर में भाव का मूल्यांकन करने के लिए अनुमति देता है।

  • awk (यह मानता है कि आपका डेटा बिल्कुल वैसा ही दिखाया गया है, जैसे तीन अंतरिक्ष अलग-अलग क्षेत्र)

    awk '{if(/./) print $1,$2 ++k,$3; else print}' file 

    यहां, हम केवल kवैरिएबल को बढ़ाते हैं kयदि वर्तमान लाइन खाली नहीं /./है जिस स्थिति में हम आवश्यक जानकारी भी प्रिंट करते हैं। खाली लाइनों के रूप में मुद्रित कर रहे हैं।

  • विभिन्न गोले

     n=0; while read -r a b c; do 
       if [ "$a" ] ; then 
          (( n++ ))
          printf "%s %s%s %s\n" "$a" "$b" "$n" "$c"
       else
          printf "%s %s %s\n" "$a" "$b" "$c"
       fi
     done < file 

    यहां, प्रत्येक इनपुट लाइन व्हॉट्सएप पर स्वचालित रूप से विभाजित हो जाती है और खेतों को सहेजा जाता है $a, $bऔर $c। फिर, पाश के भीतर, $cप्रत्येक पंक्ति के लिए एक के बाद संवर्धित है जिसके लिए $a, खाली नहीं है और यह वर्तमान मूल्य दूसरे क्षेत्र के बगल में मुद्रित किया जाता है $b

नोट: उपरोक्त सभी समाधान यह मानते हैं कि फ़ाइल में सभी लाइनें एक ही प्रारूप की हैं। यदि नहीं, तो @ स्टीफन का उत्तर जाने का रास्ता है।


कई फ़ाइलों से निपटने के लिए, और यह मानते हुए कि आप वर्तमान निर्देशिका की सभी फ़ाइलों के लिए ऐसा करना चाहते हैं , आप इसका उपयोग कर सकते हैं:

for file in ./*; do perl -i -00pe 's/instant/$& . $./e' "$file"; done

CAREFUL: यह सरल फ़ाइल नामों को मानता है, जिनमें कोई रिक्त स्थान नहीं है, अगर कुछ और जटिल चीजों से निपटने की आवश्यकता है, तो (मान लें ksh93, zshया bash):

find . -type f -print0 | while IFS= read -r -d ''; do
    perl -i -00pe 's/instant/$& . $./e' "$file"
done

पर्ल स्क्रिप्ट काम करती है। हालाँकि, एक छोटी सी समस्या है अगर लाइनें डबल स्पेस हैं।
user3342338

@ user3342338 हाँ, कि जब से मैं वर्तमान लाइन नंबर का उपयोग कर रहा हूँ, काउंटर बढ़ जाएगा। यह एक बहुत ही अच्छा दृष्टिकोण है, जैसा कि मैंने कहा कि स्टीफन अधिक मजबूत है। इनमें से कोई भी काम नहीं करता है यदि आपके पास खाली लाइनें हैं या यदि आपकी कोई भी रेखा आपके द्वारा दिखाए गए कार्यों से विचलित है।
terdon

@ user3342338 अद्यतन जवाब देखें। वे सभी अब डबल स्पेज़ फ़ाइलों के लिए काम करना चाहिए।
terdon

शानदार उत्तर और वैकल्पिक तरीकों का विकल्प !! धन्यवाद
Madivad

0

यदि आप इसे हल करना चाहते हैं, तो आप इस sedतरह से कुछ का उपयोग कर सकते हैं bash:

i=0
while read -r line; do
  sed "s/\(instant\)/\1${i}/" <<< "${line}"
  [[ ${line} =~ instant ]] && i=$(( i + 1 ))
done < file

या अधिक पोर्टेबल समाधान होगा:

i=0
while read -r line; do
  echo "${line}" | sed "s/\(instant\)/\1${i}/"
  if echo "${line}" | grep -q inst; then
    i=$(( i + 1 ))
  fi
done < file
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.