जैसे लूप का उपयोग करना
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 -0find -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।