एक खोज -exec कॉल में निष्पादित उपयोगकर्ता परिभाषित कार्य


25

मैं सोलारिस 10 पर हूं और मैंने ksh (88), बैश (3.00) और zsh (4.2.1) के साथ निम्नलिखित परीक्षण किया है।

निम्नलिखित कोड कोई परिणाम नहीं देता है:

function foo {
    echo "Hello World"
}

find somedir -exec foo \;

यह खोज कई फाइलों से मेल खाती है (जैसा कि इसके -exec ...साथ बदलकर दिखाया गया है -print), और findकॉल से बाहर बुलाए जाने पर फ़ंक्शन पूरी तरह से काम करता है ।

यहाँ man findपृष्ठ के बारे में क्या कहना है -exec:

 -exec कमांड सही है यदि निष्पादित कमांड देता है a
                     बाहर निकलने की स्थिति के रूप में शून्य मान। का अंत
                     कमांड को बचकर भाग जाना चाहिए
                     अर्धविराम (;) एक कमांड तर्क {} है
                     वर्तमान पथनाम द्वारा प्रतिस्थापित। अगर द
                     अंतिम तर्क के लिए -exec {} और आप हैं
                     अर्धविराम (;) के बजाय निर्दिष्ट करें +
                     कम समय के लिए आदेश दिया जाता है
                     {{रास्ते के समूहों द्वारा प्रतिस्थापित। अगर
                     आदेश का कोई भी आह्वान एक रिटर्न देता है
                     बाहर निकलने की स्थिति के रूप में गैर-शून्य मान, ढूंढें
                     एक गैर-शून्य निकास स्थिति लौटाता है।

मैं शायद कुछ इस तरह से कर सकता हूँ:

for f in $(find somedir); do
    foo
done

लेकिन मैं क्षेत्र विभाजक मुद्दों से निपटने से डरता हूं।

क्या एक कॉल से शेल फ़ंक्शन (उसी स्क्रिप्ट में परिभाषित किया गया है, चलो स्कूपिंग मुद्दों से परेशान नहीं होते) संभव है find ... -exec ...?

मैंने इसे दोनों के साथ आजमाया /usr/bin/findऔर /bin/findवही परिणाम मिला।


क्या आपने इसे घोषित करने के बाद फ़ंक्शन को निर्यात करने की कोशिश की? export -f foo
h3rrmiller

2
आपको फ़ंक्शन को एक बाहरी स्क्रिप्ट बनाने और इसे अंदर रखने की आवश्यकता होगी PATH। वैकल्पिक रूप से, उपयोग करें sh -c '...'और दोनों ...बिट में फ़ंक्शन को परिभाषित और चलाते हैं । यह कार्यों और लिपियों के बीच के अंतर को समझने में मदद कर सकता है ।
jw013

जवाबों:


27

A functionशेल के लिए स्थानीय है, इसलिए आपको find -execशेल का उपयोग करने की आवश्यकता होगी और इसका उपयोग करने में सक्षम होने से पहले उस शेल में फ़ंक्शन को परिभाषित करना होगा। कुछ इस तरह:

find ... -exec ksh -c '
  function foo {
    echo blan: "$@"
  }
  foo "$@"' ksh {} +

bashपर्यावरण के माध्यम से कार्यों को निर्यात करने की अनुमति देता है export -f, इसलिए आप कर सकते हैं (बैश में):

foo() { ...; }
export -f foo
find ... -exec bash -c 'foo "$@"' bash {} +

ksh88है typeset -fxनिर्यात समारोह (पर्यावरण के माध्यम से नहीं) के लिए, लेकिन यह है कि केवल वह-धमाके कम द्वारा निष्पादित स्क्रिप्ट द्वारा इस्तेमाल किया जा सकता kshके साथ है, तो नहीं ksh -c

एक अन्य विकल्प यह करना है:

find ... -exec ksh -c "
  $(typeset -f foo)"'
  foo "$@"' ksh {} +

यही है, इनलाइन स्क्रिप्ट के अंदर फ़ंक्शन typeset -fकी परिभाषा को डंप करने के लिए उपयोग करें foo। ध्यान दें कि यदि fooअन्य कार्यों का उपयोग करता है, तो आपको उन्हें भी डंप करना होगा।


2
क्या आप बता सकते हैं कि कमांड में दो घटनाएं क्यों होती हैं kshया होती हैं ? मैं पहली घटना को समझता हूं, लेकिन दूसरी घटना नहीं। bash-exec
डेनियल कुल्मन

6
@danielkullmann में bash -c 'some-code' a b c, $0है a, $1है b..., इसलिए यदि आप चाहते $@होने के लिए a, b, cइससे पहले कि आप कुछ डालने के लिए की जरूरत है। क्योंकि $0त्रुटि संदेशों को प्रदर्शित करते समय भी इसका उपयोग किया जाता है, यह शेल के नाम का उपयोग करने के लिए एक अच्छा विचार है, या ऐसा कुछ है जो उस संदर्भ में समझ में आता है।
स्टीफन चेज़लस

@ स्टीफनचेज़लस, इतने अच्छे उत्तर के लिए धन्यवाद।
User9102d82

5

यह हमेशा लागू नहीं होता है, लेकिन जब यह होता है, तो यह एक सरल उपाय है। globstarविकल्प सेट करें ( set -o globstarksh93 में, shopt -s globstarबैश it's4 में; यह डिफ़ॉल्ट रूप से zsh में है)। फिर **/वर्तमान निर्देशिका और उसकी उपनिर्देशिकाओं का पुनरावर्ती मिलान करने के लिए उपयोग करें।

उदाहरण के लिए, इसके बजाय find . -name '*.txt' -exec somecommand {} \;, आप चला सकते हैं

for x in **/*.txt; do somecommand "$x"; done

इसके बजाय find . -type d -exec somecommand {} \;, आप चला सकते हैं

for d in **/*/; do somecommand "$d"; done

इसके बजाय find . -newer somefile -exec somecommand {} \;, आप चला सकते हैं

for x in **/*; do
  [[ $x -nt somefile ]] || continue
  somecommand "$x"
done

जब **/आप के लिए काम नहीं करता है (क्योंकि आपके शेल में यह नहीं है, या क्योंकि आपको एक findविकल्प की आवश्यकता है जिसमें शेल एनालॉग नहीं है), तो फ़ंक्शन को find -execतर्क में परिभाषित करें


मैं globstarksh के संस्करण पर एक विकल्प खोजने के लिए प्रतीत नहीं कर सकता ( ksh88) मैं उपयोग कर रहा हूं।
रहमू

@ अग्रमू वास्तव में, यह ksh93 में नया है। क्या Solaris 10 में कहीं ksh93 नहीं है?
गिल्स एसओ- बुराई को रोकना '

इस ब्लॉग पोस्ट ksh93 के अनुसार सोलारिस 11 में बॉर्न शेल और ksh88 दोनों को बदलने के लिए पेश किया गया है ...
rahmu

@rahmu। सोलारिस ने कुछ समय के लिए ksh93 dtksh(कुछ X11 एक्सटेंशन के साथ ksh93) किया है, लेकिन एक पुराना संस्करण और संभवतः एक वैकल्पिक पैकेज का हिस्सा है जहां CDE वैकल्पिक है।
स्टीफन चेज़लस

यह ध्यान दिया जाना चाहिए कि पुनरावर्ती findग्लोबिंग इस से अलग है कि यह डॉटफाइल्स को छोड़कर डॉटडायर में नहीं उतरता है और यह फ़ाइल सूची को सॉर्ट करता है (दोनों को ग्लोबिंग क्वालिफायर के माध्यम से पता चल सकता है)। साथ **/*ही विरोध के साथ ./**/*, फ़ाइल नाम के साथ शुरू हो सकता है -
स्टीफन चेज़लस

4

एक परिसीमनक के रूप में \ 0 का उपयोग करें और फिल्म्स को वर्तमान प्रक्रिया में एक स्पॉन कमांड से पढ़ें, जैसे:

foo() {
  printf "Hello {%s}\n" "$1"
}

while read -d '' filename; do
  foo "${filename}" </dev/null
done < <(find . -maxdepth 2 -type f -print0)

यहाँ क्या चल रहा है:

  • read -d '' अगले \ 0 बाइट तक पढ़ता है, इसलिए आपको फ़ाइल नाम में अजीब पात्रों के बारे में चिंता करने की ज़रूरत नहीं है।
  • इसी तरह, -print0\ n के बजाय प्रत्येक उत्पन्न फ़ाइल नाम को समाप्त करने के लिए \ 0 का उपयोग करता है।
  • cmd2 < <(cmd1)रूप में ही है cmd1 | cmd2, सिवाय इसके कि CMD2 मुख्य खोल में चलाने के लिए और नहीं है एक subshell।
  • फू से कॉल को /dev/nullयह सुनिश्चित करने के लिए पुनर्निर्देशित किया गया है कि यह गलती से पाइप से नहीं पढ़ा है।
  • $filename इसलिए उद्धृत किया जाता है कि शेल एक फ़ाइल नाम को विभाजित करने की कोशिश नहीं करता है जिसमें व्हाट्सएप शामिल है।

अब, read -dऔर <(...)zsh, bash और ksh 93u में हैं, लेकिन मैं पहले के ksh संस्करणों के बारे में निश्चित नहीं हूं।


4

यदि आप एक बच्चे की प्रक्रिया चाहते हैं, तो आपकी स्क्रिप्ट से उत्पन्न, पूर्व-परिभाषित शेल फ़ंक्शन का उपयोग करने के लिए जिसे आपको इसके साथ निर्यात करने की आवश्यकता है export -f <function>

नोट: export -fबैश विशिष्ट है

चूंकि केवल एक शेल शेल फ़ंक्शन चला सकता है :

find / -exec /bin/bash -c 'function "$1"' bash {} \;

संपादित करें: अनिवार्य रूप से आपकी स्क्रिप्ट इस से मिलती जुलती होनी चाहिए:

#!/bin/bash
function foo() { do something; }
export -f foo
find somedir -exec /bin/bash -c 'foo "$0"' {} \;

1
export -fमें एक सिंटैक्स त्रुटि है ksh, और स्क्रीन में फ़ंक्शन परिभाषा को प्रिंट करता है zsh। सभी में ksh, zshऔर bash, यह समस्या का समाधान नहीं करता है।
रहमू

1
find / -exec /bin/bash -c 'function' \;
h3rrmiller

अब यह काम करता है, लेकिन केवल साथ bash। धन्यवाद!
रहमू

4
{}शेल कोड में कभी भी एम्बेड न करें ! इसका मतलब है कि फ़ाइल का नाम शेल कोड के रूप में व्याख्या किया गया है इसलिए बहुत खतरनाक है
स्टीफन चेज़लस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.