एक बैश फ़ंक्शन के लिए पैरामीटर पास करना


980

मैं खोज करने की कोशिश कर रहा हूं कि बैश फ़ंक्शन में पैरामीटर कैसे पारित करें, लेकिन जो भी आता है वह हमेशा कमांड लाइन से पैरामीटर कैसे पास करना है।

मैं अपनी स्क्रिप्ट के भीतर मापदंडों को पारित करना चाहूंगा। मैंने कोशिश की:

myBackupFunction("..", "...", "xx")

function myBackupFunction($directory, $options, $rootPassword) {
     ...
}

लेकिन वाक्यविन्यास सही नहीं है, मेरे फ़ंक्शन के लिए एक पैरामीटर कैसे पारित करें?


6
"... लेकिन क्या आता है हमेशा कमांड लाइन से पैरामीटर कैसे पास किया जाता है" - हाँ! ऐसा इसलिए है क्योंकि बैश स्क्रिप्ट मूल रूप से कमांड लाइन की सीक्वेंस हैं - एक बैश स्क्रिप्ट में एक फंक्शन को बिल्कुल वैसे ही इंवाइट करें जैसे कि यह कमांड लाइन पर कमांड था! :-) आपकी कॉल myBackupFunction ".." "..." "xx" होगी; कोई कोष्ठक नहीं, कोई अल्पविराम नहीं।
Wil

4
इस सवाल का प्रतिपक्ष: बैश फ़ंक्शन से वापसी मान
MSalters

जवाबों:


1618

फ़ंक्शन घोषित करने के दो विशिष्ट तरीके हैं। मैं दूसरे दृष्टिकोण को पसंद करता हूं।

function function_name {
   command...
} 

या

function_name () {
   command...
} 

तर्कों के साथ एक फ़ंक्शन को कॉल करने के लिए:

function_name "$arg1" "$arg2"

फ़ंक्शन उनकी स्थिति (नाम से नहीं) द्वारा पारित तर्कों को संदर्भित करता है, जो कि $ 1, $ 2, और आगे है। $ 0 ही स्क्रिप्ट का नाम है।

उदाहरण:

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

इसके घोषित होने के बाद भी, आपको अपने फ़ंक्शन को कॉल करने की आवश्यकता है।

#!/usr/bin/env sh

foo 1  # this will fail because foo has not been declared yet.

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

foo 2 # this will work.

आउटपुट:

./myScript.sh: line 2: foo: command not found
Parameter #1 is 2

संदर्भ: उन्नत बैश-स्क्रिप्टिंग गाइड


4
आप रिक्त स्थान भूल गए हैं, कोशिश करें function name() {}। हो सकता है कि इससे पहले ' {}
एन्ट्री

21
अच्छा उत्तर। मेरे पास 2 सेंट: खोल निर्माणों में है कि एक फ़ाइल में रहती हैं कि प्राप्त किया जाता है (बिंदीदार) जब जरूरत, मैं का उपयोग करना पसंद functionकीवर्ड और() । मेरा लक्ष्य (एक फ़ाइल में, कमांड लाइन नहीं) स्पष्टता को बढ़ाना है, टाइप किए गए वर्णों की संख्या को कम नहीं करना है, अर्थात function myBackupFunction() compound-statement
टेरी गार्डनर

22
@CMCDragonkai, functionकीवर्ड संस्करण एक एक्सटेंशन है; अन्य फॉर्म सभी POSIX- संगत शेल में काम करता है।
चार्ल्स डफी

8
@ टेरीगार्डनर, विचार करें कि स्पष्टता बढ़ाने के आपके प्रयास संगतता को कम कर रहे हैं।
चार्ल्स डफी

6
@ रॉनबर्क, शायद - लेकिन भले ही हम केवल स्पष्टता पर विचार करते हैं, functionकीवर्ड के पास पुराने क्श-परिवार के गोले की गारंटी थी जिसने इसे पेश किया था कि आधुनिक बैश सम्मान नहीं करते हैं (ऐसे गोले में, functionचर-स्थानीय डिफ़ॉल्ट रूप से, बश में; , ऐसा नहीं होता)। जैसे, इसके उपयोग से स्पष्टता कम हो जाती है जो किसी को जानता है, और उम्मीद कर सकता है, ksh व्यवहार। देखें wiki.bash-hackers.org/scripting/obsolete
चार्ल्स डफी

68

उच्च स्तरीय प्रोग्रामिंग भाषाओं (C / C ++ / Java / PHP / Python / Perl ...) का ज्ञान आम आदमी को सुझाव देगा कि बैश कार्यों को उन अन्य भाषाओं की तरह काम करना चाहिए। इसके बजाय , bash फ़ंक्शन शेल कमांड्स की तरह काम करते हैं और उम्मीद करते हैं कि उन्हें उसी तरह से पारित किया जाएगा, जिस तरह से एक शेल कमांड (जैसे ls -l) के लिए एक विकल्प पारित हो सकता है । वास्तव में, बैश में फ़ंक्शन तर्कों को स्थितिगत मापदंडों ( $1, $2..$9, ${10}, ${11}और इसी तरह) के रूप में माना जाता है । यह कोई आश्चर्य की बात नहीं है कि कैसे getoptsकाम करता है। कोष्ठक में फ़ंक्शन को कॉल करने के लिए कोष्ठक का उपयोग न करें।


( नोट : मैं इस समय ओपन सोलारिस पर काम कर रहा हूं।)

# bash style declaration for all you PHP/JavaScript junkies. :-)
# $1 is the directory to archive
# $2 is the name of the tar and zipped file when all is done.
function backupWebRoot ()
{
    tar -cvf - $1 | zip -n .jpg:.gif:.png $2 - 2>> $errorlog &&
        echo -e "\nTarball created!\n"
}


# sh style declaration for the purist in you. ;-)
# $1 is the directory to archive
# $2 is the name of the tar and zipped file when all is done.
backupWebRoot ()
{
    tar -cvf - $1 | zip -n .jpg:.gif:.png $2 - 2>> $errorlog &&
        echo -e "\nTarball created!\n"
}


# In the actual shell script
# $0               $1            $2

backupWebRoot ~/public/www/ webSite.tar.zip

चरों के लिए नामों का उपयोग करना चाहते हैं। बस यह करो।

declare filename=$1 # declare gives you more options and limits variable scope

एक समारोह में एक सरणी पारित करना चाहते हैं?

callingSomeFunction "${someArray[@]}" # Expands to all array elements.

फ़ंक्शन के अंदर, इस तरह के तर्कों को संभालें।

function callingSomeFunction ()
{
    for value in "$@" # You want to use "$@" here, not "$*" !!!!!
    do
        :
    done
}

एक मान और एक सरणी पास करने की आवश्यकता है, लेकिन फिर भी फ़ंक्शन के अंदर "$ @" का उपयोग करें?

function linearSearch ()
{
    declare myVar="$1"

    shift 1 # removes $1 from the parameter list

    for value in "$@" # Represents the remaining parameters.
    do
        if [[ $value == $myVar ]]
        then
            echo -e "Found it!\t... after a while."
            return 0
        fi
    done

    return 1
}

linearSearch $someStringValue "${someArray[@]}"

64

यदि आप नामित पैरामीटर पसंद करते हैं, तो यह संभव है (कुछ ट्रिक्स के साथ) वास्तव में नामित मापदंडों को फ़ंक्शन के लिए पास करें (यह भी सरणियों और संदर्भों को पारित करना संभव बनाता है)।

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

function example { args : string firstName , string lastName , integer age } {
  echo "My name is ${firstName} ${lastName} and I am ${age} years old."
}

आप तर्कों को @required या @readonly के रूप में भी एनोटेट कर सकते हैं, बना सकते हैं ... बाकी तर्कों, अनुक्रमिक तर्कों (उदाहरण के लिए string[4]) से सरणियों का निर्माण करें और वैकल्पिक रूप से कई पंक्तियों में तर्कों को सूचीबद्ध करें:

function example {
  args
    : @required string firstName
    : string lastName
    : integer age
    : string[] ...favoriteHobbies

  echo "My name is ${firstName} ${lastName} and I am ${age} years old."
  echo "My favorite hobbies include: ${favoriteHobbies[*]}"
}

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

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

shopt -s expand_aliases

function assignTrap {
  local evalString
  local -i paramIndex=${__paramIndex-0}
  local initialCommand="${1-}"

  if [[ "$initialCommand" != ":" ]]
  then
    echo "trap - DEBUG; eval \"${__previousTrap}\"; unset __previousTrap; unset __paramIndex;"
    return
  fi

  while [[ "${1-}" == "," || "${1-}" == "${initialCommand}" ]] || [[ "${#@}" -gt 0 && "$paramIndex" -eq 0 ]]
  do
    shift # first colon ":" or next parameter's comma ","
    paramIndex+=1
    local -a decorators=()
    while [[ "${1-}" == "@"* ]]
    do
      decorators+=( "$1" )
      shift
    done

    local declaration=
    local wrapLeft='"'
    local wrapRight='"'
    local nextType="$1"
    local length=1

    case ${nextType} in
      string | boolean) declaration="local " ;;
      integer) declaration="local -i" ;;
      reference) declaration="local -n" ;;
      arrayDeclaration) declaration="local -a"; wrapLeft= ; wrapRight= ;;
      assocDeclaration) declaration="local -A"; wrapLeft= ; wrapRight= ;;
      "string["*"]") declaration="local -a"; length="${nextType//[a-z\[\]]}" ;;
      "integer["*"]") declaration="local -ai"; length="${nextType//[a-z\[\]]}" ;;
    esac

    if [[ "${declaration}" != "" ]]
    then
      shift
      local nextName="$1"

      for decorator in "${decorators[@]}"
      do
        case ${decorator} in
          @readonly) declaration+="r" ;;
          @required) evalString+="[[ ! -z \$${paramIndex} ]] || echo \"Parameter '$nextName' ($nextType) is marked as required by '${FUNCNAME[1]}' function.\"; " >&2 ;;
          @global) declaration+="g" ;;
        esac
      done

      local paramRange="$paramIndex"

      if [[ -z "$length" ]]
      then
        # ...rest
        paramRange="{@:$paramIndex}"
        # trim leading ...
        nextName="${nextName//\./}"
        if [[ "${#@}" -gt 1 ]]
        then
          echo "Unexpected arguments after a rest array ($nextName) in '${FUNCNAME[1]}' function." >&2
        fi
      elif [[ "$length" -gt 1 ]]
      then
        paramRange="{@:$paramIndex:$length}"
        paramIndex+=$((length - 1))
      fi

      evalString+="${declaration} ${nextName}=${wrapLeft}\$${paramRange}${wrapRight}; "

      # continue to the next param:
      shift
    fi
  done
  echo "${evalString} local -i __paramIndex=${paramIndex};"
}

alias args='local __previousTrap=$(trap -p DEBUG); trap "eval \"\$(assignTrap \$BASH_COMMAND)\";" DEBUG;'

क्या हैं @var, @reference, @paramsचर? इस बारे में अधिक जानने के लिए मुझे इंटरनेट पर क्या देखना चाहिए?
जिप्सी कोस्मोनॉट

3
बहुत बढ़िया जवाब! मैंने बस बैश इन्फिनिटी पर शोध किया और ऐसा लग रहा है कि यह वास्तव में मददगार होगा। धन्यवाद!
जोनाथन हॉल्ट

धन्यवाद @JonathanHult! मैंने हाल ही में अपने उपरोक्त उत्तर को हाल ही में अपडेट किया है, और यह अब एक नया, फिर से लिखा गया है जो कि बैश इन्फिनिटी 2.0 में वर्तमान में एक बार कोड को हटा दिया गया है। मैं इसे फिर से लिखने का कारण पुराने कार्यान्वयन में एक बग के कारण है (यह GitHub पर मुद्दों में है)। बैश इन्फिनिटी में अभी तक नए संस्करण को एकीकृत करने का समय नहीं था। सुनकर खुशी हुई कि यह मददगार रहा है।
niieani

हाय @niieani जब मैं अपने जवाब में आपके द्वारा उपयोग किए जाने वाले फॉर्म में बैश फंक्शन बनाने की कोशिश करता हूं, तो यह बताता है कि मुझे एप्ट से यूटर्न बर्तनों को स्थापित करने की आवश्यकता है। क्या यह आपकी बैश स्क्रिप्ट काम करती है? क्या मैं यह सही ढंग से कर रहा हूं? अगर मैं आपको या किसी और को मूल रूप से बैश के विस्तार के लिए अनुमति देने के लिए असामान्य उपयोग कार्यक्रम बनाया, सही समझा?
डेविड ए। फ्रेंच

@ DavidA.French नहीं, ऐसा नहीं होना चाहिए। ucommonमेरा और मेरे कोड का कोई संबंध नहीं है । यह संभव है कि आपके पास कुछ टूल इंस्टॉल किया गया हो, जो आपके द्वारा बताई गई समस्या का कारण हो, पता नहीं यह क्या हो सकता है।
niieani

27

Parens और अल्पविराम से बाहर याद आती है:

 myBackupFunction ".." "..." "xx"

और फ़ंक्शन इस तरह दिखना चाहिए:

function myBackupFunction() {
   # here $1 is the first parameter, $2 the second etc.
}

8

मुझे उम्मीद है कि यह उदाहरण आपकी मदद कर सकता है। यह उपयोगकर्ता से दो नंबर लेता है, उन्हें फ़ंक्शन add(कोड की बहुत अंतिम पंक्ति) में खिलाता है , और addउन्हें योग और प्रिंट करेगा।

#!/bin/bash

read -p "Enter the first  value: " x
read -p "Enter the second value: " y

add(){
    arg1=$1 #arg1 gets to be the first  assigned argument (note there are no spaces)
    arg2=$2 #arg2 gets to be the second assigned argument (note there are no spaces)

    echo $(($arg1 + $arg2))
}

add x y #feeding the arguments

6
उस तरीके से नाम से गुजरना केवल पूर्णांक के लिए काम करता है जो संख्यात्मक ऑपरेटर (()) में पारित हो जाता है, और यह केवल इसलिए काम करता है क्योंकि संख्यात्मक ऑपरेटर पुनरावर्ती मानों में परिवर्तन को हल करता है। यदि आप इसका मतलब निकालना चाहते हैं, तो x के लिए '5' और फिर 'x' में प्रवेश करने का प्रयास करें और आप देखेंगे कि यह जोड़ता है (x + y) = (5 + x) = (5 + 5) = 10. अन्य सभी उपयोग मामलों के लिए आपका उदाहरण विफल हो जाएगा। इसके बजाय आपको जेनेरिक कोड के लिए '' '$ x' '' '$ y' 'का उपयोग करना चाहिए।
Wil

6

एक साधारण उदाहरण जो किसी फ़ंक्शन को कॉल करते समय स्क्रिप्ट या स्क्रिप्ट के अंदर निष्पादित करने के दौरान दोनों को स्पष्ट करेगा।

#!/bin/bash
echo "parameterized function example"
function print_param_value(){
    value1="${1}" # $1 represent first argument
    value2="${2}" # $2 represent second argument
    echo "param 1 is  ${value1}" #as string
    echo "param 2 is ${value2}"
    sum=$(($value1+$value2)) #process them as number
    echo "The sum of two value is ${sum}"
}
print_param_value "6" "4" #space sparted value
#you can also pass paramter durign executing script
print_param_value "$1" "$2" #parameter $1 and $2 during executing

#suppose our script name is param_example
# call like this 
# ./param_example 5 5
# now the param will be $1=5 and $2=5

5

सोचा था कि मैं एक और तरीके के उल्लेख के साथ नामांकित करने के लिए नामांकित मापदंडों को पारित करने के लिए पाइप करूंगा ... संदर्भ से गुजर रहा है। यह bash 4.0 के रूप में समर्थित है

#!/bin/bash
function myBackupFunction(){ # directory options destination filename
local directory="$1" options="$2" destination="$3" filename="$4";
  echo "tar cz ${!options} ${!directory} | ssh root@backupserver \"cat > /mnt/${!destination}/${!filename}.tgz\"";
}

declare -A backup=([directory]=".." [options]="..." [destination]="backups" [filename]="backup" );

myBackupFunction backup[directory] backup[options] backup[destination] backup[filename];

बैश 4.3 के लिए एक वैकल्पिक वाक्यविन्यास एक nameref का उपयोग कर रहा है

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


"पाइप में"। मैंने देखा तुमने वहॉं क्या किया है!
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.