将参数传递给Bash函数

sti*_*vlo 900 bash

我试图搜索如何在Bash函数中传递参数,但是出现的是如何从命令行传递参数.

我想在我的脚本中传递参数.我试过了:

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

function myBackupFunction($directory, $options, $rootPassword) {
     ...
}
Run Code Online (Sandbox Code Playgroud)

但是语法不正确,如何将参数传递给我的函数?

dog*_*ane 1503

声明函数有两种典型方法.我更喜欢第二种方法.

function function_name {
   command...
} 
Run Code Online (Sandbox Code Playgroud)

要么

function_name () {
   command...
} 
Run Code Online (Sandbox Code Playgroud)

要使用参数调用函数:

function_name "$arg1" "$arg2"
Run Code Online (Sandbox Code Playgroud)

该函数引用通过其位置(而不是名称)传递的参数,即$ 1,$ 2等等.$ 0是脚本本身的名称.

例:

function_name () {
   echo "Parameter #1 is $1"
}
Run Code Online (Sandbox Code Playgroud)

此外,您需要声明调用您的函数.

#!/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.
Run Code Online (Sandbox Code Playgroud)

输出:

./myScript.sh: line 2: foo: command not found
Parameter #1 is 2
Run Code Online (Sandbox Code Playgroud)

参考:Advanced Bash-Scripting Guide.

  • 好答案.我的2美分:在shell构造中,它们驻留在需要的源代码(虚线)的文件中,我更喜欢使用`function`关键字__and__`()`.我的目标(在文件中,而不是命令行)是为了提高清晰度,而不是减少键入的字符数,即`function myBackupFunction()compound-statement`. (15认同)
  • @CMCDragonkai,`function`关键字版本是扩展名; 另一种形式适用于所有符合POSIX标准的shell. (15认同)
  • @TerryGardner,考虑到你提高清晰度的尝试会降低兼容​​性. (8认同)
  • @RonBurk,也许 - 但是即使我们只考虑清晰度,`function`关键字在旧的ksh-family shell中有保证,它引入了现代bash不尊重(在这样的shell中,`function`使变量本地 - 默认情况下;在bash中,它没有).因此,对于知道并且可能期望ksh行为的任何人来说,它的使用*会降低*清晰度.请参阅http://wiki.bash-hackers.org/scripting/obsolete (5认同)
  • 你忘记了空格,试试`function name(){}`.也许在`{}`之前使用'enter' (4认同)
  • 当我写`function name(){}时,我得到一个关于'(''的错误.但是当我写`name(){}时,它就有效.有什么想法吗? (2认同)
  • 当然,如果您真的限制自己,您可以使您的脚本与 MS-DOS COMMAND.EXE 兼容。也许兼容性仅与情况相关。 (2认同)

Ant*_*dge 59

对高级编程语言(C/C++/Java/PHP/Python/Perl ...)的了解会向外行人建议bash函数应该像在其他语言中那样工作.相反,bash函数就像shell命令一样工作,并期望将参数传递给它们,就像将命令传递给shell命令(ls -l)一样.实际上,bash中的函数参数被视为位置参数($1, $2..$9, ${10}, ${11}等等).考虑到getopts工作原理,这并不奇怪.在bash中调用函数不需要括号.


(注意:我目前正在开发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
Run Code Online (Sandbox Code Playgroud)

  • @iomv 尽管如此,请务必小心“循环变量引用”问题。无论您在函数内部将数组声明为什么名称,都不要在调用上下文/客户端代码中将数组参数命名为相同的名称。请注意我如何更改最后一个示例以帮助人们避免“循环名称引用”问题。很好的决定,尽管你自己犯了一个错误。:-) (3认同)

nii*_*ani 44

如果您更喜欢命名参数,则可以(通过一些技巧)将命名参数实际传递给函数(也可以传递数组和引用).

我开发的方法允许您定义传递给函数的命名参数,如下所示:

function example { args : string firstName , string lastName , integer age } {
  echo "My name is ${firstName} ${lastName} and I am ${age} years old."
}
Run Code Online (Sandbox Code Playgroud)

您还可以将参数注释为@required或@readonly,创建... rest参数,从顺序参数创建数组(使用eg 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[*]}"
}
Run Code Online (Sandbox Code Playgroud)

换句话说,您不仅可以通过名称调用参数(这样可以构成更具可读性的核心),您实际上可以传递数组(以及对变量的引用 - 这个功能仅适用于bash 4.3)!另外,映射变量都在本地范围内,就像$ 1(和其他)一样.

使这项工作的代码非常简单,可以在bash 3和bash 4中运行(这些是我用它测试过的唯一版本).如果你对这样的更多技巧感兴趣,那么使用bash开发更好更容易,你可以看一下我的Bash Infinity Framework,下面的代码可以作为其功能之一.

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;'
Run Code Online (Sandbox Code Playgroud)

  • 好答案!我刚刚研究了Bash Infinity,它看起来真的很有帮助。谢谢! (3认同)

小智 26

错过了parens和逗号:

 myBackupFunction ".." "..." "xx"
Run Code Online (Sandbox Code Playgroud)

并且函数应如下所示:

function myBackupFunction() {
   # here $1 is the first parameter, $2 the second etc.
}
Run Code Online (Sandbox Code Playgroud)


Adi*_*iii 10

一个简单的例子,它将在执行脚本期间或在调用函数时清除脚本。

#!/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-separated value
# You can also pass parameters during executing the script
print_param_value "$1" "$2" # Parameter $1 and $2 during execution

# Suppose our script name is "param_example".
# Call it like this:
#
# ./param_example 5 5
#
# Now the parameters will be $1=5 and $2=5
Run Code Online (Sandbox Code Playgroud)


Mil*_* P. 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
Run Code Online (Sandbox Code Playgroud)

  • 以这种方式按名称传递仅适用于传递给数字运算符(())的整数,并且它只能起作用,因为数字运算符以递归方式将字符串解析为值.如果您想测试我的意思,请尝试输入'5'代表x,然后输入'x'代表y,你会看到它添加(x + y)=(5 + x)=(5 + 5) = 10.对于所有其他用例,您的示例将失败.相反,你应该使用'add"$ x""$ y"'作为通用代码. (6认同)

Wil*_*Wil 7

另一种将命名参数传递给 Bash 的方法是通过引用传递。从 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];
Run Code Online (Sandbox Code Playgroud)

Bash 4.3 的另一种语法是使用nameref

尽管 nameref 更方便,因为它可以无缝地取消引用,但一些受支持的较旧发行版仍然提供旧版本,因此我还不会推荐它。