बैश: एक फ़ंक्शन को पैरामीटर के रूप में पास करें


91

मुझे बैश में एक पैरामीटर के रूप में एक फ़ंक्शन पास करने की आवश्यकता है। उदाहरण के लिए, निम्न कोड:

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  eval $1
  echo "after"
}

around x

उत्पादन करना चाहिए:

before
Hello world
after

मुझे पता evalहै कि इस संदर्भ में सही नहीं है, लेकिन यह सिर्फ एक उदाहरण है :)

कोई उपाय?

जवाबों:


126

यदि आपको फ़ंक्शन नाम या उसके तर्कों के मूल्यांकन में देरी करने जैसी किसी चीज़ की ज़रूरत नहीं है, तो आपको ज़रूरत नहीं है eval:

function x()      { echo "Hello world";          }
function around() { echo before; $1; echo after; }

around x

जो आप चाहते हो। आप इस तरह से समारोह और इसके तर्कों को भी पास कर सकते हैं:

function x()      { echo "x(): Passed $1 and $2";  }
function around() { echo before; "$@"; echo after; }

around x 1st 2nd

प्रिंट

before
x(): Passed 1st and 2nd
after

2
यदि मेरे पास एक और y () फ़ंक्शन है, तो क्या मैं x 1st 2nd y 1st 2nd के आसपास कर सकता हूं? यह कैसे पता चलता है कि x और y चारों ओर के तर्क हैं जबकि 1st और 2nd x और y के तर्क हैं?
techguy2000

और आप फ़ंक्शन शब्द को छोड़ सकते हैं।
jasonleonhard

हालांकि, तब वे नाम-स्थान नहीं होंगे। अर्थात यदि आपके पास विधियों के अंदर विधियाँ हैं और आप functionशब्द रखते हैं तो आप उन आंतरिक विधियों तक नहीं पहुँच सकते जब तक आप शीर्ष स्तर की विधि नहीं चलाते।
जस्सोनलोंहार्ड

29

मुझे नहीं लगता कि किसी ने भी इस सवाल का जवाब दिया। उन्होंने यह नहीं पूछा कि क्या वह आदेश में तार गूँज सकते हैं। बल्कि सवाल के लेखक जानना चाहते हैं कि क्या वह फ़ंक्शन पॉइंटर व्यवहार का अनुकरण कर सकता है।

ऐसे कुछ उत्तर हैं जो मुझे पसंद हैं, और मैं इसे एक और उदाहरण के साथ विस्तारित करना चाहता हूं।

लेखक से:

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  ($1)                   <------ Only change
  echo "after"
}

around x

इसका विस्तार करने के लिए, हमारे पास फ़ंक्शन एक्स इको "हैलो वर्ल्ड: $ 1" होगा, जो यह दिखाएगा कि फ़ंक्शन निष्पादन वास्तव में कब होता है। हम एक स्ट्रिंग पास करेंगे जो फ़ंक्शन "x" का नाम है:

function x() {
  echo "Hello world:$1"
}

function around() {
  echo "before"
  ($1 HERE)                   <------ Only change
  echo "after"
}

around x

यह वर्णन करने के लिए, स्ट्रिंग "x" को फ़ंक्शन के चारों ओर () जो कि "पहले" के लिए पारित किया जाता है, फ़ंक्शन x को बुलाता है (चर $ 1 के माध्यम से, पहले पैरामीटर पास हुआ) तर्क "HERE" पास कर रहा है, आखिरकार echos के बाद। ।

एक और के रूप में, यह फ़ंक्शन नाम के रूप में चर का उपयोग करने की पद्धति है। चर वास्तव में स्ट्रिंग को पकड़ते हैं जो फ़ंक्शन का नाम है और ($ चर arg1 arg2 ...) फ़ंक्शन को तर्कों को पारित करने के लिए कहता है। निचे देखो:

function x(){
    echo $3 $1 $2      <== just rearrange the order of passed params
}

Z="x"        # or just Z=x

($Z  10 20 30)

देता है: ३० १० २०, जहाँ हमने "एक्स" नाम के फंक्शन को वेरिएबल जेड में स्टोर किया और १० २० और ३० के मापदंडों को पार किया।

ऊपर जहां हम फ़ंक्शन को वेरिएबल नाम असाइन करके फ़ंक्शन को संदर्भित करते हैं, इसलिए हम फ़ंक्शन नाम को जानने के स्थान पर वेरिएबल का उपयोग कर सकते हैं (जो कि प्रोग्राम के सामान्य प्रवाह के लिए c में बहुत क्लासिक फ़ंक्शन पॉइंटर स्थिति में आप क्या कर सकते हैं, लेकिन पूर्व के समान है -फ़ंक्शन कॉल का चयन करना जो आप कमांड लाइन तर्कों के आधार पर बना रहे हैं)।

बैश में ये फंक्शन पॉइंटर्स नहीं होते हैं, बल्कि वे वेरिएबल्स होते हैं जो उन फ़ंक्शन के नामों को संदर्भित करते हैं जिन्हें आप बाद में उपयोग करते हैं।


यह जवाब कमाल का है। मैंने सभी उदाहरणों की बैश स्क्रिप्ट बनाई और उन्हें चलाया। मुझे यह भी पसंद है कि आपने "केवल परिवर्तन" के निर्माताओं को कैसे बनाया, जिससे बहुत मदद मिली। आपके दूसरे से अंतिम पैराग्राफ में एक गलत वर्तनी है: "आबोव"
JMI MADISON

टाइपो तय। धन्यवाद @ जे मैडिसन
uDude

7
आप इसे क्यों नहीं लपेटते ()हैं कि एक उप-शेल शुरू करें?
घोड़ागुई

17

उपयोग करने की कोई आवश्यकता नहीं है eval

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  var=$($1)
  echo "after $var"
}

around x

5

आप स्ट्रिंग के अलावा किसी फ़ंक्शन को कुछ भी नहीं दे सकते। प्रक्रिया प्रतिस्थापन नकली की तरह कर सकते हैं। बैश ने FIFO को तब तक खुला रखने का आदेश दिया जब तक कि एक कमांड पूरी नहीं हो गई।

यहाँ एक त्वरित मूर्खतापूर्ण है

foldl() {
    echo $(($(</dev/stdin)$2))
} < <(tr '\n' "$1" <$3)

# Sum 20 random ints from 0-999
foldl + 0 <(while ((n=RANDOM%999,x++<20)); do echo $n; done)

कार्यों का निर्यात किया जा सकता है, लेकिन यह उतना दिलचस्प नहीं है जितना कि यह पहली बार दिखाई देता है। मुझे लगता है कि यह स्क्रिप्टिंग या स्क्रिप्ट चलाने वाले अन्य कार्यक्रमों के लिए डिबगिंग कार्यों को सुलभ बनाने के लिए मुख्य रूप से उपयोगी है।

(
    id() {
        "$@"
    }

    export -f id
    exec bash -c 'echowrap() { echo "$1"; }; id echowrap hi'
)

id अभी भी केवल एक स्ट्रिंग होती है जो किसी फ़ंक्शन का नाम (स्वचालित रूप से पर्यावरण में एक क्रमांकन से आयात) और उसके आर्ग के रूप में होती है।

Pumbaa80 का एक अन्य उत्तर के लिए टिप्पणी भी अच्छी है ( eval $(declare -F "$1")), लेकिन इसकी मुख्य रूप से सरणियों के लिए उपयोगी है, न कि कार्य, क्योंकि वे हमेशा वैश्विक हैं। यदि आप इसे एक फ़ंक्शन के भीतर चला रहे थे तो यह सब इसे फिर से परिभाषित करेगा, इसलिए इसका कोई प्रभाव नहीं है। इसका उपयोग क्लोजर या आंशिक फ़ंक्शन या "फ़ंक्शन इंस्टेंस" बनाने के लिए नहीं किया जा सकता है, जो कि वर्तमान दायरे में बाध्य होने के लिए होता है। सबसे अच्छा यह एक स्ट्रिंग में फ़ंक्शन परिभाषा को संग्रहीत करने के लिए उपयोग किया जा सकता है जो कहीं और पुनर्परिभाषित हो जाता है - लेकिन उन कार्यों को भी केवल तब तक हार्डकोड किया जा सकता है जब तक किeval का उपयोग किया जाए

मूल रूप से बैश का उपयोग इस तरह नहीं किया जा सकता है।


2

अपने कार्यों में स्थानीय चर का उपयोग करने के लिए एक बेहतर तरीका है। समस्या तब हो जाती है कि आपको कॉलर को परिणाम कैसे मिलता है। एक तंत्र कमांड प्रतिस्थापन का उपयोग करना है:

function myfunc()
{
    local  myresult='some value'
    echo "$myresult"
}

result=$(myfunc)   # or result=`myfunc`
echo $result

यहां परिणाम स्टडआउट के लिए आउटपुट है और एक चर में मूल्य को पकड़ने के लिए कॉलर कमांड प्रतिस्थापन का उपयोग करता है। चर को फिर आवश्यकतानुसार उपयोग किया जा सकता है।


1

आप की तर्ज पर कुछ होना चाहिए:

function around()
{
  echo 'before';
  echo `$1`;
  echo 'after';
}

फिर आप कॉल कर सकते हैं around x


-1

इसे पूरा करने का एकमात्र तरीका eval है। केवल वास्तविक नकारात्मक पक्ष इसका सुरक्षा पहलू है, क्योंकि आपको यह सुनिश्चित करने की आवश्यकता है कि दुर्भावनापूर्ण कुछ भी पास नहीं होता है और केवल आप जिसे कॉल करना चाहते हैं उसे कॉल किया जाएगा (यह जाँचने के साथ कि इसमें 'जैसे गंदे अक्षर नहीं हैं;') इसमें भी)।

इसलिए यदि आप कोड को कॉल कर रहे हैं, तो संभवतः इसे करने का एकमात्र तरीका है। ध्यान दें कि ऐसे अन्य रूप हैं, जो संभवत: उप-प्रक्षेत्र ($) और ``) से जुड़े होंगे, लेकिन वे अधिक सुरक्षित नहीं हैं और अधिक महंगे हैं।


eval यह करने का एकमात्र तरीका है।
वेस

1
यदि आप eval $1किसी फ़ंक्शन का उपयोग करके कॉल कर सकते हैं, तो आप आसानी से देख सकते हैंif declare -F "$1" >/dev/null; then eval $1; fi
user123444555621

2
... या इससे भी बेहतर:eval $(declare -F "$1")
user123444555621
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.