यह उत्तर निम्नलिखित भागों में आता है:
- का मूल उपयोग
-exec
- के
-exec
साथ संयोजन में उपयोग करनाsh -c
- का उपयोग करते हुए
-exec ... {} +
- का उपयोग करते हुए
-execdir
का मूल उपयोग -exec
-exec
विकल्प अपने तर्क के रूप में वैकल्पिक तर्क के साथ एक बाहरी उपयोगिता लेता है और यह निष्पादित करता है।
यदि {}
दिए गए कमांड में कहीं भी स्ट्रिंग मौजूद है, तो इसके प्रत्येक उदाहरण को वर्तमान में संसाधित किए जा रहे पथनाम से बदल दिया जाएगा (जैसे ./some/path/FILENAME
)। अधिकांश गोले में, दो पात्रों {}
को उद्धृत करने की आवश्यकता नहीं है।
यह कहाँ समाप्त होता है, यह जानने के ;
लिए कमांड को समाप्त करने की आवश्यकता है find
। ;
शेल से बचाने के लिए , इसे उद्धृत किया जाना चाहिए \;
या ';'
, अन्यथा शेल इसे find
कमांड के अंत के रूप में देखेगा ।
उदाहरण ( \
पहली दो पंक्तियों के अंत में सिर्फ पंक्ति निरंतरता के लिए हैं):
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} ';'
यह सभी नियमित फ़ाइलें ( -type f
) पाएंगे जिनके नाम *.txt
मौजूदा निर्देशिका में या उससे नीचे के पैटर्न से मेल खाते हैं । यह तब परीक्षण करेगा कि क्या स्ट्रिंग hello
का उपयोग की गई किसी भी फ़ाइल में होता है grep -q
(जो किसी भी आउटपुट का उत्पादन नहीं करता है, बस एक निकास स्थिति है)। उन फ़ाइलों के लिए जिनमें स्ट्रिंग होती है, cat
उन्हें फ़ाइल की सामग्री को टर्मिनल में आउटपुट करने के लिए निष्पादित किया जाएगा।
प्रत्येक -exec
भी find
, जैसे पाया जाता है -type
और -name
करता है , वैसे पथनामों पर एक "परीक्षण" की तरह कार्य करता है। यदि कमांड शून्य निकास स्थिति ("सफलता" को दर्शाता है) लौटाता है, तो find
कमांड का अगला भाग माना जाता है, अन्यथा find
कमांड अगले पथनाम के साथ जारी रहता है। इसका उपयोग उपरोक्त उदाहरण में उन फ़ाइलों को खोजने के लिए किया जाता है जिनमें स्ट्रिंग होती है hello
, लेकिन अन्य सभी फ़ाइलों को अनदेखा करने के लिए।
उपरोक्त उदाहरण दो सबसे आम उपयोग मामलों को दिखाता है -exec
:
- खोज को और प्रतिबंधित करने के लिए परीक्षण के रूप में।
- पाया पथनाम पर किसी प्रकार की कार्रवाई करने के लिए (आमतौर पर, लेकिन जरूरी नहीं,
find
कमांड के अंत में )।
के -exec
साथ संयोजन में उपयोग करनाsh -c
कमांड जो -exec
निष्पादित कर सकता है वह वैकल्पिक तर्कों के साथ एक बाहरी उपयोगिता तक सीमित है। शेल-इन का उपयोग करने के लिए, फ़ंक्शंस, सशर्त, पाइपलाइन, पुनर्निर्देशन आदि के साथ सीधे -exec
संभव नहीं है, जब तक कि sh -c
बच्चे के शेल की तरह किसी चीज़ में लपेटा न जाए ।
यदि bash
सुविधाएँ आवश्यक हैं, तो इसके bash -c
स्थान पर उपयोग करें sh -c
।
sh -c
/bin/sh
कमांड लाइन पर दी गई स्क्रिप्ट के साथ चलता है , उसके बाद उस स्क्रिप्ट पर वैकल्पिक कमांड लाइन तर्क देता है।
sh -c
बिना, स्वयं के उपयोग का एक सरल उदाहरण find
:
sh -c 'echo "You gave me $1, thanks!"' sh "apples"
यह चाइल्ड शेल स्क्रिप्ट के दो तर्क देता है:
स्ट्रिंग sh
। यह $0
स्क्रिप्ट के अंदर उपलब्ध होगा , और यदि आंतरिक शेल एक त्रुटि संदेश का उत्पादन करता है, तो यह इस स्ट्रिंग के साथ उपसर्ग करेगा।
तर्क स्क्रिप्ट के apples
रूप $1
में उपलब्ध है , और अधिक तर्क थे, तब ये उपलब्ध होंगे $2
, $3
आदि। वे सूची में भी उपलब्ध होंगे "$@"
(इसके अलावा $0
जो हिस्सा नहीं होगा "$@"
)।
यह संयोजन के -exec
रूप में उपयोगी है क्योंकि यह हमें मनमाने ढंग से जटिल स्क्रिप्ट बनाने की अनुमति देता है जो इसके द्वारा प्राप्त पथनामों पर कार्य करता है find
।
उदाहरण: उन सभी नियमित फ़ाइलों को खोजें जिनमें एक निश्चित फ़ाइल नाम प्रत्यय है, और उस फ़ाइल नाम के प्रत्यय को कुछ अन्य प्रत्यय में बदल दें, जहां प्रत्यय चर में रखे गए हैं:
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c 'mv "$3" "${3%.$1}.$2"' sh "$from" "$to" {} ';'
आंतरिक स्क्रिप्ट के अंदर, $1
स्ट्रिंग होगा text
, $2
स्ट्रिंग होगा txt
और $3
जो भी पथ नाम होगा find
हमारे लिए मिल गया है। पैरामीटर विस्तार ${3%.$1}
पाथनाम लेगा और इससे प्रत्यय हटा .text
देगा।
या, का उपयोग कर dirname
/ basename
:
find . -type f -name "*.$from" -exec sh -c '
mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"' sh "$from" "$to" {} ';'
या, आंतरिक स्क्रिप्ट में जोड़े गए चर के साथ:
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2; pathname=$3
mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"' sh "$from" "$to" {} ';'
ध्यान दें कि इस अंतिम भिन्नता में, वैरिएबल from
और to
चाइल्ड शेल में बाहरी स्क्रिप्ट में समान नामों वाले वेरिएबल्स से अलग हैं।
ऊपर से एक मनमाना जटिल स्क्रिप्ट बुला का सही तरीका है -exec
के साथ find
। find
जैसे लूप में उपयोग करना
for pathname in $( find ... ); do
त्रुटि प्रवण और अयोग्य (व्यक्तिगत राय) है। यह व्हॉट्सएप पर फ़ाइल नाम को विभाजित कर रहा है, फ़ाइल नाम ग्लोबिंग को आमंत्रित करता है, और शेल find
के पहले पुनरावृत्ति को चलाने से पहले शेल के पूर्ण परिणाम का विस्तार करने के लिए शेल को भी मजबूर करता है ।
यह सभी देखें:
का उपयोग करते हुए -exec ... {} +
;
अंत में द्वारा बदले जा सकते +
। यह find
दिए गए कमांड को प्रत्येक पाए गए पथनाम के लिए एक बार के बजाय संभव के रूप में कई तर्कों (पाथनाम) के साथ निष्पादित करने का कारण बनता है। इसके लिए कार्य करने {}
से ठीक पहले तार को होना होता है+
।
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} +
यहाँ, find
परिणामी मार्ग एकत्र करेगा और cat
एक ही बार में उनमें से कई को निष्पादित करेगा।
find . -type f -name "*.txt" \
-exec grep -q "hello" {} ';' \
-exec mv -t /tmp/files_with_hello/ {} +
इसी तरह, mv
जितना संभव हो उतना कम बार निष्पादित किया जाएगा। इस अंतिम उदाहरण के लिए mv
कोर्यूटिल्स (जो -t
विकल्प का समर्थन करता है) से जीएनयू की आवश्यकता होती है ।
का उपयोग करना -exec sh -c ... {} +
भी एक कुशल तरीके से एक जटिल स्क्रिप्ट के साथ पाथनाम के सेट पर लूप करने के लिए है।
उपयोग करते समय मूल बातें समान हैं -exec sh -c ... {} ';'
, लेकिन स्क्रिप्ट अब तर्कों की एक लंबी सूची लेती है। इनको "$@"
स्क्रिप्ट के अंदर लूपिंग करके ओवर किया जा सकता है ।
फ़ाइल नाम प्रत्ययों को बदलने वाले अंतिम खंड से हमारा उदाहरण:
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2
shift 2 # remove the first two arguments from the list
# because in this case these are *not* pathnames
# given to us by find
for pathname do # or: for pathname in "$@"; do
mv "$pathname" "${pathname%.$from}.$to"
done' sh "$from" "$to" {} +
का उपयोग करते हुए -execdir
वहाँ भी है -execdir
(ज्यादातर find
वेरिएंट द्वारा लागू किया गया है , लेकिन एक मानक विकल्प नहीं है)।
इस तरह काम करता है -exec
अंतर यह है कि दिए गए शेल कमांड अपने वर्तमान कार्यशील निर्देशिका के रूप में पाया पथ नाम की सूची के साथ क्रियान्वित किया जाता है और उस के साथ {}
अपने रास्ते के बिना पाया पथ नाम की basename में शामिल होंगे (लेकिन जीएनयू find
अभी भी साथ basename उपसर्ग जाएगा ./
, जबकि बीएसडी find
ऐसा नहीं करेंगे)।
उदाहरण:
find . -type f -name '*.txt' \
-execdir mv {} done-texts/{}.done \;
यह प्रत्येक पाया *.txt
-file को उसी निर्देशिका में पहले से मौजूद done-texts
उपनिर्देशिका में ले जाएगा जहाँ फ़ाइल मिली थी । इसमें प्रत्यय जोड़कर फाइल का भी नाम बदल दिया जाएगा .done
।
यह करने के लिए थोड़ा पेचीदा मामला -exec
होगा क्योंकि हमें फ़ाइल {}
का नया नाम बनाने के लिए मिली हुई फ़ाइल का बेसनेम प्राप्त करना होगा । हमें निर्देशिका का नाम ठीक से निर्देशिका {}
का पता लगाने के लिए भी चाहिए done-texts
।
इसके साथ -execdir
, कुछ चीजें आसान हो जाती हैं।
इसके -exec
बजाय ऑपरेशन का उपयोग करके -execdir
एक बच्चे के खोल को नियोजित करना होगा:
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done"
done' sh {} +
या,
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "${name%/*}/done-texts/${name##*/}.done"
done' sh {} +