Parametreleri Bash işlevine geçirme


980

Ben bir Bash işlevinde parametreleri geçirmek için nasıl çalışıyorum, ama ne geliyor her zaman nasıl komut satırından parametre geçmek olduğunu.

Komut dosyamdaki parametreleri iletmek istiyorum. Denedim:

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

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

Ancak sözdizimi doğru değil, işlevime bir parametre nasıl iletilir?


6
"... ama ortaya çıkan her zaman parametre komut satırından nasıl geçirilir" - Evet! Bunun nedeni, Bash komut dosyalarının temelde komut satırları dizisidir - Bash komut dosyasındaki bir işlevi tam olarak komut satırında bir komutmuş gibi çağırır! :-) Aramanız myBackupFunction ".." "..." "xx" olacaktır; parantez yok, virgül yok.
Wil

4
Bu sorunun karşılığı: bash işlevinden dönüş değeri
MSalters 17:18

Yanıtlar:


1618

Bir işlevi bildirmenin iki tipik yolu vardır. İkinci yaklaşımı tercih ederim.

function function_name {
   command...
} 

veya

function_name () {
   command...
} 

Bağımsız değişkenleri olan bir işlevi çağırmak için:

function_name "$arg1" "$arg2"

İşlev, geçilen bağımsız değişkenleri konumlarına göre (ada göre değil), yani $ 1, $ 2 vb. Anlamına gelir. $ 0 , komut dosyasının kendisinin adıdır.

Misal:

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

Ayrıca, bildirildikten sonra işlevinizi çağırmanız gerekir .

#!/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.

Çıktı:

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

Başvuru: Gelişmiş Bash-Komut Dosyası Kılavuzu .


4
Alanları unuttun, dene function name() {}. Belki daha önce bir 'enter' ile{}
lalo

21
İyi cevap. Benim 2 sent: kabuk yapılar içinde olduğu kaynaklı bir dosyada ikamet (noktalı) gerektiğinde, ben kullanmayı tercih functionanahtar kelimeyi ve() . Amacım (bir dosyada, komut satırında değil) netliği artırmak, yazılan karakter sayısını azaltmak değil, viz function myBackupFunction() compound-statement,.
Terry Gardner

22
@CMCDragonkai, functionanahtar kelime sürümü bir uzantıdır; diğer form POSIX uyumlu tüm mermilerde çalışır.
Charles Duffy

8
@TerryGardner, netliği artırma girişimlerinizin uyumluluğu azalttığını düşünün.
Charles Duffy

6
@RonBurk, belki de - ama sadece netliği göz önünde bulundursak bile, functionanahtar kelimenin, modern bash'ın onurlandırmadığını (bu tür kabuklarda, functionvarsayılan olarak yerel olarak; , o değil). Bu nedenle, kullanımı ksh davranışını bilen ve bekleyebilecek herkese netliği azaltır . Bkz. Wiki.bash-hackers.org/scripting/obsolete
Charles Duffy

69

Üst düzey programlama dilleri (C / C ++ / Java / PHP / Python / Perl ...) bilgisi, layman'a bash işlevlerinin diğer dillerde olduğu gibi çalışması gerektiğini önerecektir. Bunun yerine , bash işlevleri shell komutları gibi çalışır ve argümanların kendilerine aynı şekilde iletilmesini bekler, bir seçenek bir shell komutuna (örneğin ls -l) geçebilir . Aslında, işlev bağımsız değişkenleri bash içindeki konumsal parametreler ( $1, $2..$9, ${10}, ${11}ve benzeri) olarak ele alınır. Nasıl getoptsçalıştığını düşünmek sürpriz değil . Bash işlevini çağırmak için parantez kullanmayın.


( Not : Şu anda Açık Solaris üzerinde çalışıyorum.)

# 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

Değişkenler için ad kullanmak istiyorum. Sadece bunu yap.

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

Bir diziyi bir işleve geçirmek ister misiniz?

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

Fonksiyonun içinde, bu gibi argümanları ele alın.

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

Bir değer ve dizi geçirmeniz gerekiyor, ancak yine de işlevin içinde "$ @" kullanılmalı mı?

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

Adlandırılmış parametreleri tercih ederseniz, (birkaç hile ile) adlandırılmış parametreleri fonksiyonlara geçirmek mümkündür (ayrıca dizileri ve referansları geçirmeyi de mümkün kılar).

Geliştirdiğim yöntem, böyle bir işleve geçirilen adlandırılmış parametreleri tanımlamanızı sağlar:

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

Ayrıca, bağımsız değişkenlere @reired veya @readonly olarak ek açıklama ekleyebilir, ... rest bağımsız değişkenler oluşturabilir, sıralı bağımsız değişkenlerden diziler oluşturabilir (örn. Kullanarak string[4]) ve isteğe bağlı olarak bağımsız değişkenleri birden çok satırda listeleyebilirsiniz:

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[*]}"
}

Başka bir deyişle, parametrelerinizi isimlerine göre (daha okunabilir bir çekirdeği oluşturan) çağıramazsınız, aslında dizileri de geçirebilirsiniz (ve değişkenlere referanslar - bu özellik sadece bash 4.3'te çalışır)! Ayrıca, eşlenen değişkenlerin tümü yerel kapsamda, tıpkı 1 $ (ve diğerleri) gibi.

Bu işi yapan kod oldukça hafif ve hem bash 3 hem de bash 4'te çalışıyor (bunlar test ettiğim tek sürümler). Eğer bash ile geliştirmeyi daha güzel ve kolay hale getiren daha fazla püf noktasıyla ilgileniyorsanız, Bash Infinity Framework'üme göz atabilirsiniz , aşağıdaki kod işlevlerinden biri olarak mevcuttur.

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;'

Ne @var, @reference, @paramsdeğişkenler? Bununla ilgili daha fazla bilgi edinmek için internette nelere bakmalıyım?
Cosmonaut

3
Mükemmel cevap! Bash Infinity'yi araştırdım ve gerçekten yararlı olacak gibi görünüyor. Teşekkürler!
Jonathan Hult

Teşekkürler @JonathanHult! Aslında yukarıdaki cevabımı son zamanlarda güncelledim ve şimdi kodun şu anda Bash Infinity 2.0'dakine daha yeni, yeniden yazılmış bir pasajı. Yeniden yazmamın nedeni eski uygulamadaki bir hatadan kaynaklanıyor (GitHub'daki sorunlarda). Yeni sürümü henüz Bash Infinity'a entegre etmek için zamanım olmadı. Yararlı olduğunu duymak sevindim.
niieani

Merhaba @niieani Cevabınızda kullandığınız formda bir bash işlevi oluşturmaya çalıştığınızda bana apt ucommon utils yüklemeniz gerektiğini söylüyor. Bh betiğiniz böyle mi çalışıyor? Bunu doğru mu yapıyorum? Sizi veya başka birini temelde Bash'in uzatılmasına izin vermek için ucommon util programını oluşturduysam, doğru mu?
David A. Fransız

@ DavidA.Fransız hayır, bu olmamalı. Kodum ile arasında hiçbir ilişki yok ucommon. Bahsettiğiniz soruna neden olan bazı araçların yüklü olması mümkündür, ne olabileceği hakkında hiçbir fikriniz yoktur.
niieani

27

Parens ve virgülleri kaçırmayın:

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

ve işlev şöyle görünmelidir:

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

8

Umarım bu örnek size yardımcı olabilir. Kullanıcıdan iki sayı alır, bunları add(kodun en son satırında) adlı işleve aktarır ve addtoplar ve yazdırır.

#!/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
Ada göre bu şekilde geçmek yalnızca sayısal işleç (()) içine iletilen tamsayılar için çalışır ve yalnızca sayısal işleç dizeleri değerlere özyinelemeli olarak çalışır. Ne demek istediğimi test etmek isterseniz, x için '5' ve ardından y için 'x' girmeyi deneyin; eklediğini görürsünüz (x + y) = (5 + x) = (5 + 5) = 10. Diğer tüm kullanım durumlarında örneğiniz başarısız olur. Bunun yerine, genel kod için 'add "$ x" "$ y"' kullanmalısınız.
Wil

6

Bir işlev çağrılırken hem komut dosyası yürütülürken hem de komut dosyasının içinde temizlenecek basit bir örnek.

#!/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

Referans olarak geçerek adlandırılmış parametreleri bash'a geçirmenin başka bir yolundan bahsetmiştim. Bu, bash 4.0'dan itibaren desteklenir

#!/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];

Bash 4.3 için alternatif bir sözdizimi, aderef

Nameref sorunsuz bir şekilde dereferences olması açısından çok daha uygun olsa da, bazı eski desteklenen dağıtımlar hala eski bir sürüm gönderir, bu yüzden henüz tavsiye etmeyeceğim.


“Boru girişi”. Orada ne yaptığınızı görüyorum!
Jacktose
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.