`खोज` के -exec विकल्प को समझना


53

मैं खुद को लगातार वाक्य रचना देख रहा हूं

find . -name "FILENAME"  -exec rm {} \;

मुख्य रूप से क्योंकि मैं नहीं देखता कि वास्तव में -execभाग कैसे काम करता है। ब्रेसिज़, बैकस्लैश और अर्धविराम का क्या अर्थ है? क्या उस सिंटैक्स के अन्य उपयोग के मामले हैं?


11
@ फिलीपोस: मैं आपकी बात देखता हूं। कृपया ध्यान रखें कि मैन पेज एक संदर्भ है, जो कि वाक्यविन्यास को देखने के लिए मामले की समझ रखने वालों के लिए उपयोगी है। किसी नए विषय के लिए, वे अक्सर उपयोगी होने के लिए गुप्त और औपचारिक होते हैं। आप पाएंगे कि स्वीकृत उत्तर मैन पेज प्रविष्टि के बारे में 10 गुना लंबा है, और यह एक कारण के लिए है।
Zsolt Szilagy

6
यहां तक ​​कि पुराने POSIX manपेज में A उपयोगिता_name या तर्क होता है जिसमें केवल दो वर्ण "{}" होते हैं , जिन्हें वर्तमान pathname द्वारा प्रतिस्थापित किया जाएगा , जो कि मेरे लिए दमदार लगता है। अतिरिक्त यह एक उदाहरण के साथ है -exec rm {} \;, जैसे आपके प्रश्न में। मेरे दिनों में, "बड़ी ग्रे दीवार" की तुलना में बमुश्किल कोई अन्य संसाधन थे, मुद्रित manपृष्ठों की किताबें (कागज भंडारण की तुलना में शेपर थी)। इसलिए मुझे पता है कि यह किसी नए विषय के लिए पर्याप्त है। आपका अंतिम प्रश्न हालांकि यहाँ पूछना उचित है। दुर्भाग्य से न तो @ कुसलानंद और न ही खुद उनके पास इसका जवाब है।
फिलिपोस

1
कॉमोन @Philippos। क्या आप वास्तव में कुसलानंद को बता रहे हैं कि उन्होंने मैनपेज पर सुधार नहीं किया है? :-)
Zsolt Szilagy

1
@allo हालांकि xargsकभी-कभी काम आता है, findइसके बिना कमांड करने के लिए कई पथ तर्क पारित कर सकते हैं। -exec command... {} +(के +बजाय \;) के साथ command...फिट होने के बाद कई रास्ते गुजरते हैं (प्रत्येक OS की अपनी सीमा है कि कमांड-लाइन कितने समय तक हो सकती है)। और जैसे xargs, 's की क्रिया +का -अनुरूपित रूप भी दुर्लभ मामले में कई बार चलेगा कि सीमा के भीतर फिट होने के लिए बहुत सारे रास्ते हैं। find-execcommand...
एलिया कगन

2
@ZsoltSzilagy मैंने न तो यह बताया और न ही इसका मतलब है। उसने आपको बहुत अच्छी तरह से खिलाया, मुझे लगता है कि आप खुद से खाने के लिए पर्याप्त बूढ़े हैं। - (;
फिलिप्पुस

जवाबों:


90

यह उत्तर निम्नलिखित भागों में आता है:

  • का मूल उपयोग -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:

  1. खोज को और प्रतिबंधित करने के लिए परीक्षण के रूप में।
  2. पाया पथनाम पर किसी प्रकार की कार्रवाई करने के लिए (आमतौर पर, लेकिन जरूरी नहीं, 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"

यह चाइल्ड शेल स्क्रिप्ट के दो तर्क देता है:

  1. स्ट्रिंग sh। यह $0स्क्रिप्ट के अंदर उपलब्ध होगा , और यदि आंतरिक शेल एक त्रुटि संदेश का उत्पादन करता है, तो यह इस स्ट्रिंग के साथ उपसर्ग करेगा।

  2. तर्क स्क्रिप्ट के 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के साथ findfindजैसे लूप में उपयोग करना

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 {} +

7
-execएक कार्यक्रम और तर्क लेता है और इसे चलाता है; कुछ शेल कमांड में केवल एक प्रोग्राम और तर्क होते हैं, लेकिन कई नहीं होते हैं। एक शेल कमांड में पुनर्निर्देशन और पाइपिंग शामिल हो सकते हैं; -exec(हालांकि पूरे findको पुनर्निर्देशित किया जा सकता है)। एक शेल कमांड ; && ifआदि का उपयोग कर सकते हैं ; -execहालांकि, -a -oकुछ नहीं कर सकते। एक शेल कमांड एक उपनाम या शेल फ़ंक्शन, या बिलिन हो सकता है; -execनही सकता। एक शेल कमांड vars का विस्तार कर सकता है; -execनहीं कर सकता (हालांकि बाहरी शेल जो कैन चलाता है find)। एक शेल कमांड $(command)हर बार अलग-अलग स्थानापन्न कर सकता है ; -execनही सकता। ...
dave_thompson_085

... एक शेल कमांड ग्लोब कर सकता है, -execहालांकि - findफाइलों पर उसी तरह से पुनरावृति हो सकती है जिस तरह से अधिकांश ग्लोब होगा, इसलिए शायद ही कभी ऐसा हो।
dave_thompson_085

@ dave_thompson_085 बेशक, शेल कमांड shखुद हो सकती है, जो पूरी तरह से उन सभी चीजों को करने में पूरी तरह से सक्षम है
Tavian Barnes

2
उन्होंने कहा कि यह एक शेल कमांड यहाँ गलत है, find -exec cmd arg \;एक खोल कमांड लाइन व्याख्या करने के लिए एक खोल आह्वान नहीं है, यह चलाता है execlp("cmd", "arg")सीधे, नहीं execlp("sh", "-c", "cmd arg")(जिसके लिए खोल के बराबर कर रही हो जाएंगे execlp("cmd", "arg")अगर cmdनिर्मित नहीं था)।
स्टीफन चेजलस

2
आप स्पष्ट कर सकते हैं कि सभी findतर्कों के बाद -execऔर ऊपर ;या +उसके तर्कों के साथ निष्पादित करने के लिए कमांड बनाते हैं, प्रत्येक {}तर्क के उदाहरण के साथ वर्तमान फ़ाइल (साथ ;), और {}अंतिम तर्क के रूप +में फ़ाइलों की सूची के साथ प्रतिस्थापित किया जाता है। अलग-अलग तर्कों के रूप में ( {} +मामले में)। IOW कई दलीलें -execलेता है, या द्वारा समाप्त । ;{} +
स्टीफन चेजलस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.