एक निर्देशिका में फ़ाइलों के माध्यम से पुनरावृत्ति


15

एक निर्देशिका में फ़ाइलों के माध्यम से पुनरावृत्ति आसानी से किया जा सकता है:

find . -type f -exec bar {} \;

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

while read line; do [...]; done < <(find . -type f)

हालाँकि, ऐसा लगता है कि यह अस्पष्ट वर्ण वाली फ़ाइलों के लिए काम नहीं करता है:

$ touch $'a\nb'
$ find . -type f
./a?b

क्या कोई विकल्प है जो ऐसे अस्पष्ट पात्रों को अच्छी तरह से संभालता है?


1
find ... -exec bash -c 'echo filename is in \$0: "$0"' {} \;यह करने के लिए एक बेहतर तरीका है।
— jw013

आप इस का समाधान, और अपने मूल डिजाइन रखने के लिए, अपने को बदलने के द्वारा कर सकते हैं read lineकरने के लिए IFS= read -r line। एकमात्र चरित्र जो इसे तोड़ देगा, फिर एक नई रेखा है।
— पैट्रिक

1
@ पैट्रिक, लेकिन फ़ाइल नाम में नई सीमाएँ हो सकती हैं। इसलिए -d $'\0'बेहतर है।
— १

जवाबों:


7

सुरक्षित केfind लिए एक और उपयोग :

while IFS= read -r -d '' -u 9
do
    [Do something with "$REPLY"]
done 9< <( find . -type f -exec printf '%s\0' {} + )

(यह किसी भी POSIX के साथ काम करता है find, लेकिन शेल भाग को bash की आवश्यकता होती है। * BSD और GNU के साथ, आप -print0इसके बजाय उपयोग कर सकते हैं -exec printf '%s\0' {} +, यह थोड़ा तेज़ होगा।)

यह लूप के भीतर मानक इनपुट का उपयोग करना संभव बनाता है, और यह किसी भी पथ के साथ काम करता है ।


1
क्योंकि मुझे इसे देखना था: "पढ़िए ... यदि कोई नाम नहीं दिया गया है, तो पढ़ी गई रेखा चर चर को सौंपा गया है।" तोdo echo "Filename is '$REPLY'"
— एंड्रयू

9

यह करना उतना ही सरल है:

find -exec sh -c 'inline script "$0"' {} \;

या ...

find -exec executable_script {} \;

5

शेल ग्लोबिंग का उपयोग करने के लिए सबसे सरल (अभी तक सुरक्षित) तरीका है:

$ for f in *; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
h:

उप पुनर्निर्देशन को उपनिर्देशिका (बैश में) बनाने के लिए, आप globstarविकल्प का उपयोग कर सकते हैं ; dotglobउन फ़ाइलों से मिलान करने के लिए भी सेट करें जिनका नाम निम्नलिखित है .:

$ shopt -s globstar dotglob
$ for f in **/*; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
:foo:
:foo/file1:
:foo/file two:
h:

4.2 को **/कोसने के लिए सावधान रहें, निर्देशिकाओं के प्रतीकात्मक लिंक में पुनरावृत्ति करता है। 4.3 के बाद से, **/केवल निर्देशिका में पुनरावृत्ति करता है, जैसे find।

एक अन्य आम समाधान उपयोग करने के लिए है find -print0के साथ xargs -0:

$ touch -- 'a b' $'c\nd' $'e\tf' $'g\rh' '-e'
$ find . -type f -print0 | xargs -0 -I{} printf ":%s:\n" {}
h:/g
:./e    f:
:./a b:
:./-e:
:./c
d:

ध्यान दें कि h:/gफ़ाइल नाम में ए से वास्तव में सही है \r।


4

अपने रीड लूप को कुछ हद तक करना थोड़ा मुश्किल है, लेकिन विशेष रूप से आपको इस तरह से कुछ करने की कोशिश करनी चाहिए ।

प्रासंगिक भाग:

while IFS= read -d $'\0' -r file ; do
        printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)

यह findNUL वर्णों (0x00) द्वारा सीमांकित इसके उत्पादन को मुद्रित करने के लिए निर्देश देता है, और अन्य वर्णों के लिए भाग के रूप में बैकस्लैश को संभालने के बिना readएनयूएल-सीमांकित रेखाओं ( -d $'\0') को लाने के लिए ( -r) और लाइनों पर विभाजित किसी भी शब्द को न करें ( IFS=)। चूंकि 0x00 एक बाइट है जो यूनिक्स में फ़ाइलनाम या पथ में नहीं हो सकता है, यह आपकी सभी अजीब फ़ाइल समस्याओं को संभालना चाहिए।


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