将参数传递给 Bash 函数

Passing parameters to a Bash function

提问人:stivlo 提问时间:6/2/2011 最后编辑:Vadim Kotovstivlo 更新时间:9/28/2023 访问量:1770431

问:

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

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

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

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

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

bash 函数 参数

评论

13赞 Wil 10/20/2017
"...但出现的总是如何从命令行传递参数“ - 是的!这是因为 Bash 脚本基本上是命令行的序列 - 在 Bash 脚本中调用函数,就像在命令行上调用命令一样!:-)您的调用将是 myBackupFunction “..”"..."“xx”;没有括号,没有逗号。
5赞 MSalters 9/17/2018
与此问题的对应物:从 bash 函数返回值
0赞 Gabriel Staples 1/26/2022
Смотритетакже: 在 bash 中将数组作为参数传递

答:

55赞 user2100815 6/2/2011 #1

去掉括号和逗号:

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

函数应如下所示:

function myBackupFunction() {
    # Here $1 is the first parameter, $2 the second, etc.
}
2243赞 dogbane 6/2/2011 #2

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

function function_name {
   command...
} 

function_name () {
   command...
} 

调用带有参数的函数:

function_name "$arg1" "$arg2"

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

例:

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

参考:高级 Bash 脚本指南

评论

6赞 Leandro 11/11/2013
你已经忘记了空格,试试.也许之前有一个“输入”function name() {}{}
34赞 Terry Gardner 11/28/2013
好答案。我的 2 美分:在需要时驻留在来源(带点)文件中的 shell 结构中,我更喜欢使用关键字 .我的目标是(在文件中,而不是命令行中)是提高清晰度,而不是减少键入的字符数,即 .function()function myBackupFunction() compound-statement
38赞 Charles Duffy 5/5/2015
@CMCDragonkai,关键字版本是扩展名;另一种形式适用于所有符合 POSIX 的 shell。function
21赞 Charles Duffy 5/5/2015
@TerryGardner,请考虑您试图提高清晰度正在降低兼容性。
12赞 Charles Duffy 3/1/2018
@RonBurk,也许 - 但即使我们只考虑清晰度,该关键字在引入它的旧 ksh 系列 shell 中也有现代 bash 不遵守的保证(在这样的 shell 中,默认使变量为本地;在 Bash 中,它没有)。因此,它的使用会降低任何知道并可能期望 ksh 行为的人的清晰度。查看 wiki.bash-hackers.org/scripting/obsoletefunctionfunction
171赞 Anthony Rutledge 5/11/2014 #3

高级编程语言(C/C++、Java、PHP、Python、Perl 等)的知识会向外行建议 Bourne Again Shell (Bash) 函数应该像在其他语言中一样工作。

相反,Bash 函数的工作方式类似于 shell 命令,并期望将参数传递给它们,就像将选项传递给 shell 命令一样(例如 )。实际上,Bash 中的函数参数被视为位置参数(,依此类推)。考虑到工作原理,这并不奇怪。不要使用括号在 Bash 中调用函数。ls -l$1, $2..$9, ${10}, ${11}getopts


(注意:我目前正好在OpenSolaris上工作。

# 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

想要为变量使用名称?只是做点什么。

local filename=$1 # The keyword declare can be used, but local is semantically more specific.

不过要小心。如果函数的参数中有一个空格,您可能希望这样做!否则,可能不是你想象的那样。$1

local filename="$1" # Just to be on the safe side. Although, if $1 was an integer, then what? Is that even possible? Humm.

想要按值将数组传递给函数吗?

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

在函数内部,像这样处理参数。

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

需要传递一个值和一个数组,但仍然在函数中使用“$@”?

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

    shift 1 # Removes $1 from the parameter list

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

    return 1
}

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

在 Bash 4.3 及更高版本中,您可以通过使用 option 定义函数的参数,通过引用将数组传递给函数。-n

function callingSomeFunction ()
{
    local -n someArray=$1 # also ${1:?} to make the parameter mandatory.

    for value in "${someArray[@]}"; do # Nice!
        :
    done
}

callingSomeFunction myArray # No $ in front of the argument. You pass by name, not expansion / value.

评论

0赞 iomv 11/26/2021
据我所知,发布的最后一个示例不起作用。我试图在 bash v5+ 上运行它,它只是在循环中返回给我完整的数组,而不是每个项目
1赞 iomv 11/26/2021
再次测试后,我发现这是我的错误,因为我在行中声明了数组,而不是之前声明了它
3赞 Anthony Rutledge 11/26/2021
@iomv 尽管如此,还是要小心“循环变量引用”问题。无论您在函数中将数组声明为什么名称,都不要在调用上下文/客户端代码中将数组参数命名为相同的名称。请注意我如何更改最后一个示例以帮助人们避免“循环名称引用”问题。好电话,即使你自己犯了一个错误。:-)
84赞 niieani 5/4/2015 #4

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

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

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

您还可以将参数注释为@required或@readonly,创建...REST 参数,从顺序参数创建数组(例如使用 ),并选择性地在多行中列出参数: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[*]}"
}

换句话说,你不仅可以通过参数的名称来调用参数(这弥补了一个更易读的核心),你实际上还可以传递数组(以及对变量的引用 - 这个功能只在 Bash 4.3 中有效)!此外,映射的变量都在局部范围内,就像(和其他变量)一样。$1

使这项工作的代码非常轻量级,并且可以在 Bash 3 和 Bash 4 中工作(这是我测试过的唯一版本)。如果你对更多这样的技巧感兴趣,这些技巧使使用 bash 进行开发变得更好、更容易,你可以看看我的 Bash Infinity 框架,下面的代码可作为其功能之一使用。

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 parameter:
      shift
    fi
  done
  echo "${evalString} local -i __paramIndex=${paramIndex};"
}

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

评论

0赞 GypsyCosmonaut 8/6/2017
、、我应该在互联网上查找什么以了解更多信息?@var@reference@params
1赞 Aslan French 7/19/2018
嗨,@niieani当我尝试以您在答案中使用的形式创建一个 bash 函数时,它告诉我我需要从 apt 安装 ucommon utils。这就是你的 bash 脚本的工作方式吗?我这样做正确吗?如果我理解你或其他人基本上构建了 ucommon util 程序来允许扩展 Bash,对吗?
0赞 niieani 8/2/2018
@DavidA.French 不,这不应该发生。和我的代码之间没有关系。您可能安装了一些工具,导致您提到的问题,不知道它可能是什么。ucommon
1赞 Anthony Rutledge 3/13/2020
考虑到这个问题,太复杂了。对于大多数人来说,这样的事情已经足够好了。此外,在 bash 中,可以选择使用 declare 来创建关联数组。您已经可以将数组作为列表传递了!local filename=$1-AcallingSomeFunction "${someArray[@]}"
14赞 Milad P. 10/24/2016 #5

它从用户那里获取两个数字,将它们提供给调用的函数(在代码的最后一行),然后将它们相加并打印出来。addadd

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

评论

8赞 Wil 10/20/2017
以这种方式按名称传递仅适用于传递给数值运算符 (( )) 的整数,并且它仅适用于数值运算符递归地将字符串解析为值。如果你想测试我的意思,试着输入'5'表示x,然后输入'x'表示y,你会看到它加上(x + y)=(5 + x)=(5 + 5)=10。对于所有其他用例,您的示例将失败。相反,您应该使用“add ”$x“”“$y”作为通用代码。
10赞 Wil 2/21/2017 #6

将命名参数传递给 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];

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

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

21赞 Adiii 11/23/2017 #7

一个简单的示例,在执行脚本期间或在调用函数时清除脚本内部。

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

评论

0赞 tijko 12/14/2022
为了它的价值,我也遵循这种形式,但我想知道有什么额外的好处吗?