2012-08-26 13 views
35

मुझे बैश में एक फ़ंक्शन लिखना है। समारोह में लगभग 7 तर्क होंगे। मुझे पता है कि मैं इस तरह एक समारोह कॉल कर सकते हैं: औरक्या बैश में स्थितित्मक तर्क से बचने का कोई तरीका है?

function_name $arg1 $arg2 

मैं समारोह के अंदर इस तरह मेरी मापदंडों उल्लेख कर सकते हैं:

मानकों के साथ एक समारोह कॉल करने के लिए

function_name() { 
    echo "Parameter #1 is $1" 
} 

मेरा प्रश्न क्या, क्या फ़ंक्शन के अंदर पैरामीटर का एक बेहतर तरीका है? क्या मैं $ 1, $ 2, $ 3, .... चीज़ से बच सकता हूं और बस $ arg1, $ arg2, ... का उपयोग कर सकता हूं?

क्या इसके लिए कोई उचित विधि है या क्या मुझे इन पैरामीटर को फ़ंक्शन के अंदर कुछ अन्य चरों को फिर से असाइन करने की आवश्यकता है? उदा .:

function_name() { 
    $ARG1=$1 
    echo "Parameter #1 is $ARG1" 
} 

किसी भी उदाहरण की बहुत सराहना की जाएगी।

+1

'$ 1' बनाम' $ ARG1' का उपयोग करने के बीच क्या अंतर है? –

+0

@ जोनलिन: मैं $ 1, $ 2 की बजाय अधिक सहज नाम का उपयोग कर सकता हूं। संख्या भ्रमित कर रहे हैं। – Bhushan

+2

क्या आपको वास्तव में सात अनिवार्य पैरामीटर की आवश्यकता है? एक आम दृष्टिकोण विकल्प में जितना संभव हो सके बारी करना होगा, और एक डिफ़ॉल्ट मान प्रदान करना होगा जहां यह समझ में आता है। यह आपकी स्क्रिप्ट को उपयोग करने में बहुत आसान बनाता है यदि आपको इतनी बड़ी संख्या में तर्कों के लिए किसी विशेष स्थितिगत क्रम को याद रखने की आवश्यकता नहीं है, भले ही अधिकांश उपयोगकर्ता अधिकांश समय डिफ़ॉल्ट मानों को ओवरराइड कर दें। – tripleee

उत्तर

45

कर कि समारोह में स्थानीय चर करने के लिए तर्क बताए है आम तरीका है, यानी में रुचि रखते हैं getopt -स्टाइल विकल्प पार्सिंग हो सकता है।

copy() { 
    local arg from to 
    while getopts 'f:t:' arg 
    do 
     case ${arg} in 
      f) from=${OPTARG};; 
      t) to=${OPTARG};; 
      *) return 1 # illegal option 
     esac 
    done 
} 

copy -f /tmp/a -t /tmp/b 

दुःख की बात है बैश लंबे विकल्पों को संभाल नहीं कर सकते हैं जो अधिक पढ़े जा सकेंगे, अर्थात्:

copy --from /tmp/a --to /tmp/b 

के लिए, आप या तो बाहरी getopt प्रोग्राम का उपयोग करने की जरूरत है (जो मुझे लगता है कि केवल जीएनयू सिस्टम पर लंबा विकल्प समर्थन है) या हाथ से लंबे विकल्प पार्सर को लागू करें, यानी:

copy() { 
    local from to 

    while [[ ${1} ]]; do 
     case "${1}" in 
      --from) 
       from=${2} 
       shift 
       ;; 
      --to) 
       to=${2} 
       shift 
       ;; 
      *) 
       echo "Unknown parameter: ${1}" >&2 
       return 1 
     esac 

     if ! shift; then 
      echo 'Missing parameter argument.' >&2 
      return 1 
     fi 
    done 
} 

copy --from /tmp/a --to /tmp/b 
,210

यह भी देखें: using getopts in bash shell script to get long and short command line options


तुम भी आलसी हो सकता है, और सिर्फ समारोह, यानी तर्क के रूप में 'चर' पारित:

copy() { 
    local "${@}" 

    # ... 
} 

copy from=/tmp/a to=/tmp/b 

और आप ${from} और स्थानीय चर के रूप में कार्य में ${to} होगा।

बस ध्यान दें कि नीचे जैसा ही मुद्दा लागू होता है - यदि कोई विशेष चर पारित नहीं होता है, तो यह मूल वातावरण से विरासत में प्राप्त होगा।

copy() { 
    local from to # reset first 
    local "${@}" 

    # ... 
} 

सुनिश्चित करना है कि ${from} और ${to} सेट नहीं होगा जब पारित नहीं: आप की तरह एक 'सुरक्षा लाइन' जोड़ सकते हैं।


और अगर कुछ बहुत बुरा अपने ब्याज की है, तो आप भी वैश्विक चर के रूप तर्क जब समारोह लागू आवंटित कर सकते हैं, यानी:

from=/tmp/a to=/tmp/b copy 

तो फिर तुम सिर्फ ${from} और ${to} इस्तेमाल कर सकते हैं copy() फ़ंक्शन के भीतर। बस ध्यान दें कि आपको हमेशा सभी पैरामीटर पास करना चाहिए। अन्यथा, एक यादृच्छिक चर समारोह में रिसाव हो सकता है।

from= to=/tmp/b copy # safe 
to=/tmp/b copy   # unsafe: ${from} may be declared elsewhere 

आप बैश 4.1 (मुझे लगता है कि), आप भी साहचर्य सरणियों का उपयोग कर प्रयास कर सकते हैं। यह आपको नामांकित तर्क पारित करने की अनुमति देगा लेकिन बदसूरत हो जाएगा। की तरह कुछ:

args=([from]=/tmp/a [to]=/tmp/b) 
copy args 

और फिर copy() में, आप grab the array नहीं करनी होंगी।

+0

'स्थानीय' विधि: =/tmp/a से =/tmp/b से कॉपी करें; ऐसा लगता है कि कोई कैसे लक्ष्य बनाएं 'प्रतिलिपि' कहलाता है और इसे दो नए पर्यावरण चर (से और से) पास करता है। मुझे वास्तव में सिंटैक्स समानताएं पसंद हैं। –

+0

@claytontstanley: हाँ, वह इरादा था;)। –

+0

मैं वैकल्पिक तर्कों के लिए वैश्विक परिवर्तनीय दृष्टिकोण का उपयोग करता हूं, लेकिन मैंने फ़ंक्शन के बाद वैरिएबल कहा है और चेक किए जाने के बाद इसे फ़ंक्शन के भीतर साफ़ कर दिया है। उदाहरण के लिए, यदि मेरे पास 'get_input()' के लिए एक वैकल्पिक संकेत है, तो मैं 'gi_prompt =" blah blah blah "' का उपयोग करता हूं, और फ़ंक्शन में, 'gi_prompt को अनसेट करें'। फ़ंक्शन से संबंधित सबकुछ को नाम देकर, यह कुछ लचीलापन की अनुमति देते हुए लीक और नामकरण संघर्ष को रोकता है। –

0

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

अब, अगर आप सिर्फ मापदंडों वापस प्रतिध्वनित करने के लिए चाहते हैं, आप उन पर पुनरावृति कर सकते हैं:

for $arg in "[email protected]" 
do 
    echo "$arg" 
done 

सिर्फ एक मजेदार तथ्य यह है;

copy() { 
    local from=${1} 
    local to=${2} 

    # ... 
} 

एक अन्य समाधान: जब तक आप एक सूची कार्रवाई कर रहे हैं, तो आप शायद somthing अधिक उपयोगी

3

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

A=aaa 
B=bbb 

echo "A=$A B=$B C=$C" 

example() { 
    echo "example(): A=$A B=$B C=$C" 

    A=AAA 
    local B=BBB 
    C=CCC 

    echo "example(): A=$A B=$B C=$C" 
} 

example 

echo "A=$A B=$B C=$C" 

इस स्निपेट के आउटपुट है:

A=aaa B=bbb C= 
example(): A=aaa B=bbb C= 
example(): A=AAA B=BBB C=CCC 
A=AAA B=bbb C=CCC 

इस दृष्टिकोण का स्पष्ट नुकसान है कि कार्यों आत्म निहित नहीं हैं किसी भी अधिक और कहा कि एक समारोह के बाहर एक चर की स्थापना अनपेक्षित दुष्प्रभाव हो सकते हैं। इससे चीजों को कड़ी मेहनत भी मिलती है यदि आप पहले किसी चर को निर्दिष्ट किए बिना किसी फ़ंक्शन में डेटा पास करना चाहते हैं, क्योंकि यह फ़ंक्शन अब स्थितित्मक पैरामीटर का उपयोग नहीं कर रहा है।

सबसे आम तरीका यह संभाल करने के लिए एक समारोह के अंदर तर्क के लिए स्थानीय चर और किसी भी अस्थायी चर का उपयोग करने के लिए है:

example() { 
    local A="$1" B="$2" C="$3" TMP="/tmp" 

    ... 
} 

इस समारोह-स्थानीय चर के साथ खोल नेमस्पेस को दूषित बचा जाता है।

7

तुम हमेशा पर्यावरण के माध्यम से चीजों को पारित कर सकते हैं:

#!/bin/sh 
foo() { 
    echo arg1 = $arg1 
    echo arg2 = $arg2 
} 

arg1=banana arg2=apple foo 
0

इस एक पुराने विषय है, लेकिन अभी भी मैं नीचे समारोह साझा करना चाहते हैं (बैश 4 आवश्यकता है)। यह तर्कों का नाम पार्स करता है और स्क्रिप्ट वातावरण में चर सेट करता है। बस सुनिश्चित करें कि आपके पास आवश्यक सभी पैरामीटर के लिए आपके पास डिफ़ॉल्ट डिफ़ॉल्ट मान हैं। अंत में निर्यात विवरण भी एक eval हो सकता है। मौजूदा स्क्रिप्ट्स को विस्तारित करने के लिए शिफ्ट के साथ संयोजन में यह बहुत अच्छा है जो पहले से ही कुछ पोजीशनल पैरामीटर लेता है और आप सिंटैक्स को बदलना नहीं चाहते हैं, लेकिन फिर भी कुछ लचीलापन जोड़ते हैं।

parseOptions() 
{ 
    args=("[email protected]") 
    for opt in "${args[@]}"; do 
    if [[ ! "${opt}" =~ .*=.* ]]; then 
     echo "badly formatted option \"${opt}\" should be: option=value, stopping..." 
     return 1 
    fi 
    local var="${opt%%=*}" 
    local value="${opt#*=}" 
    export ${var}="${value}" 
    done 
    return 0 
} 
1

मुझे लगता है कि मेरे पास आपके लिए समाधान है। कुछ चाल के साथ आप वास्तव में सरणी के साथ कार्यों के लिए नामित पैरामीटर पास कर सकते हैं।

testPassingParams() { 

    @var hello 
    l=4 @array anArrayWithFourElements 
    l=2 @array anotherArrayWithTwo 
    @var anotherSingle 
    @reference table # references only work in bash >=4.3 
    @params anArrayOfVariedSize 

    test "$hello" = "$1" && echo correct 
    # 
    test "${anArrayWithFourElements[0]}" = "$2" && echo correct 
    test "${anArrayWithFourElements[1]}" = "$3" && echo correct 
    test "${anArrayWithFourElements[2]}" = "$4" && echo correct 
    # etc... 
    # 
    test "${anotherArrayWithTwo[0]}" = "$6" && echo correct 
    test "${anotherArrayWithTwo[1]}" = "$7" && echo correct 
    # 
    test "$anotherSingle" = "$8" && echo correct 
    # 
    test "${table[test]}" = "works" 
    table[inside]="adding a new value" 
    # 
    # I'm using * just in this example: 
    test "${anArrayOfVariedSize[*]}" = "${*:10}" && echo correct 
} 

fourElements=(a1 a2 "a3 with spaces" a4) 
twoElements=(b1 b2) 
declare -A assocArray 
assocArray[test]="works" 

testPassingParams "first" "${fourElements[@]}" "${twoElements[@]}" "single with spaces" assocArray "and more... " "even more..." 

test "${assocArray[inside]}" = "adding a new value" 

दूसरे शब्दों में, न केवल आप (उनके नाम से अपने मानकों को कॉल कर सकते हैं जो एक और अधिक पठनीय कोर के लिए बनाता है:

विधि मैं विकसित आप इस प्रकार का कार्य करने के लिए पारित कर दिया मापदंडों का उपयोग करने की अनुमति देता है), आप वास्तव में सरणी (और चर के संदर्भों को संदर्भित कर सकते हैं - यह सुविधा केवल बैश 4.3 में काम करती है)! इसके अलावा, मैप किए गए चर सभी स्थानीय दायरे में हैं, जैसे $ 1 (और अन्य)।

यह कोड जो इस काम को बनाता है वह बहुत हल्का है और बैश 3 और बैश 4 दोनों में काम करता है (ये केवल वे संस्करण हैं जिनके साथ मैंने इसका परीक्षण किया है)। यदि आप इस तरह की अधिक चालों में रूचि रखते हैं जो बहुत अच्छे और आसान के साथ विकास करना चाहते हैं, तो आप मेरे Bash Infinity Framework पर एक नज़र डाल सकते हैं, नीचे दिए गए कोड को उस उद्देश्य के लिए विकसित किया गया था।

Function.AssignParamLocally() { 
    local commandWithArgs=($1) 
    local command="${commandWithArgs[0]}" 

    shift 

    if [[ "$command" == "trap" || "$command" == "l="* || "$command" == "_type="* ]] 
    then 
     paramNo+=-1 
     return 0 
    fi 

    if [[ "$command" != "local" ]] 
    then 
     assignNormalCodeStarted=true 
    fi 

    local varDeclaration="${commandWithArgs[1]}" 
    if [[ $varDeclaration == '-n' ]] 
    then 
     varDeclaration="${commandWithArgs[2]}" 
    fi 
    local varName="${varDeclaration%%=*}" 

    # var value is only important if making an object later on from it 
    local varValue="${varDeclaration#*=}" 

    if [[ ! -z $assignVarType ]] 
    then 
     local previousParamNo=$(expr $paramNo - 1) 

     if [[ "$assignVarType" == "array" ]] 
     then 
      # passing array: 
      execute="$assignVarName=(\"\${@:$previousParamNo:$assignArrLength}\")" 
      eval "$execute" 
      paramNo+=$(expr $assignArrLength - 1) 

      unset assignArrLength 
     elif [[ "$assignVarType" == "params" ]] 
     then 
      execute="$assignVarName=(\"\${@:$previousParamNo}\")" 
      eval "$execute" 
     elif [[ "$assignVarType" == "reference" ]] 
     then 
      execute="$assignVarName=\"\$$previousParamNo\"" 
      eval "$execute" 
     elif [[ ! -z "${!previousParamNo}" ]] 
     then 
      execute="$assignVarName=\"\$$previousParamNo\"" 
      eval "$execute" 
     fi 
    fi 

    assignVarType="$__capture_type" 
    assignVarName="$varName" 
    assignArrLength="$__capture_arrLength" 
} 

Function.CaptureParams() { 
    __capture_type="$_type" 
    __capture_arrLength="$l" 
} 

alias @trapAssign='Function.CaptureParams; trap "declare -i \"paramNo+=1\"; Function.AssignParamLocally \"\$BASH_COMMAND\" \"\[email protected]\"; [[ \$assignNormalCodeStarted = true ]] && trap - DEBUG && unset assignVarType && unset assignVarName && unset assignNormalCodeStarted && unset paramNo" DEBUG; ' 
alias @param='@trapAssign local' 
alias @reference='_type=reference @trapAssign local -n' 
alias @var='_type=var @param' 
alias @params='_type=params @param' 
alias @array='_type=array @param' 
+0

धन्यवाद। मुझे इसे आज़माना है। – Bhushan

+0

@ बुशान मैंने अभी एक और जोड़ के साथ कोड अपडेट किया है - पासिंग संदर्भ, जिसे बैश 4.3 में पेश किया गया था। अन्य चीजों के अलावा यह सहयोगी सरणी को कार्यों में पार करना संभव बनाता है। – niieani

1

मैं व्यक्तिगत रूप से की तरह

func(a b){ 
    echo $a 
    echo $b 
} 

वाक्य रचना के कुछ प्रकार को देखने के लिए उम्मीद कर रही थी लेकिन चूंकि कि एक बात नहीं है, और एक मैं (वैश्विक चर करने के लिए काफी कुछ संदर्भ देखें scoping की चेतावनी के बिना नहीं और विवादों का नामकरण), मैं अपना दृष्टिकोण साझा करूंगा।

copy समारोह का उपयोग करना Michal's answer से:

copy(){ 
    cp $from $to 
} 
from=/tmp/a 
to=/tmp/b 
copy 

यह बुरा है, क्योंकि from और to इस तरह के व्यापक शब्द है कि कार्यों के किसी भी संख्या के लिए इसका उपयोग कर सकते हैं। आप जल्दी से नामकरण संघर्ष या अपने हाथों पर "रिसाव" के साथ समाप्त हो सकते हैं।

letter(){ 
    echo "From: $from" 
    echo "To: $to" 
    echo 
    echo "$1" 
} 

to=Emily 
letter "Hello Emily, you're fired for missing two days of work." 

# Result: 
# From: /tmp/a 
# To: Emily 

# Hello Emily, you're fired for missing two days of work. 

तो मेरा दृष्टिकोण उन्हें "नेमस्पेस" करना है। मैं फ़ंक्शन के बाद चर का नाम देता हूं और इसके साथ फ़ंक्शन के बाद इसे हटा देता हूं। बेशक, मैं केवल वैकल्पिक मानों के लिए इसका उपयोग करता हूं जिनमें डिफ़ॉल्ट मान हैं। अन्यथा, मैं बस स्थितित्मक तर्क का उपयोग करता हूं।

copy(){ 
    if [[ $copy_from ]] && [[ $copy_to ]]; then 
     cp $copy_from $copy_to 
     unset copy_from copy_to 
    fi 
} 
copy_from=/tmp/a 
copy_to=/tmp/b 
copy # Copies /tmp/a to /tmp/b 
copy # Does nothing, as it ought to 
letter "Emily, you're 'not' re-hired for the 'not' bribe ;)" 
# From: (no /tmp/a here!) 
# To: 

# Emily, you're 'not' re-hired for the 'not' bribe ;) 

मैं एक भयानक मालिक होगा ...


अभ्यास में, मेरी फ़ंक्शन नाम "कॉपी" या "पत्र" की तुलना में अधिक विस्तृत रहे हैं।

मेरी मेमोरी का सबसे हालिया उदाहरण get_input() है, जिसमें gi_no_sort और gi_prompt है।

  • gi_no_sort एक वास्तविक/झूठा मान है जो यह निर्धारित करता है कि पूरा करने के सुझावों को हल किया गया है या नहीं। सही
  • gi_prompt पर एक स्ट्रिंग है जो ... है, यह आत्म-स्पष्टीकरण है। "" के लिए डिफ़ॉल्ट।

वास्तविक तर्क समारोह लेता है इनपुट संकेत के लिए ऊपर उल्लिखित 'की पूर्णता का सुझाव' के स्रोत हैं, और जैसा कि कहा सूची समारोह में [email protected] से लिया जाता है, "नाम आर्ग" वैकल्पिक हैं [1], और उस मामले के लिए [2]; उस मामले के लिए, पूर्ण होने के रूप में एक स्ट्रिंग और बूलियन/प्रॉम्प्ट-संदेश, या वास्तव में कुछ भी अलग-अलग स्पेस से अलग करने के लिए कोई स्पष्ट तरीका नहीं है; उपर्युक्त समाधान ने मुझे परेशानी के लॉट को बचाया।

नोट:

  1. तो एक हार्ड-कोडेड shift और $1, $2, आदि सवाल से बाहर हैं।

  2. उदा। "0 Enter a command: {1..9} $(ls)"0, "Enter a command:" का एक मान है, और 1 2 3 4 5 6 7 8 9 <directory contents> का एक सेट है? या "0", "Enter", "a", और "command:" उस सेट के हिस्से भी हैं? बैश बाद वाले को मान लेगा कि आपको यह पसंद है या नहीं।

0

आपको केवल फ़ंक्शन कॉल के रास्ते पर नाम चर है।

function test() { 
    echo $a 
} 

a='hello world' test 
#prove variable didnt leak 
echo $a . 

enter image description here

इस कार्य की बस सुविधा नहीं है, तो आप अपने आप लिपि में है कि समारोह है और a='hello world' test.sh फोन और यह सिर्फ एक ही


काम करेगा सकता है थोड़ी सी मस्ती के रूप में, आप इस विधि को स्थितित्मक तर्कों के साथ जोड़ सकते हैं (कहें कि आप एक स्क्रिप्ट बना रहे थे और कुछ उपयोगकर्ता चर नामों को नहीं जानते)।
हेक, क्यों उन तर्कों के लिए इसे डिफ़ॉल्ट नहीं होने दें? खैर, आसान peasy!

function test2() { 
    [[ -n "$1" ]] && local a="$1"; [[ -z "$a" ]] && local a='hi' 
    [[ -n "$2" ]] && local b="$2"; [[ -z "$b" ]] && local b='bye' 
    echo $a $b 
} 

#see the defaults 
test2 

#use positional as usual 
test2 '' there 
#use named parameter 
a=well test2 
#mix it up 
b=one test2 nice 

#prove variables didnt leak 
echo $a $b . 

enter image description here

ध्यान दें कि यदि test अपनी ही लिपि था, तो आप local कीवर्ड का उपयोग करने की जरूरत नहीं है।