लिनक्स में एक शेल फंक्शन खोजें?


185

क्या findशेल में परिभाषित एक फ़ंक्शन को निष्पादित करने का एक तरीका है ? उदाहरण के लिए:

dosomething () {
  echo "doing something with $1"
}
find . -exec dosomething {} \;

उसी का परिणाम है:

find: dosomething: No such file or directory

वहाँ एक रास्ता पाने के लिए है finds ' -execको देखने के लिए dosomething?

जवाबों:


262

चूंकि केवल शेल जानता है कि शेल फ़ंक्शन कैसे चलाना है, आपको फ़ंक्शन चलाने के लिए शेल चलाना होगा। आपको निर्यात के लिए अपने फ़ंक्शन को भी चिह्नित करना export -fहोगा, अन्यथा उपधारा उन्हें विरासत में नहीं देगी:

export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;

7
तुमने मुझे पराजित किया। वैसे आप ब्रेसेस को इस्तेमाल करने के बजाय कोट्स के अंदर रख सकते हैं $0
अगली सूचना तक रोक दिया गया।

20
यदि आप इसका उपयोग find . -exec bash -c 'dosomething "$0"' {} \;करेंगे तो फ़ाइल नाम में स्थान (और अन्य अजीब अक्षर) संभाल लेंगे ...
गॉर्डन डेविसन

3
@alxndr: डबल-कोट्स, बैकक्वाट्स, डॉलर-सिग्नल्स, कुछ एस्केप कॉम्बो, आदि के साथ फिल्म्स पर असफल हो जाएंगे ...
गॉर्डन डेविसन

4
यह भी ध्यान दें कि आपके द्वारा किया जा रहा कोई भी फ़ंक्शन तब तक उपलब्ध नहीं होगा जब तक कि आप उन्हें निर्यात नहीं करते हैं।
hraban

3
export -fबैश के कुछ संस्करणों में केवल काम करेंगे। यह /bin/sh
पॉज़िक्स

120
find . | while read file; do dosomething "$file"; done

10
अच्छा समाधान है। फ़ंक्शन का निर्यात करने की आवश्यकता नहीं है या तर्कों से बचने के लिए गड़बड़ कर रहा है और संभवतः अधिक कुशल है क्योंकि यह प्रत्येक फ़ंक्शन को निष्पादित करने के लिए सब-टाइम स्पॉनिंग नहीं कर रहा है।
टॉम

19
हालांकि, ध्यान रखें कि यह नए नाम वाले फ़ाइल नाम पर टूट जाएगा।
chepner

3
यह अधिक "शेल'इश" है क्योंकि आपके वैश्विक चर और फ़ंक्शन हर बार पूरी तरह से नया शेल / वातावरण बनाए बिना उपलब्ध होंगे। एडम के तरीके को आजमाने और हर तरह की पर्यावरण की समस्याओं में दौड़ने के बाद यह कठिन तरीका सीखा। यह विधि सभी निर्यातों के साथ आपके वर्तमान उपयोगकर्ता के शेल को भी भ्रष्ट नहीं करती है और इसके लिए कम डैपलाइन की आवश्यकता होती है।
रे फॉस

कोई उप शेल के साथ स्वीकार किए गए उत्तर की तुलना में बहुत तेजी से! अच्छा! धन्यवाद!
बिल हेलर

2
मैंने भी while readलूप के लिए बदलकर अपना मुद्दा ठीक किया ; for item in $(find . ); do some_function "${item}"; done
user5359531

22

ऊपर जैक का जवाब बहुत अच्छा है, लेकिन कुछ ऐसे नुकसान हैं जो आसानी से दूर हो जाते हैं:

find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done

यह एक लाइनफीड के बजाय एक सीमांकक के रूप में नल का उपयोग करता है, इसलिए लाइनफीड वाले फ़ाइलनाम काम करेंगे। यह उस -rध्वज का भी उपयोग करता है जो बैकस्लैश से बचने को अक्षम करता है, इसके बिना फ़ाइलनाम में बैकस्लैश काम नहीं करेगा। यह भी साफ करता IFSहै कि नामों में संभावित अनुगामी व्हाट्सएप को नहीं छोड़ा गया है।


1
इसके लिए अच्छा है /bin/bashलेकिन इसमें काम नहीं किया जाएगा /bin/sh। कितने दुख की बात है।
Роман Коптев

@ РоманКоптев कितना भाग्यशाली है कि कम से कम / बिन / बैश में काम करता है।
दक्षिणायन

21

{}नीचे दिखाए अनुसार उद्धरण जोड़ें :

export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;

इसके द्वारा लौटाए गए विशेष वर्णों के कारण किसी भी त्रुटि को ठीक किया जाता है find, उदाहरण के लिए उनके नाम में कोष्ठक के साथ फाइलें।


1
यह उपयोग करने का सही तरीका नहीं है {}। यह डबल फाइल वाले फ़ाइल नाम के लिए टूट जाएगा। touch '"; rm -rf .; echo "I deleted all you files, haha। उफ़।
ग्नौरफ_गनीउरफ

हां, यह बहुत बुरा है। इंजेक्शन द्वारा इसका फायदा उठाया जा सकता है। बहुत असुरक्षित। इस का उपयोग न करें!
डॉमिनिक

1
@kdubs: कमांड-स्ट्रिंग के भीतर $ 0 (अयोग्य) का उपयोग करें और फ़ाइलनाम को पहले तर्क के रूप में पास करें: -exec bash -c 'echo $0' '{}' \;ध्यान दें कि उपयोग करते समय bash -c, $ 0 पहला तर्क है, स्क्रिप्ट नाम नहीं।
श्रीधाम

10

थोक में प्रसंस्करण परिणाम

बढ़ी हुई दक्षता के लिए, कई लोग xargsथोक में परिणामों को संसाधित करने के लिए उपयोग करते हैं, लेकिन यह बहुत खतरनाक है। क्योंकि वहाँ एक वैकल्पिक विधि है findकि थोक में परिणाम निष्पादित में शुरू किया गया था ।

नोट यद्यपि कि इस विधि POSIX- में एक आवश्यकता उदाहरण के लिए जैसे कुछ कैविएट्स के साथ आ सकते हैं findकरने के लिए {}आदेश के अंत में।

export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +

findकी एक कॉल करने के लिए तर्क के रूप में कई परिणाम पारित करेंगे bashऔर forउन तर्कों के माध्यम से -loop दोहराता, फ़ंक्शन को निष्पादित करने dosomethingके लिए उन में से हर एक पर।

उपरोक्त समाधान में तर्क शुरू होता है $1, यही वजह है कि एक _(जो प्रतिनिधित्व करता है $0) है।

प्रसंस्करण एक के बाद एक परिणाम देता है

उसी तरह, मुझे लगता है कि स्वीकृत शीर्ष उत्तर को सही होना चाहिए

export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;

यह न केवल अधिक समझदार है, क्योंकि तर्क हमेशा शुरू होना चाहिए $1, लेकिन यह भी $0अप्रत्याशित व्यवहार का उपयोग कर सकता है यदि फ़ाइल नाम द्वारा लौटाया गया findशेल के लिए विशेष अर्थ है।


9

स्क्रिप्ट को ही कॉल करें, तर्क के रूप में मिली प्रत्येक आइटम को पास करना:

#!/bin/bash

if [ ! $1 == "" ] ; then
   echo "doing something with $1"
   exit 0
fi

find . -exec $0 {} \;

exit 0

जब आप स्क्रिप्ट को अपने आप चलाते हैं, तो यह वही पाता है जो आप ढूंढ रहे हैं और कॉल को प्रत्येक परिणाम को तर्क के रूप में पारित करते हैं। जब स्क्रिप्ट को एक तर्क के साथ चलाया जाता है, तो यह तर्क पर कमांड निष्पादित करता है और फिर बाहर निकलता है।


शांत विचार लेकिन बुरी शैली: दो उद्देश्यों के लिए एक ही स्क्रिप्ट का उपयोग करता है। यदि आप अपने बिन में फ़ाइलों की संख्या को कम करना चाहते हैं / तो आप अपनी सभी लिपियों को एक एकल में मर्ज कर सकते हैं जिसमें शुरुआत में एक बड़ा मामला है। बहुत साफ समाधान, है ना?
user829755

इसका उल्लेख नहीं करने पर यह असफल हो जाएगा find: ‘myscript.sh’: No such file or directoryजैसा कि शुरू किया गया bash myscript.sh...
Camusensei

5

आप उन लोगों के लिए जो किसी bash फ़ंक्शन की तलाश कर रहे हैं, जो वर्तमान निर्देशिका में सभी फ़ाइलों पर दिए गए कमांड को निष्पादित करेगा, मैंने उपरोक्त उत्तरों में से एक को संकलित किया है:

toall(){
    find . -type f | while read file; do "$1" "$file"; done
}

ध्यान दें कि यह रिक्त स्थान वाले फ़ाइल नामों के साथ टूटता है (नीचे देखें)।

एक उदाहरण के रूप में, इस फ़ंक्शन को लें:

world(){
    sed -i 's_hello_world_g' "$1"
}

कहते हैं कि मैं वर्तमान निर्देशिका में सभी फ़ाइलों में दुनिया में हैलो के सभी उदाहरण बदलना चाहता था। मुझे क्या करना होगा:

toall world

फ़ाइल नाम में किसी भी प्रतीक के साथ सुरक्षित होने के लिए, उपयोग करें:

toall(){
    find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}

(लेकिन आपको इसके लिए एक findहैंडल की आवश्यकता है -print0जैसे, GNU find)।


3

मैं अनुसरण के रूप में सबसे आसान तरीका ढूंढता हूं, एकल में दो कमांड दोहराता हूं

func_one () {
  echo "first thing with $1"
}

func_two () {
  echo "second thing with $1"
}

find . -type f | while read file; do func_one $file; func_two $file; done

3

संदर्भ के लिए, मैं इस परिदृश्य से बचता हूं:

for i in $(find $dir -type f -name "$name" -exec ls {} \;); do
  _script_function_call $i;
done;

वर्तमान स्क्रिप्ट फ़ाइल में खोज का आउटपुट प्राप्त करें और अपनी इच्छानुसार आउटपुट पर पुनरावृति करें। मैं स्वीकृत उत्तर से सहमत हूं, लेकिन मैं अपनी स्क्रिप्ट फ़ाइल के बाहर फ़ंक्शन को उजागर नहीं करना चाहता।


इसका आकार सीमा है
रिचर्ड

2

इस तरह से किसी फ़ंक्शन को निष्पादित करना संभव नहीं है ।

इसे दूर करने के लिए आप अपने फ़ंक्शन को शेल स्क्रिप्ट में रख सकते हैं और इसे कॉल कर सकते हैं find

# dosomething.sh
dosomething () {
  echo "doing something with $1"
}
dosomething $1

अब इसे इस रूप में उपयोग करें:

find . -exec dosomething.sh {} \;

मेरे ~ बिन में अधिक फ़ाइलों से बचने की कोशिश कर रहा था। हालांकि धन्यवाद!
alxndr

मैं नीच मानता था लेकिन अपने आप में समाधान बुरा नहीं है। कृपया बस सही उद्धरण का उपयोग करें: dosomething $1=> dosomething "$1"और अपनी फ़ाइल को ठीक से शुरू करेंfind . -exec bash dosomething.sh {} \;
Camusensei

2

फ़ंक्शन को एक अलग फ़ाइल में रखें और findउस पर अमल करें।

शेल फ़ंक्शंस उस शेल के लिए आंतरिक हैं जिसे वे परिभाषित करते हैं; findकभी नहीं देख पाएंगे।


पकड़ लिया; समझ में आता है। हालांकि मेरे ~ / बिन में अधिक फ़ाइलों से बचने की कोशिश कर रहा था।
alxndr

2

कुछ अन्य उत्तरों को जोड़ और स्पष्टीकरण प्रदान करने के लिए, यदि आप ( execया ) के लिए बल्क विकल्प का उपयोग कर रहे हैं , और सभी स्थिति संबंधी तर्कों को पुनः प्राप्त करना चाहते हैं, तो आपको इसके साथ निपटने पर विचार करने की आवश्यकता है । और अधिक संक्षेप में, नीचे दिए गए आदेश पर विचार करें, जो कि ऊपर दिए गए सुझाव के अनुसार उपयोग करता है, और बस प्रत्येक निर्देशिका से '.wav' के साथ समाप्त होने वाले फ़ाइल पथों को गूँजता है:execdir-exec command {} +$0bash -cbash -c

find "$1" -name '*.wav' -execdir bash -c 'echo $@' _ {} +

बैश मैनुअल कहते हैं:

If the -c option is present, then commands are read from the first non-option argument command_string.  If there are arguments after the command_string, they  are  assigned  to  the
                 positional parameters, starting with $0.

यहाँ, 'check $@'कमांड स्ट्रिंग है, और _ {}कमांड स्ट्रिंग के बाद तर्क हैं। ध्यान दें कि $@बैश में एक विशेष स्थितीय पैरामीटर है जो 1 से शुरू होने वाले सभी स्थितीय मापदंडों तक फैलता है । यह भी ध्यान दें कि -cविकल्प के साथ , पहला तर्क स्थितीय पैरामीटर को सौंपा गया है $0। इसका मतलब यह है कि यदि आप सभी स्थितिगत मापदंडों का उपयोग करने की कोशिश करते हैं $@, तो आपको केवल शुरू $1और ऊपर के पैरामीटर मिलेंगे । यही कारण है कि डोमिनिक के उत्तर में _पैरामीटर भरने के लिए एक डमी तर्क है $0, इसलिए हम चाहते हैं कि सभी तर्क बाद में उपलब्ध हों यदि हम $@उदाहरण के लिए पैरामीटर विस्तार का उपयोग करते हैं , या उस उत्तर में लूप के लिए।

बेशक, स्वीकृत उत्तर के समान, bash -c 'shell_function $0 $@'यह भी स्पष्ट रूप से गुजरने से काम करेगा $0, लेकिन फिर, आपको यह ध्यान रखना $@होगा कि अपेक्षा के अनुरूप काम नहीं होगा।


0

सीधे नहीं, नहीं। खोज एक अलग प्रक्रिया में निष्पादित हो रही है, आपके शेल में नहीं।

एक शेल स्क्रिप्ट बनाएं जो आपके फ़ंक्शन के समान काम करती है और वह मिल सकती -execहै।


मेरे ~ बिन में अधिक फ़ाइलों से बचने की कोशिश कर रहा था। हालांकि धन्यवाद!
alxndr

-2

मैं -execपूरी तरह से उपयोग करने से बचूंगा। उपयोग क्यों नहीं xargs?

find . -name <script/command you're searching for> | xargs bash -c

उस समय, IIRC यह उपयोग किए गए संसाधनों की मात्रा को कम करने के प्रयास में था। लाखों खाली फ़ाइलों को खोजने और उन्हें हटाने के बारे में सोचें।
अलक्षेंद्र
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.