समस्या
for f in $(find .)
दो असंगत चीजों को जोड़ती है।
findन्यूलाइन वर्णों द्वारा सीमांकित फ़ाइल पथों की सूची प्रिंट करता है। जबकि स्प्लिट + ग्लोब ऑपरेटर को जब आप छोड़ते हैं $(find .), तो उस सूची के संदर्भ में यह अछूता रहता है कि यह इसके वर्णों पर विभाजित होता है $IFS(डिफ़ॉल्ट रूप से इसमें नईलाइन, लेकिन स्पेस और टैब (और एनयूएल इन zsh) भी शामिल है और प्रत्येक शब्द पर ग्लोबिंग करता है (सिवाय में zsh) (और यहां तक कि ksh93 या pdksh डेरिवेटिव में ब्रेस विस्तार!)।
भले ही आप इसे बनाते हैं:
IFS='
' # split on newline only
set -o noglob # disable glob (also disables brace expansion in pdksh
# but not ksh93)
for f in $(find .) # invoke split+glob
यह अभी भी गलत है क्योंकि न्यूलाइन वर्ण फ़ाइल पथ में किसी भी मान्य है। इसका आउटपुट find -printकेवल पोस्ट-प्रोसेस करने योग्य मज़बूती से नहीं है (कुछ जटिल चाल का उपयोग छोड़कर, जैसा कि यहां दिखाया गया है )।
इसका मतलब यह भी है कि शेल को findपूरी तरह से आउटपुट को स्टोर करने की आवश्यकता है , और फिर फ़ाइलों पर लूप शुरू करने से पहले इसे (जो कि आउटपुट को दूसरी बार मेमोरी में स्टोर करने का मतलब है) ग्लोब को विभाजित करें।
ध्यान दें कि find . | xargs cmdऐसी ही समस्याएं हैं (वहाँ, ब्लैंक, न्यूलाइन, सिंगल कोट, डबल कोट और बैकस्लैश (और कुछ xargकार्यान्वयन के साथ बाइट्स वैध वर्णों का हिस्सा नहीं हैं) एक समस्या है)
अधिक सही विकल्प
समर्थन और उपयोग करने के लिए forआउटपुट पर एक लूप का उपयोग करने का एकमात्र तरीका findहोगा :zshIFS=$'\0'
IFS=$'\0'
for f in $(find . -print0)
(की जगह -print0के साथ -exec printf '%s\0' {} +के लिए findकार्यान्वयन कि अमानक (लेकिन बहुत आम आजकल) का समर्थन नहीं करते -print0)।
यहाँ, सही और पोर्टेबल तरीका उपयोग करना है -exec:
find . -exec something with {} \;
या यदि somethingएक से अधिक तर्क ले सकते हैं:
find . -exec something with {} +
यदि आपको शेल द्वारा नियंत्रित की जाने वाली फ़ाइलों की सूची की आवश्यकता है:
find . -exec sh -c '
for file do
something < "$file"
done' find-sh {} +
(खबरदार यह एक से अधिक शुरू हो सकता है sh)।
कुछ प्रणालियों पर, आप उपयोग कर सकते हैं:
find . -print0 | xargs -r0 something with
कि मानक वाक्य रचना पर थोड़ा लाभ दिया है और इसका मतलब है, हालांकि somethingकी stdinया तो पाइप या है /dev/null।
एक कारण आप का उपयोग करना चाहते हो सकता है कि समानांतर प्रसंस्करण के लिए -PGNU के विकल्प का उपयोग किया जा सकता है xargs। stdinमुद्दा भी जीएनयू के साथ चारों ओर काम किया जा सकता है xargsके साथ -aप्रक्रिया प्रतिस्थापन समर्थन के गोले के साथ विकल्प:
xargs -r0n 20 -P 4 -a <(find . -print0) something
उदाहरण के लिए, something20 फ़ाइल तर्क लेने वाले प्रत्येक के 4 समवर्ती चालान तक चलाने के लिए ।
साथ zshया bash, के उत्पादन से अधिक पाश करने के लिए एक और तरीका है find -print0के साथ है:
while IFS= read -rd '' file <&3; do
something "$file" 3<&-
done 3< <(find . -print0)
read -d '' न्यूलाइन सीमांकित के बजाय एनयूएल सीमांकित रिकॉर्ड पढ़ता है।
bash-4.4और ऊपर के find -print0साथ एक सरणी में लौटी फ़ाइलों को भी स्टोर कर सकते हैं :
readarray -td '' files < <(find . -print0)
zshबराबर (जो संरक्षण का लाभ दिया है findके बाहर निकलने की स्थिति):
files=(${(0)"$(find . -print0)"})
साथ zsh, आप findग्लोब क्वालिफायर के साथ पुनरावर्ती ग्लोबबिंग के संयोजन में अधिकांश अभिव्यक्तियों का अनुवाद कर सकते हैं । उदाहरण के लिए, लूपिंग ओवर find . -name '*.txt' -type f -mtime -1:
for file (./**/*.txt(ND.m-1)) cmd $file
या
for file (**/*.txt(ND.m-1)) cmd -- $file
(की जरूरत के --साथ सावधान रहना **/*, फ़ाइल पथ के साथ शुरू नहीं कर रहे हैं ./, इसलिए -उदाहरण के लिए शुरू हो सकता है )।
ksh93और bashअंत **/में पुनरावर्ती ग्लोबबिंग के अधिक अग्रिम रूपों के लिए समर्थन नहीं जोड़ा गया है , लेकिन अभी भी ग्लोब क्वालिफायर नहीं है जो **वहां बहुत सीमित उपयोग करता है । यह भी सावधान रहें कि bashनिर्देशिका वृक्ष के नीचे उतरते समय 4.3 से पहले सहजीवन का पालन करें।
लूपिंग ओवर की तरह $(find .), इसका मतलब है कि मेमोरी 1 में फ़ाइलों की पूरी सूची को संग्रहीत करना । यह वांछनीय हो सकता है, हालांकि कुछ मामलों में जब आप फाइलों पर अपने कार्यों को फाइलों की खोज पर प्रभाव नहीं डालना चाहते हैं (जैसे जब आप अधिक फाइलें जोड़ते हैं जो अंत में खुद को पाया जा सकता है)।
अन्य विश्वसनीयता / सुरक्षा विचार
दौर कि शर्ते
अब, अगर हम विश्वसनीयता की बात कर रहे हैं, तो हमें समय के बीच की दौड़ की शर्तों का उल्लेख करना होगा find/ zshएक फ़ाइल ढूंढनी होगी और जांच करनी होगी कि यह मानदंडों को पूरा करती है और समय का उपयोग किया जा रहा है ( TOCTOU race )।
यहां तक कि एक निर्देशिका पेड़ के नीचे उतरते समय, किसी को सहानुभूति का पालन न करने और TOCTOU दौड़ के बिना ऐसा करने के लिए सुनिश्चित करना होगा। find(GNU findकम से कम) यह करता है कि openat()सही O_NOFOLLOWझंडे (जहाँ समर्थित हो) का उपयोग करके निर्देशिकाओं को खोलकर और प्रत्येक निर्देशिका के लिए एक फाइल डिस्क्रिप्टर को खुला रखें, zsh/ bash/ kshऐसा न करें। इसलिए एक हमलावर के सामने एक निर्देशिका को सही समय पर सिम्लिंक के साथ बदलने में सक्षम होने के कारण, आप गलत निर्देशिका को अवरूद्ध कर सकते हैं।
भले ही findनिर्देशिका को ठीक से, साथ -exec cmd {} \;और इससे भी अधिक -exec cmd {} +, एक बार cmdनिष्पादित किया गया हो, उदाहरण के लिए, cmd ./foo/barया cmd ./foo/bar ./foo/bar/baz, जैसे ही समय का cmdउपयोग करता है, नीचे उतरता है ./foo/bar, barहो सकता है कि विशेषताएँ अब मापदंड से मेल नहीं खातीं find, लेकिन इससे भी बदतर ./fooहो सकती हैं। कुछ अन्य जगह पर एक सिमलिंक द्वारा प्रतिस्थापित (और दौड़ खिड़की बहुत से बड़ा किया जाता है -exec {} +, जहां findफोन करने के लिए पर्याप्त फ़ाइलों के लिए इंतजार कर रहा है cmd)।
कुछ findकार्यान्वयन में एक (गैर-मानक अभी तक) -execdirदूसरी समस्या को कम करने के लिए विधेय है।
साथ में:
find . -execdir cmd -- {} \;
find chdir()चलाने से पहले फ़ाइल के मूल निर्देशिका में है cmd। cmd -- ./foo/barकॉल करने के बजाय , यह कॉल करता है cmd -- ./bar( cmd -- barकुछ कार्यान्वयनों के साथ, इसलिए --), इसलिए ./fooसिम्लिंक में परिवर्तित होने की समस्या से बचा जाता है। यह rmसुरक्षित जैसे आदेशों का उपयोग करता है (यह अभी भी एक अलग फ़ाइल को हटा सकता है, लेकिन एक अलग निर्देशिका में एक फ़ाइल नहीं है), लेकिन उन आदेशों को नहीं जो फाइलों को संशोधित कर सकते हैं जब तक कि उन्हें सिमिलिंक का पालन न करने के लिए डिज़ाइन नहीं किया गया हो।
-execdir cmd -- {} +कभी-कभी यह भी काम करता है लेकिन GNU के कुछ संस्करणों सहित कई कार्यान्वयन के साथ find, यह इसके बराबर है -execdir cmd -- {} \;।
-execdir बहुत गहरी निर्देशिका पेड़ों से जुड़ी कुछ समस्याओं के आसपास काम करने का लाभ भी है।
में:
find . -exec cmd {} \;
दिए गए पथ का आकार cmd, फ़ाइल की निर्देशिका की गहराई के साथ बढ़ेगा। यदि वह आकार PATH_MAX(लिनक्स पर 4k की तरह कुछ) से बड़ा हो जाता है , तो cmdउस पथ पर जो भी सिस्टम कॉल करता है वह एक ENAMETOOLONGत्रुटि के साथ विफल हो जाएगा ।
के साथ -execdir, केवल फ़ाइल नाम (संभवतः के साथ उपसर्ग किया जाता है ./) को पास किया जाता है cmd। अधिकांश फ़ाइल सिस्टम पर फ़ाइल नाम की NAME_MAXतुलना में बहुत कम सीमा ( ) है PATH_MAX, इसलिए ENAMETOOLONGत्रुटि का सामना करने की संभावना कम है।
बाइट्स बनाम वर्ण
इसके अलावा, अक्सर सुरक्षा के बारे में विचार करते समय अनदेखी की जाती है findऔर आम तौर पर सामान्य रूप से फ़ाइल नामों को संभालने के साथ और अधिक तथ्य यह है कि अधिकांश यूनिक्स जैसी प्रणालियों पर, फ़ाइल नाम बाइट्स के अनुक्रम (किसी भी बाइट मान लेकिन एक फ़ाइल पथ में 0, और अधिकांश सिस्टम पर हैं) ASCII आधारित वाले, हम अब के लिए दुर्लभ EBCDIC आधारित लोगों की उपेक्षा करेंगे) 0x2f पथ सीमांकक है)।
यह तय करना अनुप्रयोगों पर निर्भर है कि क्या वे उन बाइट्स को पाठ के रूप में मानना चाहते हैं। और वे आम तौर पर करते हैं, लेकिन आम तौर पर बाइट्स से पात्रों तक का अनुवाद उपयोगकर्ता के स्थान पर, पर्यावरण के आधार पर किया जाता है।
इसका मतलब यह है कि किसी दिए गए फ़ाइल नाम में स्थान के आधार पर भिन्न पाठ प्रतिनिधित्व हो सकता है। उदाहरण के लिए, बाइट अनुक्रम एक फ़ाइल के नाम के लिए उस स्थान में एक फ़ाइल नाम की व्याख्या करने के लिए 63 f4 74 e9 2e 74 78 74होगा côté.txtजहां चरित्र सेट ISO-8859-1 है, और cєtщ.txtएक लोकल में जहां charset IS0-8859-5 है।
और भी बुरा। एक लोकल में, जहाँ charset UTF-8 (आजकल का मानक) है, 63 f4 74 e9 2e 74 78 74 बस अक्षरों से मैप नहीं किया जा सकता है!
findऐसा एक अनुप्रयोग है जो फ़ाइल नामों को अपने -name/ -pathविधेय के लिए पाठ के रूप में मानता है (और अधिक, जैसे -inameया -regexकुछ कार्यान्वयन के साथ)।
इसका मतलब यह है कि उदाहरण के लिए, कई findकार्यान्वयन के साथ (GNU सहित find)।
find . -name '*.txt'
63 f4 74 e9 2e 74 78 74जब हमारी UTF-8 लोकेल में कॉल की गई *(जो 0 या अधिक वर्णों से मेल खाती है , बाइट्स से नहीं) तो उन गैर-वर्णों से मेल नहीं खा सकता है जब हमारी फ़ाइल ऊपर नहीं मिलेगी ।
LC_ALL=C find... समस्या के चारों ओर काम करेगा क्योंकि सी लोकेल प्रति चरित्र एक बाइट का अर्थ है और (आमतौर पर) गारंटी देता है कि सभी बाइट मान एक चरित्र के लिए मैप करते हैं (कुछ बाइट मानों के लिए संभवतः अपरिभाषित)।
अब जब शेल से उन फ़ाइल नामों पर लूपिंग की बात आती है, तो वह बाइट बनाम चरित्र भी एक समस्या बन सकती है। हम आम तौर पर उस संबंध में 4 मुख्य प्रकार के गोले देखते हैं:
जो अभी भी बहु-बाइट की तरह जागरूक नहीं हैं dash। उनके लिए, एक चरित्र को बाइट मैप करता है। उदाहरण के लिए, UTF-8 में, côté4 वर्ण हैं, लेकिन 6 बाइट्स हैं। एक लोकल में जहां UTF-8 चारसेट है, में
find . -name '????' -exec dash -c '
name=${1##*/}; echo "${#name}"' sh {} \;
findउन फ़ाइलों को सफलतापूर्वक ढूँढेगा जिनके नाम में UTF-8 में एन्कोडेड 4 वर्ण हैं, लेकिन dash4 और 24 के बीच लंबाई की रिपोर्ट करेंगे।
yash: विलोम। यह केवल पात्रों से संबंधित है । सभी इनपुट को आंतरिक रूप से वर्णों में अनुवादित किया जाता है। यह सबसे सुसंगत शेल के लिए बनाता है, लेकिन इसका मतलब यह भी है कि यह मनमाने ढंग से बाइट अनुक्रमों के साथ सामना नहीं कर सकता है (जो मान्य वर्णों में अनुवाद नहीं करते हैं)। यहां तक कि सी लोकेल में, यह 0x7f से ऊपर के बाइट मूल्यों के साथ सामना नहीं कर सकता है।
find . -exec yash -c 'echo "$1"' sh {} \;
côté.txtउदाहरण के लिए, पहले से हमारे ISO-8859-1 पर UTF-8 लोकेल विफल हो जाएगा ।
बहु-बाइट समर्थन को पसंद किया गया है bashया zshजहां उन लोगों को उत्तरोत्तर जोड़ा गया है। वे उन बाइट्स पर विचार करने से पीछे हट जाएंगे जिन्हें पात्रों के लिए मैप नहीं किया जा सकता है जैसे कि वे वर्ण थे। उनके पास अभी भी यहाँ कुछ कीड़े हैं और विशेष रूप से कम आम मल्टी-बाइट चारसेट जैसे GBK या BIG5-HKSCS (जिनके काफी मल्टी-बाइट कैरेक्टर होने के कारण 0-127 रेंज में बाइट्स होते हैं (जैसे ASCII अक्षर) )।
जैसे उन shFreeBSD के (11 कम से कम) या mksh -o utf8-modeकि समर्थन बहु बाइट लेकिन केवल UTF-8 के लिए।
टिप्पणियाँ
1 पूर्णता के लिए, हम zshपूरी सूची में पूरी सूची को संग्रहीत किए बिना पुनरावर्ती ग्लोबिंग का उपयोग करके फ़ाइलों पर लूप का एक हैक करने का तरीका बता सकते हैं :
process() {
something with $REPLY
false
}
: **/*(ND.m-1+process)
+cmdएक ग्लोब क्वालीफ़ायर है जो cmdवर्तमान फ़ाइल पथ के साथ कॉल करता है (आमतौर पर एक फ़ंक्शन) $REPLY। यह तय करने के लिए फ़ंक्शन सही या गलत है कि क्या फ़ाइल का चयन किया जाना चाहिए (और $REPLYएक $replyसरणी में कई फ़ाइलों को संशोधित या वापस भी कर सकता है )। यहां हम उस फंक्शन में प्रोसेसिंग करते हैं और गलत रिटर्न करते हैं ताकि फाइल का चयन न हो।