इसे पूरा करने के लिए कई व्यावहारिक तरीके हैं।
यदि आप अपने मूल संस्करण से निकटता से चिपकना चाहते हैं तो यह इस प्रकार किया जा सकता है:
getlist() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: %s\n' "$file"
done
}
यह तब भी विफल हो जाएगा जब फ़ाइल नाम में शाब्दिक नई सुर्खियाँ हों, लेकिन रिक्त स्थान इसे नहीं तोड़ेंगे।
हालाँकि, IFS के साथ खिलवाड़ करना आवश्यक नहीं है। यहाँ ऐसा करने का मेरा पसंदीदा तरीका है:
getlist() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
यदि आपको < <(command)
सिंटैक्स अपरिचित लगता है तो आपको प्रक्रिया प्रतिस्थापन के बारे में पढ़ना चाहिए । इस ओवर for file in $(find ...)
का लाभ यह है कि रिक्त स्थान, newlines और अन्य वर्णों वाली फ़ाइलों को सही ढंग से संभाला जाता है। यह काम करता है क्योंकि find
के साथ -print0
एक का उपयोग करेगा null
(उर्फ \0
न्यू लाइन के विपरीत प्रत्येक फ़ाइल नाम के लिए टर्मिनेटर के रूप में) और,, अशक्त एक फ़ाइल नाम में एक कानूनी चरित्र नहीं है।
लगभग-बराबर संस्करण पर इसका लाभ
getlist() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done
}
क्या लूप के शरीर में कोई भी चर असाइनमेंट संरक्षित है। यही है, यदि आप while
ऊपर के रूप में पाइप करते हैं, तो शरीर while
एक उप-भाग में है जो वह नहीं है जो आप चाहते हैं।
प्रक्रिया प्रतिस्थापन संस्करण का लाभ find ... -print0 | xargs -0
कम से कम है: xargs
संस्करण ठीक है अगर आपको ज़रूरत है तो बस एक लाइन प्रिंट करना है या फ़ाइल पर एक ही ऑपरेशन करना है, लेकिन अगर आपको कई चरणों को निष्पादित करने की आवश्यकता है तो लूप संस्करण आसान है।
संपादित करें : यहां एक अच्छी परीक्षण स्क्रिप्ट है ताकि आप इस समस्या को हल करने के विभिन्न प्रयासों के बीच अंतर का अंदाजा लगा सकें
#!/usr/bin/env bash
dir=/tmp/getlist.test/
mkdir -p "$dir"
cd "$dir"
touch 'file not starting foo' foo foobar barfoo 'foo with spaces'\
'foo with'$'\n'newline 'foo with trailing whitespace '
# while with process substitution, null terminated, empty IFS
getlist0() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
# while with process substitution, null terminated, default IFS
getlist1() {
while read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
# pipe to while, newline terminated
getlist2() {
find . -iname 'foo*' | while read -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# pipe to while, null terminated
getlist3() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# for loop over subshell results, newline terminated, default IFS
getlist4() {
for file in "$(find . -iname 'foo*')" ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# for loop over subshell results, newline terminated, newline IFS
getlist5() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# see how they run
for n in {0..5} ; do
printf '\n\ngetlist%d:\n' $n
eval getlist$n
done
rm -rf "$dir"