Übergabe von Parametern an eine Bash-Funktion

980

Ich versuche zu suchen, wie Parameter in einer Bash-Funktion übergeben werden, aber es wird immer angezeigt, wie Parameter über die Befehlszeile übergeben werden.

Ich möchte Parameter in meinem Skript übergeben. Ich habe es versucht:

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

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

Aber die Syntax ist nicht korrekt. Wie übergebe ich einen Parameter an meine Funktion?

stivlo
quelle
6
"... aber es kommt immer vor, wie man Parameter von der Kommandozeile übergibt" - Ja! Das liegt daran, dass Bash-Skripte im Grunde genommen Sequenzen von Befehlszeilen sind - rufen Sie eine Funktion in einem Bash-Skript genau so auf, als wäre es ein Befehl in der Befehlszeile! :-) Ihr Anruf wäre myBackupFunction ".." "..." "xx"; Keine Klammern, keine Kommas.
Wil
4
Das Gegenstück zu dieser Frage: Rückgabewert von einer Bash-Funktion
MSalters

Antworten:

1618

Es gibt zwei typische Möglichkeiten, eine Funktion zu deklarieren. Ich bevorzuge den zweiten Ansatz.

function function_name {
   command...
} 

oder

function_name () {
   command...
} 

So rufen Sie eine Funktion mit Argumenten auf:

function_name "$arg1" "$arg2"

Die Funktion bezieht sich auf übergebene Argumente nach ihrer Position (nicht nach Namen), dh $ 1, $ 2 usw. $ 0 ist der Name des Skripts.

Beispiel:

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

Außerdem müssen Sie Ihre Funktion aufrufen, nachdem sie deklariert wurde.

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

Ausgabe:

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

Referenz: Advanced Bash-Scripting Guide .

Dogbane
quelle
4
Sie haben die Leerzeichen vergessen, versuchen Sie es function name() {}. Vielleicht mit einem 'Enter' vor{}
lalo
21
Gute Antwort. Meine 2 Cent: In Shell-Konstrukten, die sich in einer Datei befinden, die bei Bedarf bezogen (gepunktet) ist, bevorzuge ich die Verwendung des functionSchlüsselworts und des (). Mein Ziel (in einer Datei, nicht in der Befehlszeile) ist es, die Übersichtlichkeit zu erhöhen und nicht die Anzahl der eingegebenen Zeichen zu verringern function myBackupFunction() compound-statement.
Terry Gardner
22
@CMCDragonkai, die functionKeyword-Version ist eine Erweiterung; Die andere Form funktioniert in allen POSIX-kompatiblen Shells.
Charles Duffy
8
@TerryGardner, denken Sie daran, dass Ihre Versuche, die Klarheit zu erhöhen, die Kompatibilität verringern.
Charles Duffy
6
@RonBurk vielleicht - aber selbst wenn wir nur Klarheit betrachten, hatte das functionSchlüsselwort Garantien in den alten Shells der ksh-Familie, die es einführten, dass moderne Bash nicht ehren (in solchen Shells wurden functionVariablen standardmäßig lokal gemacht; in Bash , Es tut nicht). Daher verringert seine Verwendung die Klarheit für jeden, der das ksh-Verhalten kennt und erwarten könnte. Siehe wiki.bash-hackers.org/scripting/obsolete
Charles Duffy
69

Kenntnisse in Programmiersprachen auf hoher Ebene (C / C ++ / Java / PHP / Python / Perl ...) legen dem Laien nahe, dass Bash-Funktionen wie in diesen anderen Sprachen funktionieren sollten. Stattdessen funktionieren Bash-Funktionen wie Shell-Befehle und erwarten, dass Argumente an sie übergeben werden, so wie man eine Option an einen Shell-Befehl übergeben könnte (z ls -l. B. ). Effektiv Funktionsargumente in bash behandelt werden als Positionsparameter ( $1, $2..$9, ${10}, ${11}und so weiter). Dies ist keine Überraschung, wenn man bedenkt, wie es getoptsfunktioniert. Verwenden Sie keine Klammern, um eine Funktion in bash aufzurufen.


( Hinweis : Ich arbeite gerade an Open Solaris.)

# 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

Möchten Sie Namen für Variablen verwenden. Mach das einfach.

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

Möchten Sie ein Array an eine Funktion übergeben?

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

Behandeln Sie die Argumente innerhalb der Funktion wie folgt.

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

Müssen Sie einen Wert und ein Array übergeben, aber trotzdem "$ @" in der Funktion verwenden?

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[@]}"
Anthony Rutledge
quelle
64

Wenn Sie benannte Parameter bevorzugen, ist es möglich (mit ein paar Tricks), benannte Parameter tatsächlich an Funktionen zu übergeben (ermöglicht auch das Übergeben von Arrays und Referenzen).

Mit der von mir entwickelten Methode können Sie benannte Parameter definieren, die an eine Funktion wie die folgende übergeben werden:

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

Sie können Argumente auch als @required oder @readonly mit Anmerkungen versehen, ... rest-Argumente erstellen, Arrays aus sequentiellen Argumenten erstellen (z. B. string[4]) und die Argumente optional in mehreren Zeilen auflisten :

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

Mit anderen Worten, Sie können Ihre Parameter nicht nur bei ihren Namen aufrufen (was einen besser lesbaren Kern ausmacht), sondern auch Arrays übergeben (und Verweise auf Variablen - diese Funktion funktioniert jedoch nur in Bash 4.3)! Außerdem befinden sich die zugeordneten Variablen alle im lokalen Bereich, genau wie $ 1 (und andere).

Der Code, mit dem dies funktioniert, ist ziemlich leicht und funktioniert sowohl in Bash 3 als auch in Bash 4 (dies sind die einzigen Versionen, mit denen ich es getestet habe). Wenn Sie an weiteren Tricks wie diesen interessiert sind, die das Entwickeln mit Bash viel schöner und einfacher machen, können Sie sich mein Bash Infinity Framework ansehen. Der folgende Code ist als eine seiner Funktionen verfügbar.

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;'
niieani
quelle
Was sind die @var, @reference, @paramsVariablen? Was sollte ich im Internet nachschlagen, um mehr darüber zu erfahren?
GypsyCosmonaut
3
Gute Antwort! Ich habe gerade Bash Infinity recherchiert und es sieht so aus, als ob es wirklich hilfreich sein wird. Vielen Dank!
Jonathan Hult
Danke @JonathanHult! Ich habe meine obige Antwort kürzlich aktualisiert und sie ist jetzt eine neuere, neu geschriebene Version des Codes, die derzeit in Bash Infinity 2.0 enthalten ist. Der Grund, warum ich es umgeschrieben habe, ist ein Fehler in der alten Implementierung (es liegt in den Problemen auf GitHub). Ich hatte noch keine Zeit, die neue Version wieder in Bash Infinity zu integrieren. Ich bin froh zu hören, dass es hilfreich war.
Niieani
Hallo @niieani, wenn ich versuche, eine Bash-Funktion in der Form zu erstellen, die Sie in Ihrer Antwort verwenden, sagt es mir, dass ich ucommon utils von apt installieren muss. Funktioniert Ihr Bash-Skript so? Mache ich das richtig Wenn ich verstehe, dass Sie oder jemand anderes im Grunde das Programm ucommon util erstellt hat, um eine Erweiterung von Bash zu ermöglichen, richtig?
David A. French
@ DavidA.Französisch nein, das sollte nicht passieren. Es gibt keine Beziehung zwischen ucommonund meinem Code. Möglicherweise haben Sie ein Tool installiert, das das von Ihnen erwähnte Problem verursacht. Sie wissen nicht, woran es liegen könnte.
Niieani
27

Verpassen Sie die Parens und Kommas:

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

und die Funktion sollte so aussehen:

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

quelle
8

Ich hoffe, dieses Beispiel kann Ihnen helfen. Der Benutzer nimmt zwei Zahlen entgegen, gibt sie an die aufgerufene Funktion add(in der allerletzten Zeile des Codes) weiter und addfasst sie zusammen und druckt sie aus.

#!/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
Milad P.
quelle
6
Die Übergabe des Namens auf diese Weise funktioniert nur für Ganzzahlen, die an den numerischen Operator (()) übergeben werden, und nur, weil der numerische Operator Zeichenfolgen rekursiv in Werte auflöst. Wenn Sie testen möchten, was ich meine, geben Sie '5' für x und dann 'x' für y ein und Sie werden sehen, dass (x + y) = (5 + x) = (5 + 5) hinzugefügt wird. = 10. In allen anderen Anwendungsfällen schlägt Ihr Beispiel fehl. Stattdessen sollten Sie 'add "$ x" "$ y"' für generischen Code verwenden.
Wil
6

Ein einfaches Beispiel, das sowohl beim Ausführen eines Skripts als auch innerhalb des Skripts beim Aufrufen einer Funktion gelöscht wird.

#!/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
Adiii
quelle
5

Ich dachte, ich würde mich mit der Erwähnung einer anderen Möglichkeit befassen, benannte Parameter an bash zu übergeben ... als Referenz übergeben. Dies wird ab Bash 4.0 unterstützt

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

Eine alternative Syntax für Bash 4.3 ist die Verwendung eines Namens

Obwohl das nameref viel praktischer ist, da es nahtlos dereferenziert, liefern einige ältere unterstützte Distributionen immer noch eine ältere Version, so dass ich es noch nicht ganz empfehlen werde.

Wil
quelle
"Pipe in". Ich sehe, was du dort getan hast!
Jacktose