जैसे लूप का उपयोग करना
for i in `find . -name \*.txt`
टूट जाएगा अगर कुछ फ़ाइल नाम में रिक्त स्थान है।
इस समस्या से बचने के लिए मैं किस तकनीक का उपयोग कर सकता हूं?
जैसे लूप का उपयोग करना
for i in `find . -name \*.txt`
टूट जाएगा अगर कुछ फ़ाइल नाम में रिक्त स्थान है।
इस समस्या से बचने के लिए मैं किस तकनीक का उपयोग कर सकता हूं?
जवाबों:
आदर्श रूप से आप इसे इस तरह से बिल्कुल नहीं करते हैं, क्योंकि शेल स्क्रिप्ट में ठीक से फ़ाइल नाम को पार्स करना हमेशा मुश्किल होता है (इसे रिक्त स्थान के लिए ठीक करें, आपको अभी भी अन्य एम्बेडेड वर्णों के साथ समस्याएँ होंगी, विशेष रूप से नईलाइन में)। यहां तक कि इसे बैशपिट्स पेज में पहली प्रविष्टि के रूप में सूचीबद्ध किया गया है ।
उस ने कहा, आप जो चाहते हैं वह लगभग करने का एक तरीका है:
oIFS=$IFS
IFS=$'\n'
find . -name '*.txt' | while read -r i; do
# use "$i" with whatever you're doing
done
IFS=$oIFS
$i
बाद में रिक्त स्थान की व्याख्या करने वाली अन्य चीजों से बचने के लिए, इसका उपयोग करते समय भी याद रखें । $IFS
इसका उपयोग करने के बाद वापस सेट करना भी याद रखें , क्योंकि ऐसा न करने पर बाद में मनमुटाव हो जाएगा।
इसमें एक अन्य कैविएट संलग्न है: while
लूप के अंदर जो कुछ होता है वह सब- उपले में हो सकता है, आपके द्वारा उपयोग किए जा रहे सटीक शेल के आधार पर, इसलिए वैरिएबल सेटिंग्स बनी नहीं रह सकती हैं। for
लूप संस्करण से बचा जाता है कि लेकिन कीमत पर कि, भले ही आप लागू $IFS
समाधान से बचने के मुद्दों के लिए रिक्त स्थान के साथ, आप मुसीबत अगर में मिल जाएगा find
रिटर्न बहुत सारी फ़ाइलें।
कुछ बिंदु पर इस सब के लिए सही फिक्स शेल के बजाय पर्ल या पायथन जैसी भाषा में कर रहा है।
इसका उपयोग करें find -print0
और इसे xargs -0
अपने छोटे C प्रोग्राम में लिखें या अपने छोटे C प्रोग्राम में इसे पाइप करें। यह वही है -print0
और -0
इसके लिए आविष्कार किया गया था।
शैल स्क्रिप्ट उन में रिक्त स्थान के साथ फ़ाइल नाम को संभालने का सबसे अच्छा तरीका नहीं है: आप इसे कर सकते हैं, लेकिन यह क्लंकी हो जाता है।
आप "आंतरिक क्षेत्र विभाजक" ( IFS
) लूप तर्क विभाजन के लिए अंतरिक्ष की तुलना में कुछ और सेट कर सकते हैं , जैसे
ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
IFS=${ORIGIFS}
#do stuff
done
IFS=${ORIGIFS}
मुझे IFS
लगता है कि इसके उपयोग के बाद रीसेट हो गया है, ज्यादातर क्योंकि यह अच्छा लग रहा है, मुझे लगता है। मैंने इसे नईलाइन पर सेट करने में कोई समस्या नहीं देखी है, लेकिन मुझे लगता है कि यह "क्लीनर" है।
एक अन्य विधि, इस बात पर निर्भर करती है कि आप आउटपुट से क्या करना चाहते हैं find
, यह या तो सीधे कमांड के -exec
साथ उपयोग करना है find
, या इसका उपयोग करना -print0
और इसमें पाइप करना है xargs -0
। पहले मामले में find
बच निकलने वाले फ़ाइल नाम का ध्यान रखता है। में -print0
मामला है, find
एक अशक्त विभाजक के साथ इसके उत्पादन प्रिंट, और फिर xargs
इस पर विभाजित होता है। चूँकि किसी फ़ाइल नाम में वह चरित्र नहीं हो सकता (जिसे मैं जानता हूँ), यह हमेशा की तरह सुरक्षित है। यह ज्यादातर साधारण मामलों में उपयोगी होता है; और आमतौर पर एक पूर्ण for
लूप के लिए एक बढ़िया विकल्प नहीं है ।
find -print0
साथ उपयोग कर रहा हैxargs -0
find -print0
के साथ संयुक्त का उपयोग करना xargs -0
कानूनी फ़ाइल नामों के खिलाफ पूरी तरह से मजबूत है, और सबसे अधिक उपलब्ध तरीकों में से एक है। उदाहरण के लिए, मान लें कि आप वर्तमान निर्देशिका में प्रत्येक PDF फ़ाइल की सूची चाहते हैं। आप लिख सकते हैं
$ find . -iname '*.pdf' -print0 | xargs -0 -n 1 echo
यह -iname '*.pdf'
वर्तमान निर्देशिका ( .
) और किसी भी उप-निर्देशिका में हर पीडीएफ (के माध्यम से ) मिलेगा , और उनमें से प्रत्येक को echo
कमांड के तर्क के रूप में पास करेगा । क्योंकि हमने -n 1
विकल्प निर्दिष्ट किया है, xargs
केवल एक समय में एक तर्क पास करेगा echo
। अगर हम उस विकल्प को छोड़ देते, xargs
तो जितना संभव हो उतने पास हो जाते echo
। (आप echo short input | xargs --show-limits
देख सकते हैं कि कमांड लाइन में कितने बाइट्स की अनुमति है।)
xargs
है, बिल्कुल?हम स्पष्ट रूप से xargs
इसके इनपुट पर प्रभाव को देख सकते हैं - और -n
विशेष रूप से प्रभाव - एक स्क्रिप्ट का उपयोग करके जो इसके तर्कों को अधिक सटीक तरीके से गूँजता है echo
।
$ cat > echoArgs.sh <<'EOF'
#!/bin/bash
echo "Number of arguments: $#"
[[ $# -eq 0 ]] && exit
for i in $(seq 1 $#); do
echo "Arg $i: <$1>"
shift
done
EOF
$ find . -iname '*.pdf' -print0 | xargs -0 ./echoArgs.sh
$ find . -iname '*.pdf' -print0 | xargs -0 -n 1 ./echoArgs.sh
ध्यान दें कि यह रिक्त स्थान और newlines को पूरी तरह से संभालता है,
$ touch 'A space-age
new line of vending machines.pdf'
$ find . -iname '*space*' -print0 | xargs -0 -n 1 ./echoArgs.sh
जो निम्नलिखित आम समाधान के साथ विशेष रूप से परेशानी होगी:
chmod +x ./echoArgs.sh
for file in $(ls *spacey*); do
./echoArgs.sh "$file"
done
टिप्पणियाँ
मैं bash
बैशर्स से असहमत हूं , क्योंकि bash
, * निक्स टूल सेट के साथ, फाइलों को संभालने में काफी माहिर हैं (जिनमें जिनके नाम व्हाट्सएप एम्बेडेड हैं)।
वास्तव में, find
आपको कौन से फाइलों को प्रोसेस करने के लिए चुनने पर ठीक अनाज नियंत्रण प्रदान करता है ... बैश पक्ष पर, आपको वास्तव में केवल यह महसूस करने की आवश्यकता है कि आपको इसमें तार बनाना चाहिए bash words
; आम तौर पर "दोहरे उद्धरण चिह्नों", या IFS का उपयोग करने वाले कुछ अन्य तंत्रों का उपयोग करके, या पाते हैं{}
ध्यान दें कि अधिकांश / कई स्थितियों में आपको IFS सेट और रीसेट करने की आवश्यकता नहीं है; बस IFS का स्थानीय रूप से उपयोग करें जैसा कि नीचे के उदाहरणों में दिखाया गया है। सभी तीनों व्हाट्सएप को ठीक से संभालते हैं। इसके अलावा, आप क्योंकि, एक "मानक" पाश संरचना की जरूरत नहीं है खोजने के \;
है प्रभावी रूप से एक पाश; बस अपने पाश तर्क को बैश फ़ंक्शन में डालें (यदि आप एक मानक टूल नहीं कह रहे हैं)।
IFS=$'\n' find ~/ -name '*.txt' -exec function-or-util {} \;
और, दो और उदाहरण
IFS=$'\n' find ~/ -name '*.txt' -exec printf 'Hello %s\n' {} \;
IFS=$'\n' find ~/ -name '*.txt' -exec echo {} \+ |sed 's/home//'
'खोजें also allows you to pass multiple filenames as args to you script ..(if it suits your need: use
+ instead
\; `)
find -print0
और हैxargs -0
।