लूप के लिए रिक्त स्थान के साथ फाइलनाम, कमांड ढूंढें


34

मेरे पास एक स्क्रिप्ट है जो कई सबफ़ोल्डर्स और अभिलेखागार में सभी फ़ाइलों को टार में खोजती है। मेरी स्क्रिप्ट है

for FILE in `find . -type f  -name '*.*'`
  do
if [[ ! -f archive.tar ]]; then

  tar -cpf archive.tar $FILE
else 
  tar -upf archive.tar $FILE 
fi
done

खोज आदेश मुझे निम्न आउटपुट देता है

find . -type f  -iname '*.*'
./F1/F1-2013-03-19 160413.csv
./F1/F1-2013-03-19 164411.csv
./F1-FAILED/F2/F1-2013-03-19 154412.csv
./F1-FAILED/F3/F1-2011-10-02 212910.csv
./F1-ARCHIVE/F1-2012-06-30 004408.csv
./F1-ARCHIVE/F1-2012-05-08 190408.csv

लेकिन फ़ाइल चर केवल पथ के पहले भाग को संग्रहीत करता है ।/F1/F1-2013-03-19 और फिर अगला भाग 160413.csv

मैंने readथोड़ी देर के लूप के साथ प्रयोग करने की कोशिश की ,

while read `find . -type f  -iname '*.*'`;   do ls $REPLY; done

लेकिन मुझे निम्नलिखित त्रुटि मिलती है

bash: read: `./F1/F1-2013-03-19': not a valid identifier

क्या कोई वैकल्पिक तरीका सुझा सकता है?

अद्यतन करें

जैसा कि नीचे दिए गए उत्तरों में सुझाया गया है मैंने स्क्रिप्ट्स को अपडेट किया है

#!/bin/bash

INPUT_DIR=/usr/local/F1
cd $INPUT_DIR
for FILE in "$(find  . -type f -iname '*.*')"
do
archive=archive.tar

        if [ -f $archive ]; then
        tar uvf $archive "$FILE"
        else
        tar -cvf $archive "$FILE"
        fi
done

मुझे जो आउटपुट मिलता है

./test.sh
tar: ./F1/F1-2013-03-19 160413.csv\n./F1/F1-2013-03-19 164411.csv\n./F1/F1-2013-03-19 153413.csv\n./F1/F1-2013-03-19 154412.csv\n./F1/F1-2012-09-10 113409.csv\n./F1/F1-2013-03-19 152411.csv\n./.tar\n./F1-FAILED/F3/F1-2013-03-19 154412.csv\n./F1-FAILED/F3/F1-2013-03-19 170411.csv\n./F1-FAILED/F3/F1-2012-09-10 113409.csv\n./F1-FAILED/F2/F1-2011-10-03 113911.csv\n./F1-FAILED/F2/F1-2011-10-02 165908.csv\n./F1-FAILED/F2/F1-2011-10-02 212910.csv\n./F1-ARCHIVE/F1-2012-06-30 004408.csv\n./F1-ARCHIVE/F1-2011-08-17 133905.csv\n./F1-ARCHIVE/F1-2012-10-21 154410.csv\n./F1-ARCHIVE/F1-2012-05-08 190408.csv: Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors

4
ऐसा लगता है कि आपको IFS=$'\n'प्रत्येक लाइन द्वारा पार्स बनाने के लिए `लूप से पहले ' सेट करना चाहिए
kiri

जवाबों:


36

के forसाथ प्रयोग करना findयहाँ गलत दृष्टिकोण है, उदाहरण के लिए इस राइटअप को आप जो कीड़े खोल रहे हैं उसके बारे में देखें।

अनुशंसित दृष्टिकोण का उपयोग करना है find, whileऔर readजैसा कि यहां वर्णित है । नीचे एक उदाहरण है जो आपके लिए काम करना चाहिए:

find . -type f -name '*.*' -print0 | 
while IFS= read -r -d '' file; do
    printf '%s\n' "$file"
done

इस तरह से आप नल ( \0) वर्णों के साथ फ़ाइल नाम को हटाते हैं , इसका मतलब है कि अंतरिक्ष में बदलाव और अन्य विशेष वर्ण समस्याओं का कारण नहीं बनेंगे।

उन फ़ाइलों के साथ एक संग्रह को अपडेट करने के लिए find, जो आप इसके आउटपुट को सीधे पास कर सकते हैं tar:

find . -type f -name '*.*' -printf '%p\0' | 
tar --null -uf archive.tar -T -

ध्यान दें कि अगर संग्रह मौजूद है या नहीं, तो आपको अंतर करने की आवश्यकता नहीं है, tarइसे समझदारी से संभाल लेंगे। संग्रह में बिट -printfसहित से बचने के लिए यहां के उपयोग पर भी ध्यान दें ./


धन्यवाद, यह लगभग काम करता है। केवल एक चीज इसके ./टार के रूप में संग्रहित है । ./.tar tar: ./archive.tar: file is the archive; not dumped
उबंटुसर

@Ubuntuser आप देखने के लिए एक साधारण चेक जोड़ सकते हैंif [[ "$FILE" == "./" ]]; then continue
kiri

@Ubuntuser: आप अपडेट किए गए उत्तर ./को -printfदेखने से थोड़ा बच सकते हैं। हालाँकि इसमें कोई अंतर नहीं होना चाहिए अगर यह शामिल है या नहीं क्योंकि यह केवल वर्तमान निर्देशिका को संदर्भित करता है। मैंने एक वैकल्पिक find/tarसंयोजन भी शामिल किया जिसे आप उपयोग करना चाहते हैं।
थोर

उन लोगों के लिए जिन्हें sortआप इसे पुनरावृत्त करने से पहले परिणाम की इच्छा रखते हैं , आपको sort -zअशक्त विभाजक की आवश्यकता होगी ।
Adambean

13

forलूप को इस तरह उद्धृत करने का प्रयास करें:

for FILE in "`find . -type f  -name '*.*'`"   # note the quotation marks

उद्धरण के बिना, बश रिक्त स्थान और newlines ( \n) अच्छी तरह से संभाल नहीं करता है ...

सेटिंग का भी प्रयास करें

IFS=$'\n'

1
$ IFS के लिए +1। यह विभाजक चरित्र को दर्शाता है।
रे

1
यह वह उपाय है जो मेरे लिए काम करता है। मैं commसॉर्ट की गई फ़ाइल लिस्टिंग की तुलना करने के लिए उपयोग कर रहा था और फ़ाइल नाम में उनके स्थान थे, चर को उद्धृत करने के बावजूद यह काम नहीं कर रहा था। तब मैंने cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html और IFS = $ के साथ $ IFS सेट करने के समाधान को देखा (इको-एन "\ n \ b" मेरे लिए काम किया।
pbhj

डबल उद्धरण, सुरुचिपूर्ण, सरल, सुंदर का धन्यवाद - धन्यवाद!
बिग रिच

8

यह काम करता है और सरल है:

find . -name '<pattern>' | while read LINE; do echo "$LINE" ; done

इस जवाब के लिए रूपा ( https://github.com/rupa/z ) को क्रेडिट ।


4

उचित उद्धरण के अलावा, आप findएक NULL विभाजक का उपयोग करने के लिए कह सकते हैं , और फिर परिणाम को whileलूप में पढ़ सकते हैं और संसाधित कर सकते हैं

while read -rd $'\0' file; do
    something with "$file"
done < <(find  . -type f -name '*.*' -print0)

यह किसी भी फ़ाइल नाम को संभालना चाहिए जो POSIX- अनुरूप है - देखें man find

   -print0
          True; print the full file name on the standard output, followed by a null character (instead of the newline character that  -print  uses).   This  allows  file
          names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output.  This option corresponds to the
          -0 option of xargs.

यह केवल समाधान है कि मेरे लिए काम किया है। धन्यवाद।
कोडफ्रीक


1

मैंने कुछ ऐसा किया कि ऐसी फाइलें मिलें जिनमें स्पेस हो।

IFS=$'\n'
for FILE in `/usr/bin/find $DST/shared -name *.nsf | grep -v bookmark.nsf | grep -v names.nsf`; do
    file $FILE | tee -a $LOG
done

एक जादू की तरह काम किया :)


0

यहाँ ज्यादातर जवाब टूट जाता है अगर फ़ाइल नाम में कोई नया अक्षर है। मैं बैश का उपयोग अधिक 15 साल करता हूं, लेकिन केवल इंटरैक्टिव।

अजगर में आप हमें os.walk (): http://docs.python.org/2/library/os.html#os.walk कर सकते हैं

और टार्फाइल मॉड्यूल: http://docs.python.org/2/library/tarfile.html#tar-exam


0

मुझे लगता है कि आप find's -exec विकल्प का उपयोग करके बेहतर हो सकते हैं ।

find . -type f -name '*.*' -exec tar -cpf archive.tar {} +

इसके बाद सिस्टम कॉल का उपयोग करके कमांड को खोजें, ताकि स्पेस और न्यूलाइन्स को संरक्षित किया जाए (बल्कि एक पाइप, जिसे विशेष वर्णों के उद्धरण की आवश्यकता होगी)। ध्यान दें कि "टार्क-सी" काम करता है कि क्या संग्रह पहले से मौजूद है या नहीं, और वह (कम से कम बैश के साथ) न तो {} और न ही + उद्धृत किए जाने की आवश्यकता है।


-1

जैसा कि minerz029 ने सुझाव दिया था, आपको findकमांड के विस्तार को उद्धृत करने की आवश्यकता है । आपको $FILEअपने लूप की सभी बारीकियों को भी उद्धृत करना होगा ।

for FILE in "$(find . -type f  -name '*.*')"
do
    if [ ! -f archive.tar ]; then
        tar -cpf archive.tar "$FILE"
    else 
        tar -upf archive.tar "$FILE" 
    fi
done

ध्यान दें कि $()सिंटैक्स को बैकटिक्स के उपयोग के लिए प्राथमिकता दी जानी चाहिए; देखना यह यू एंड एल सवाल । मैंने [[कीवर्ड भी हटा दिया और इसे [कमांड द्वारा बदल दिया क्योंकि यह POSIX है।


के बारे में [[और [ऐसा लगता है कि [[ग्लोबिंग और रेगुलर एक्सप्रेशन मिलान की तरह नए और समर्थन करता है और अधिक सुविधाओं है। [[केवल में है bash, हालांकि, नहींsh
किरी

@ minerz029 हाँ। मैं भी येही कह रहा हूँ। मुझे नहीं पता है कि [[ग्लोबिंग का समर्थन करने से आपका क्या मतलब है । ग्रेग की विकी के अनुसार , कोई ग्लोबिंग अंदर नहीं होता है [[
जोसेफ आर।

[ "ab" == a? ] && echo "true"फिर कोशिश करें[[ "ab" == a? ]] && echo "true"
किरी

@ minerz029 यह ग्लोबिंग नहीं है। ये नियमित अभिव्यक्तियाँ हैं (शिथिल व्याख्या)। यह एक ग्लोब नहीं है क्योंकि a*इसका अर्थ है "सभी फ़ाइलों के बजाय" aवर्णों की संख्या के बाद " जिसका नाम शुरू होता है और बाद में वर्णों की संख्या होती है"। कोशिश [ ab = a* ] && echo true बनाम [[ ab == a* ]] && echo true
जोसेफ़ आर।

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