rm कमांड लाइन पर काम करता है लेकिन स्क्रिप्ट में नहीं


11

जब मैं rm *.old.*कमांड लाइन पर करता हूं तो यह सही तरीके से निकल जाता है, लेकिन जब मैं इसे अपनी स्क्रिप्ट के निम्नलिखित भाग में करता हूं, तो यह सभी *.old.*फाइलों को rm नहीं करता है ।

मेरी बैश स्क्रिप्ट में क्या गलत है:

 for i in ./*; do
    if [[ -f $i ]];  then

        if [[ $i  ==  *.old.* ]]; then
                oldfile=$i
                echo "this file is to be removed: $oldfile"
                rm $oldfile
                exec 2>errorfile
            if [ -s $errorfile ]
            then
                echo "rm failed"
            else
                echo "removed $oldfile!"
            fi
        else
            echo "file with old extension  does not exist"
        fi

        orig=$i
        dest=$i.old
        cp $orig $dest
        echo "Copied $i"

    else
        echo "${i} is not a file"
    fi 
done

जवाबों:


4

यदि मैं समझता हूं कि आप क्या कर रहे हैं ( .oldप्रत्यय के साथ किसी भी फाइल को हटा दें , और प्रत्यय के साथ किसी भी मौजूदा फ़ाइलों की एक प्रति बनाएं .old), तो आप इसके बजाय इसका उपयोग कर सकते हैं:

#!/bin/sh

find . -maxdepth 1 -name \*.old -type f -printf "deleting %P\n" -delete
find . -maxdepth 1 -type f -printf "copying %P to %P.old\n" -exec cp '{}' '{}.old' \;

-maxdepth 0उपनिर्देशिकाओं में ढूंढने वाले कमांड को रोकता है, -type fकेवल नियमित फ़ाइलों की तलाश करता है; -printfसंदेश बनाता है ( %Pफ़ाइल नाम मिला है)। -exec cpप्रतिलिपि फ़ंक्शन को कॉल और '{}'फ़ाइल नाम है


14

आपकी स्क्रिप्ट में विभिन्न संभावित विफलता बिंदु हैं। सबसे पहले, सभी मिलान फ़ाइलों की एक सूची बनाने के rm *.old*लिए ग्लोबिंग का उपयोग करेंगे , और जो व्हॉट्सएप युक्त फ़ाइल नामों से निपट सकते हैं। हालाँकि, आपकी स्क्रिप्ट ग्लोब के प्रत्येक परिणाम के लिए एक वैरिएबल प्रदान करती है और वह भी बिना उद्धरण के। अगर आपकी फ़ाइल के नाम में व्हॉट्सएप है तो वह टूट जाएगा। उदाहरण के लिए:

$ ls
'file name with spaces.old.txt'  file.old.txt
$ rm *.old.*   ## works: both files are deleted

$ touch "file.old.txt" "file name with spaces.old.txt"
$ for i in ./*; do oldfile=$i; rm -v $oldfile; done
rm: cannot remove './file': No such file or directory
rm: cannot remove 'name': No such file or directory
rm: cannot remove 'with': No such file or directory
rm: cannot remove 'spaces.old.txt': No such file or directory
removed './file.old.txt'

जैसा कि आप देख सकते हैं, लूप अपने नाम के रिक्त स्थान वाली फ़ाइल के लिए विफल हो गया। इसे सही ढंग से करने के लिए, आपको चर को उद्धृत करना होगा:

$ for i in ./*; do oldfile="$i"; rm -v "$oldfile"; done
removed './file name with spaces.old.txt'
removed './file.old.txt'

$iआपकी स्क्रिप्ट के हर उपयोग पर एक ही समस्या लागू होती है । आपको हमेशा अपने चर को उद्धृत करना चाहिए ।

अगला संभावित मुद्दा यह है कि आप उम्मीद करते हैं कि *.old.*एक्सटेंशन के साथ फाइल मेल खाती है .old। यह नहीं है यह "0 या अधिक वर्ण" ( *), फिर a ., फिर "पुराना", फिर दूसरा .और फिर "0 या अधिक वर्ण" से मेल खाता है । इसका मतलब यह है कि यह किसी चीज़ से मेल नहीं खाएगा file.old, लेकिन केवल `file.old.foo की तरह कुछ:

$ ls
file.old  file.old.foo
$ for i in *; do if [[ "$i" == *.old.* ]]; then echo $i; fi; done
file.old.foo     

तो, कोई मुकाबला नहीं दुश्मन file.old। किसी भी मामले में, आपकी स्क्रिप्ट ज़रूरत से ज़्यादा जटिल है। इसके बजाय यह प्रयास करें:

#!/bin/bash

for i in *; do
    if [[ -f "$i" ]];  then
        if [[ "$i"  ==  *.old ]]; then
            rm -v "$i" || echo "rm failed for $i"
        else
            echo "$i doesn't have an .old extension"
        fi
        cp -v "$i" "$i".old
    else
        echo "$i is not a file"
    fi 
done

ध्यान दें कि मैंने cp echo` स्टेटमेंट्स में जोड़ा -vहै ।rmwhich does the same thing as what you were doing with your

यह तब से परिपूर्ण नहीं है जब आप पाते हैं, उदाहरण के लिए, file.oldजिसे हटा दिया जाएगा और, बाद में, स्क्रिप्ट इसे कॉपी करने की कोशिश करेगी और विफल हो जाएगी क्योंकि फ़ाइल अब मौजूद नहीं है। हालाँकि, आपने यह नहीं बताया कि आप वास्तव में क्या स्क्रिप्ट का प्रयास कर रहे हैं, इसलिए मैं आपके लिए यह तय नहीं कर सकता कि जब तक आप हमें यह न बताएं कि आप वास्तव में क्या हासिल करना चाहते हैं।

यदि आप चाहते हैं कि मैं) .oldएक्सटेंशन वाली सभी फ़ाइलों को हटा दूं और ii) .oldकिसी भी मौजूदा फाइल में एक्सटेंशन जोड़ें, जो आपके पास नहीं है, तो आपको वास्तव में इसकी आवश्यकता है:

#!/bin/bash

for i in *.old; do
    if [[ -f "$i" ]]; then
        rm -v "$i" || echo "rm failed for $i"
    else
        echo "$i is not a file"
    fi 
done
## All the ,old files have been removed at this point
## copy the rest
for i in *; do
    if [[ -f "$i" ]]; then
        ## the -v makes cp report copied files
        cp -v "$i" "$i".old
    fi
done

मैं file.old में फ़ाइलों का बैकअप लेने की कोशिश कर रहा हूं, लेकिन साथ ही साथ .old.old.old.old.old.old या .old.old.old आदि में समाप्त होने वाली किसी भी फ़ाइल को कमांडलाइन पर उपयोग rm *.old.*करता हूं जो इन फ़ाइलों को हटा देती है। लेकिन file.old बैकअप फ़ाइल नहीं। मैं अपनी स्क्रिप्ट में ऐसा करने की कोशिश कर रहा हूं। धन्यवाद
डॉन

1
@ अपने सवाल को संपादित करें और इसे और अधिक विस्तार से बताएं। उदाहरण फ़ाइल नाम शामिल करें और आपकी स्क्रिप्ट चलने के बाद आप उनके साथ क्या करना चाहते हैं। आदर्श रूप में, चैट में आओ और मुझे वहां पिंग करो ताकि हम इस पर चर्चा कर सकें।
टेराडन

8

केवल मामलों rm $oldfileविफल हो सकता है जब आपके फ़ाइल नाम के किसी भी वर्ण हैं IFS(अंतरिक्ष, टैब, न्यू लाइन) या किसी ग्लोब चरित्र ( *, ?, [])।

यदि कोई वर्ण IFSमौजूद है तो शेल शब्द विभाजन का कार्य करेगा और चर विस्तार पर ग्लोबबिंग वर्ण पथनाम विस्तार की उपस्थिति के आधार पर करेगा।

इसलिए, उदाहरण के लिए, यदि फ़ाइल का नाम है foo bar.old., तो चर oldfileहोगा foo bar.old.

जब तुम करोगे:

rm $oldfile

खोल पहले oldfileअंतरिक्ष में दो शब्दों में अंतरिक्ष के विस्तार को विभाजित करता है , fooऔर bar.old.। तो कमांड बन जाता है:

rm foo bar.old.

जो स्पष्ट रूप से अप्रत्याशित परिणाम की ओर ले जाएगा। वैसे, अगर आप किसी भी ग्लोबिंग ऑपरेटरों (अगर *, ?, []विस्तार में), तो पथ विस्तार भी किए जाएंगे।

वांछित परिणाम प्राप्त करने के लिए आपको चर को उद्धृत करने की आवश्यकता है:

rm "$oldfile"

अब, कोई शब्द विभाजन या पथनाम विस्तार नहीं किया जाएगा, इसलिए आपको वांछित परिणाम प्राप्त करना चाहिए अर्थात वांछित फ़ाइल निकाल दी जाएगी। यदि कोई फ़ाइल नाम के साथ शुरू होता है -, तो करें:

rm -- "$oldfile"

आप पूछ सकते हैं, क्यों हमें अंदर इस्तेमाल होने पर चर को उद्धृत करने की आवश्यकता नहीं है [[, इसका कारण [[एक bashकीवर्ड है और यह विस्तार विस्तार शाब्दिक रूप से आंतरिक रूप से चर विस्तार को संभालता है।


अब, कुछ बिंदु:

  • आपको आदेश exec 2>errorfileसे पहले STDERR ( ) को पुनर्निर्देशित करना चाहिए rmअन्यथा [[ -s errorfile ]]परीक्षण झूठी सकारात्मकता देगा

  • आपने उपयोग किया है [ -s $errorfile ], आप एक चर विस्तार का उपयोग कर रहे हैं $errorfile, जो होगा NUL दिया गया errorfileचर कहीं भी परिभाषित नहीं है। शायद आप मतलब है, बस [ -s errorfile ], STDERR पुनर्निर्देशन पर आधारित है

  • यदि चर errorfileको परिभाषित किया जाता है, तो उपयोग करते समय [ -s $errorfile ], यह उपरोक्त मामलों IFSऔर ग्लोबिंग पर फिर से चोक होगा क्योंकि इसके विपरीत [[, [आंतरिक रूप से नियंत्रित नहीं होता हैbash

  • स्क्रिप्ट के बाद के भाग में, आप cpपहले से हटाए गए फ़ाइल (फिर से चर को उद्धृत किए बिना) की कोशिश कर रहे हैं , इसका कोई मतलब नहीं है, आपको उस चक को जांचना चाहिए और अपने लक्ष्य के आधार पर आवश्यक सुधार करना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.