NON GNU awk के साथ संशोधनों को सहेजें


9

मुझे एक प्रश्न (SO पर ही) आया है, जहाँ OP को स्वयं इनपुट_फाइल (s) में ऑपरेशन को संपादित और सहेजना है।

मैं एक एकल इनपुट_फाइल के लिए जानता हूं जो हम निम्नलिखित कर सकते हैं:

awk '{print "test here..new line for saving.."}' Input_file > temp && mv temp Input_file

अब हम कहते हैं कि हमें एक ही तरह की फ़ाइलों के प्रारूप में परिवर्तन करने की आवश्यकता है (मान लें। यहाँ क्लिक करें)।

मैंने इस समस्या के लिए क्या सोचा / सोचा है: इसका दृष्टिकोण .txt फ़ाइलों के लूप से गुजर रहा है और सिंगल कॉलिंगawkएक दर्दनाक और अनुशंसित प्रक्रिया नहीं है, क्योंकि यह अनावश्यक सीपीयू चक्रों को बेकार कर देगा और अधिक संख्या में फ़ाइलों के लिए अधिक होगा धीमी गति से।

तो संभवतः एक गैर GNU के साथ कई फ़ाइलों के लिए inplace edit करने के लिए यहाँ क्या किया जा सकता है awkजो inplace विकल्प का समर्थन नहीं करता है। मैं थ्रेड के साथ इस थ्रेड सेव संशोधनों में भी जा चुका हूं, लेकिन गैर GNU awk वाइस के लिए ज्यादा कुछ नहीं है और कई फाइलों को awkअपने भीतर बदल रहा है, क्योंकि एक गैर GNU awk के पास इसका inplaceविकल्प नहीं होगा ।

ध्यान दें: मैंbashकब से टैगजोड़ रहा हूं, मेरे उत्तर भाग में मैंने अस्थायी फ़ाइलों का नाम बदलकर उनके वास्तविक Input_file नामों में नाम जोड़ने के लिए bash कमांड का उपयोग किया है।



EDIT: एड सर की टिप्पणी के अनुसार, यहाँ नमूनों का एक उदाहरण है, हालाँकि इस थ्रेड कोड का उपयोग सामान्य प्रयोजन के द्वारा भी किया जा सकता है।

नमूना Input_file (s):

cat test1.txt
onetwo three
tets testtest

cat test2.txt
onetwo three
tets testtest

cat test3.txt
onetwo three
tets testtest

अपेक्षित उत्पादन का नमूना:

cat test1.txt
1
2

cat test2.txt
1
2

cat test3.txt
1
2

1
दिलचस्प और प्रासंगिक
जाग

1
@ RavinderSingh13 यदि आपके पास इसे लागू करने के लिए फ़ाइलों का एक पूरा गुच्छा है awk, तो (शायद एक उपखंड में) या एक {...}संलग्न समूह के लिए एक कॉल का उपयोग क्यों न करें और फिर वांछित आउटपुट फ़ाइल (प्रत्येक इनपुट फ़ाइल के लिए, या तो परिणाम लिखें) या सभी इनपुट फ़ाइलों के लिए एक संयुक्त फ़ाइल)। तब आप केवल वर्तमान फ़ाइल में सब्स्क्रिप्शन या ब्रेस-एनक्लोज्ड ग्रुप के आउटपुट को रीडायरेक्ट करते हैं? बस इनपुट फ़ाइलों की एक स्ट्रिंग सहित awkकमांड के बाद क्रमिक रूप से सभी फाइलों (या कुछ इसी तरह) की प्रक्रिया होगी ??
डेविड सी। रंकिन

@ DavidC.Rankin, इस पर जवाब देने के लिए धन्यवाद। हाँ, मैंने इसी तरह का सामान पोस्ट किया है जो आप कह रहे हैं सर, मेरा उत्तर भी इस प्रश्न में पोस्ट किया गया है कि एक ही महोदय के बारे में आपके विचार जानते हैं, चीयर्स।
रविंदरसिंह १३

1
कुछ नींद और इसके बारे में सोचने के बाद, मैं awk {..} file1 .. fileXसंशोधित फ़ाइल लिखने के साथ 2 विकल्प (1) देखता हूं , जैसे कि temp01और अगली फ़ाइल को संसाधित करते समय आपकी अगली पुनरावृत्ति में, mv -f tmp01 input01संशोधित डेटा के साथ इनपुट फ़ाइल को अधिलेखित करने के लिए; या (2) बस स्क्रिप्ट ./tmp/tmp01 ... ./tmp/tmp0Xके निष्पादन के दौरान एक नई निर्देशिका लिखें और निर्देशिका awkमें फ़ाइलों पर एक लूप के साथ अनुवर्ती करें ./tmpऔर, उदाहरण के लिए mv -f "$i" "input_${i##*[^0-9]}"(या जो भी विस्तार आपको पुरानी इनपुट फ़ाइलों को बदलने की आवश्यकता है।
डेविड सी। रैंकिन

@ DavidC.Rankin, अपने विचारों को यहां बताने के लिए धन्यवाद सर, IMHO 1 विकल्प थोड़ा जोखिम भरा हो सकता है, क्योंकि हम बिना कुछ किए awkपूरा कर रहे हैं पूरा कोड, दूसरा विकल्प लगभग वही है जो मैं अपने सुझाव में उपयोग कर रहा हूं, आभारी रहें यदि आप उस समाधान पर अपने विचार बता सकते हैं।
रविन्दरसिंह

जवाबों:


6

चूँकि इस सूत्र का मुख्य उद्देश्य यह है कि NON GNU में इनवेस्ट कैसे करें, awkइसलिए मैं इसका पहला टेम्पलेट पोस्ट कर रहा हूँ जो किसी को भी किसी भी प्रकार की आवश्यकता में मदद करेगा, उन्हें अपने मुख्य ब्लॉक को अपने अनुसार रखते हुए अपने कोड में जोड़ना / जोड़ना BEGINऔर जोड़ना होगा ENDआवश्यकता है और यह तब करना चाहिए कि इनप्लेट एडिट हो:

नोट: इसके बाद अपने सभी आउटपुट को output_file पर लिखेंगे, इसलिए यदि आप मानक आउटपुट के लिए कुछ भी प्रिंट करना चाहते हैं, तो कृपया केवलनिम्नलिखित केprint...बिना स्टेटमेंटजोड़ें> (out)

सामान्य टेम्पलेट:

awk -v out_file="out" '
FNR==1{
close(out)
out=out_file count++
rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047"
}
{
    .....your main block code.....
}
END{
 if(rename){
   system(rename)
 }
}
' *.txt


विशिष्ट प्रदान नमूना का समाधान:

मैं अपने भीतर निम्नलिखित दृष्टिकोण के साथ आया हूं awk(निम्नलिखित नमूनों के लिए यह हल करने के लिए मेरा दृष्टिकोण है और खुद इनपुट_फाइल में आउटपुट को बचाने के लिए)

awk -v out_file="out" '
FNR==1{
  close(out)
  out=out_file count++
  rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047"
}
{
  print FNR > (out)
}
END{
  if(rename){
    system(rename)
  }
}
' *.txt

नोट: यह केवल इनपुट_फाइल (एड) में संपादित आउटपुट को बचाने के लिए एक परीक्षण है, कोई अपने BEGIN सेक्शन का उपयोग कर सकता है, साथ ही अपने प्रोग्राम में अपने END सेक्शन के साथ, मुख्य सेक्शन विशिष्ट प्रश्न की आवश्यकता के अनुसार होना चाहिए।

उचित चेतावनी: चूंकि यह दृष्टिकोण पथ में एक नई अस्थायी फ़ाइल बनाता है इसलिए बेहतर है कि हम सिस्टम पर पर्याप्त स्थान सुनिश्चित करें, हालांकि अंतिम परिणाम में यह केवल मुख्य Input_file (s) रखेगा, लेकिन संचालन के दौरान इसे सिस्टम / निर्देशिका पर स्थान की आवश्यकता होती है



निम्नलिखित उपरोक्त कोड के लिए एक परीक्षण है।

एक उदाहरण के साथ कार्यक्रम का निष्पादन: मान लें कि निम्नलिखित.txtInput_file (s) हैं:

cat << EOF > test1.txt
onetwo three
tets testtest
EOF

cat << EOF > test2.txt
onetwo three
tets testtest
EOF

cat << EOF > test3.txt
onetwo three
tets testtest
EOF

अब जब हम निम्नलिखित कोड चलाते हैं:

awk -v out_file="out" '
FNR==1{
  close(out)
  out=out_file count++
  rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047"
}
{
  print "new_lines_here...." > (out)
}
END{
  if(rename){
    system("ls -lhtr;" rename)
  }
}
' *.txt

नोट: मैं जगह नहीं हैls -lhtrमेंsystemजानबूझकर जो उत्पादन फ़ाइलों यह पैदा कर रही है (अस्थायी आधार) को देखने के लिए, क्योंकि बाद में यह उन्हें अपने वास्तविक नाम में नाम बदल देगा अनुभाग।

-rw-r--r-- 1 runner runner  27 Dec  9 05:33 test2.txt
-rw-r--r-- 1 runner runner  27 Dec  9 05:33 test1.txt
-rw-r--r-- 1 runner runner  27 Dec  9 05:33 test3.txt
-rw-r--r-- 1 runner runner  38 Dec  9 05:33 out2
-rw-r--r-- 1 runner runner  38 Dec  9 05:33 out1
-rw-r--r-- 1 runner runner  38 Dec  9 05:33 out0

जब हम एक ls -lhtrआफ्टर awk स्क्रिप्ट रनिंग के साथ करते हैं, तो हम .txtवहां केवल फाइल्स देख सकते हैं ।

-rw-r--r-- 1 runner runner  27 Dec  9 05:33 test2.txt
-rw-r--r-- 1 runner runner  27 Dec  9 05:33 test1.txt
-rw-r--r-- 1 runner runner  27 Dec  9 05:33 test3.txt


स्पष्टीकरण: ऊपर दिए गए आदेश का एक विस्तृत विवरण यहां जोड़ रहा है:

awk -v out_file="out" '                                    ##Starting awk program from here, creating a variable named out_file whose value SHOULD BE a name of files which are NOT present in our current directory. Basically by this name temporary files will be created which will be later renamed to actual files.
FNR==1{                                                    ##Checking condition if this is very first line of current Input_file then do following.
  close(out)                                               ##Using close function of awk here, because we are putting output to temp files and then renaming them so making sure that we shouldn't get too many files opened error by CLOSING it.
  out=out_file count++                                     ##Creating out variable here, whose value is value of variable out_file(defined in awk -v section) then variable count whose value will be keep increment with 1 whenever cursor comes here.
  rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047"     ##Creating a variable named rename, whose work is to execute commands(rename ones) once we are done with processing all the Input_file(s), this will be executed in END section.
}                                                          ##Closing BLOCK for FNR==1  condition here.
{                                                          ##Starting main BLOCK from here.
  print "new_lines_here...." > (out)                       ##Doing printing in this example to out file.
}                                                          ##Closing main BLOCK here.
END{                                                       ##Starting END block for this specific program here.
  if(rename){                                              ##Checking condition if rename variable is NOT NULL then do following.
    system(rename)                                         ##Using system command and placing renme variable inside which will actually execute mv commands to rename files from out01 etc to Input_file etc.
  }
}                                                          ##Closing END block of this program here.
' *.txt                                                    ##Mentioning Input_file(s) with their extensions here.

1
मजेदार तथ्य: यदि आप FNR==1ब्लॉक में इनपुट फ़ाइल को हटाते हैं , तो आप अभी भी परिवर्तन करने वाले को सहेज सकते हैं। की तरह awk 'FNR==1{system("rm " FILENAME)} {print "new lines" > FILENAME}' files...। यह बिल्कुल भी विश्वसनीय नहीं है (पूर्ण डेटा हानि होने की संभावना है), लेकिन फिर भी, यह ज्यादातर ठीक काम करता है: D
oguz ismail

1
बहुत अच्छी तरह से काम के आसपास समझाया
शुभ

3

मैं शायद इस तरह से कुछ के साथ जाऊँगा अगर मैं ऐसा करने की कोशिश करूँ:

$ cat ../tst.awk
FNR==1 { saveChanges() }
{ print FNR > new }
END { saveChanges() }

function saveChanges(   bak, result, mkBackup, overwriteOrig, rmBackup) {
    if ( new != "" ) {
        bak = old ".bak"
        mkBackup = "cp \047" old "\047 \047" bak "\047; echo \"$?\""
        if ( (mkBackup | getline result) > 0 ) {
            if (result == 0) {
                overwriteOrig = "mv \047" new "\047 \047" old "\047; echo \"$?\""
                if ( (overwriteOrig | getline result) > 0 ) {
                    if (result == 0) {
                        rmBackup = "rm -f \047" bak "\047"
                        system(rmBackup)
                    }
                }
            }
        }
        close(rmBackup)
        close(overwriteOrig)
        close(mkBackup)
    }
    old = FILENAME
    new = FILENAME ".new"
}

$ awk -f ../tst.awk test1.txt test2.txt test3.txt

मैंने पहले मूल फ़ाइल को बैकअप में कॉपी करना पसंद किया है और फिर उस सहेजने वाले परिवर्तनों को मूल में संचालित किया है, लेकिन ऐसा करने से प्रत्येक इनपुट फ़ाइल के लिए FILENAME चर का मूल्य बदल जाएगा जो अवांछनीय है।

ध्यान दें कि यदि आपके पास एक मूल फ़ाइल थी जिसका नाम whatever.bakया whatever.newआपकी निर्देशिका है, तो आप उन्हें अस्थायी फ़ाइलों के साथ अधिलेखित कर देंगे, इसलिए आपको इसके लिए एक परीक्षण भी जोड़ना होगा। mktempअस्थायी फ़ाइल नाम प्राप्त करने के लिए एक कॉल अधिक मजबूत होगी।

इस स्थिति में होने के लिए FAR अधिक उपयोगी चीज एक ऐसा उपकरण होगा जो किसी भी अन्य कमांड को निष्पादित करता है और "inplace" संपादन भाग को करता है क्योंकि इसका उपयोग POSIX sed, awk, grep, tr, जो भी हो और के लिए "inplace" संपादन प्रदान करने के लिए किया जा सकता है। print > outहर बार जब आप मान छापना चाहते हैं, तो आपको अपनी स्क्रिप्ट का सिंटैक्स बदलने आदि की आवश्यकता नहीं होगी । एक सरल, नाजुक, उदाहरण:

$ cat inedit
#!/bin/env bash

for (( pos=$#; pos>1; pos-- )); do
    if [[ -f "${!pos}" ]]; then
        filesStartPos="$pos"
    else
        break
    fi
done

files=()
cmd=()
for (( pos=1; pos<=$#; pos++)); do
    arg="${!pos}"
    if (( pos < filesStartPos )); then
        cmd+=( "$arg" )
    else
        files+=( "$arg" )
    fi
done

tmp=$(mktemp)
trap 'rm -f "$tmp"; exit' 0

for file in "${files[@]}"; do
    "${cmd[@]}" "$file" > "$tmp" && mv -- "$tmp" "$file"
done

जो आप निम्नानुसार उपयोग करेंगे:

$ awk '{print FNR}' test1.txt test2.txt test3.txt
1
2
1
2
1
2

$ ./inedit awk '{print FNR}' test1.txt test2.txt test3.txt

$ tail test1.txt test2.txt test3.txt
==> test1.txt <==
1
2

==> test2.txt <==
1
2

==> test3.txt <==
1
2

उस ineditस्क्रिप्ट के साथ एक स्पष्ट समस्या इनपुट / आउटपुट फ़ाइलों को कमांड से अलग से पहचानने में कठिनाई होती है जब आपके पास कई इनपुट फाइलें होती हैं। ऊपर दी गई स्क्रिप्ट सभी इनपुट फ़ाइलों को मानती है जो कमांड के अंत में एक सूची के रूप में दिखाई देती हैं और कमांड को एक बार में उनके खिलाफ चलाया जाता है, लेकिन निश्चित रूप से इसका मतलब है कि आप इसे उन स्क्रिप्ट के लिए उपयोग नहीं कर सकते हैं जिनके लिए 2 या अधिक फ़ाइलों की आवश्यकता होती है एक समय, उदाहरण के लिए:

awk 'NR==FNR{a[$1];next} $1 in a' file1 file2

या स्क्रिप्ट जो arg सूची में फ़ाइलों के बीच चर सेट करती है, जैसे:

awk '{print $7}' FS=',' file1 FS=':' file2

इसे पाठक के लिए एक अभ्यास के रूप में और अधिक मजबूत बना दिया गया है, लेकिन xargsसिनॉप्सिस को शुरुआती बिंदु के रूप में देखें कि कैसे एक मजबूत ineditकाम करने की आवश्यकता होगी :-)।


0

खोल समाधान सरल और संभव जल्दी पर्याप्त है:

for f in *.txt
do  awk '...' $f > $f.tmp
    mv $f.tmp $f
done

केवल एक अलग समाधान की खोज करें यदि आपने निर्णायक रूप से प्रदर्शित किया है कि यह बहुत धीमा है। याद रखें: समयपूर्व अनुकूलन सभी बुराई की जड़ है।


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