函数

和其它“真正”的编程语言一样,Bash也有函数,尽管它在实现方面有一些限制。一个函数就是一个子程序,实现一系列操作的代码块,执行一个特定任务的“黑盒子”。有重复代码的地方,当一个过程只需要轻微修改任务就会重复执行的时候,那么你就需要考虑使用函数了。

# 第一种函数定义, { 可以单独占一行
function function_name () {
	# function's body
}

# 第二种函数定义,()可以省略,{ 可以单独占一行
function function_name {
	# function's body
}

# 第三种函数定义,function可以省略,{ 可以单独占一行
function_name() {
	# function's body
}

一个函数可能被“压缩”到一个单独行里
在这种情况下,函数里的最后一个命令必须跟有一个分号。

fun () { echo "This is a function"; echo } # Error! 
fun2 () { echo "Even a single-command function? Yes!"; } 

函数调用 就像使用命令一样:

#!/bin/bash  
  
# 定义函数  
function my_function() {  
    echo "Hello from my_function!"  
}  
  
# 调用函数  
my_function  

# 输出其他内容  
echo "This is outside the function."  

# 再次调用函数  
my_function

函数体不能为空 否则会报错,以下空函数会报错syntax error near unexpected token '}'

#!/bin/bash
empty() {
}
empty

函数定义必须在第一次函数调用之前。没有声明函数的方法,比如像C语言中一样:

f1
# 将会产生一个错误消息,因为“f1”函数还没有定义。

declare -f f1      # 这样也不会有帮助。
f1                 # 仍然会产生一个错误消息。

# 然而...

f1() {
    echo "Calling function \"f2\" from within function \"f1\"."
    f2 
}

f2 () {
    echo "Function \"f2\"."
}

f1  #  在此之前,事实上函数“f2”是没有被调用的,
    #+ 尽管在它定义之前被引用了。
    #  这是可以的。
    # 感谢, S.C.

函数参数

Bash函数支持参数传递function_name arg1 arg2 arg3,函数体中通过$1,$2,$3引用arg1,arg2,arg3。 和其它的编程语言相比,shell脚本一般只会传值给函数。如果把变量名作为参数传递给函数的话,那将被解释为字面含义,也就是被看做字符串。 函数只会以字面含义来解释函数参数。

#!/bin/bash
# func-cmdlinearg.sh
#  带一个命令行参数来执行这个脚本,
#+ 类似于 $0 arg1.


func(){
    echo "$1"   # 显示传递给这个函数的第一个参数。
}               # 命令行参数可以么?

echo "First call to function: no arg passed."
echo "See if command-line arg is seen."
func
# 不! 没有见到命令行参数.

echo "============================================================"
echo
echo "Second call to function: command-line arg passed explicitly."

func $1
# 现在,见到命令行参数了!
exit 0
#!/bin/bash
# 函数和参数

DEFAULT=default                 # 默认参数值。D

func2 () {
    if [ -z "$1" ]              # 第一个参数长度是否为零?
    then
        echo "-Parameter #1 is zero length.-"  # 或者没有参数传递进来。
    else
        echo "-Parameter #1 is \"$1\".-"
    fi

    variable=${1-$DEFAULT}
    echo "variable = $variable"     #  这里的参数替换
                                    #+ 表示什么?
                                    #  ---------------------------
                                    #  为了区分没有参数的情况
                                    #+ 和只有一个null参数的情况。

    if [ "$2" ]
    then
        echo "-Parameter #2 is \"$2\".-"
    fi

    return 0
}

echo

echo "Nothing passed."
func2                          # 不带参数调用
echo

echo "Zero-length parameter passed."
func2 ""                        # 使用0长度的参数进行调用
echo

echo "Null parameter passed."
func2 "$uninitialized_param"    # 使用未初始化的参数进行调用
echo


echo "One parameter passed."
func2 first           # 带一个参数的调用
echo

echo "Two parameters passed."
func2 first second    # 带两个参数的调用
echo

echo "\"\" \"second\" passed."
func2 "" second       # 第一个调用参数为0长度参数,
echo                  # 第二个是ASCII码的字符串参数。

exit 0

shift命令用于移动位置参数
在 Bash 脚本中,shift 命令通常用于移动位置参数(即 $1$2 等),使得 $2 的值变为 $1$3 的值变为 $2,依此类推。当在函数中使用 shift 时,它可以用来处理传递给函数的参数列表。

以下是一个使用 shift 命令在 Bash 函数中处理参数的示例:

#!/bin/bash

# 定义一个处理参数的函数
process_args() {
    while [[ $# -gt 0 ]]; do
        echo "Processing argument: $1"
        shift  # 移除第一个参数
    done
}

# 调用函数并传递参数
process_args arg1 arg2 arg3

在这个示例中,process_args 函数使用了一个 while 循环来检查是否还有参数($# -gt 0)。如果有参数,它会输出该参数($1),然后使用 shift 命令移除已经处理过的参数。在 shift 命令执行后,下一个参数(原来的 $2)就变成了 $1,依此类推。

输出结果将是:

Processing argument: arg1
Processing argument: arg2
Processing argument: arg3

这个示例展示了如何在 Bash 函数中迭代处理传递给函数的参数列表。shift 命令在每次迭代后移除已经处理过的参数,使得循环能够继续处理剩余的参数。

局部变量

函数内定义的变量默认是全局变量,要想变成局部变量,需要用local关键字声明,那么它就只能够在该变量被声明的代码块中可见。 这个代码块就是局部范围。 在一个函数中,一个局部变量只有在函数代码中才有意义;在函数被调用之前,所有在函数中声明的变量,在函数外部都是不可见的,当然也包括那些被明确声明为local的变量。使用local关键字声明变量是先赋值,然后再限定变量的作用域。下面看一些示例。 局部变量的可见范围:

#!/bin/bash
# ex62.sh: 函数内部的局部变量与全局变量。

func () {
    local loc_var=23       # 声明为局部变量。
    echo                   # 使用'local'内建命令
    echo "\"loc_var\" in function = $loc_var"
    global_var=999         # 没有声明为局部变量。
    # 默认为全局变量。

    echo "\"global_var\" in function = $global_var"
}

func

# 现在,来看看局部变量“loc_var”在函数外部是否可见。

echo
echo "\"loc_var\" outside function = $loc_var"
                                    # $loc_var outside function =
                                    # 不行, $loc_var 不是全局可见的.
echo "\"global_var\" outside function = $global_var"
                                    # $在函数外部global_var = 999
                                    # $global_var 是全局可见的.
echo

exit 0
#  与C语言相比,在函数内声明的Bash变量
#+ 除非它被明确声明为local时,它才是局部的。

再来看个示例:

#!/bin/bash

function1 ()
{
  local func1var=20

  echo "Within function1, \$func1var = $func1var."

  function2
}

function2 ()
{
  echo "Within function2, \$func1var = $func1var."
}

function1

exit 0


# 脚本的输出:

# Within function1, $func1var = 20.
# Within function2, $func1var = 20.
#!/bin/bash

func (){
    global_var=37    #  变量只在函数体内可见
                     #+ 在函数被调用之前。
}                    #  函数结束

echo "global_var = $global_var"  # global_var =
                                 #  函数 "func" 还没被调用,
                                 #+ 所以$global_var 在这里还不是可见的.
func
echo "global_var = $global_var"  # global_var = 37
                                 # 已经在函数调用的时候设置。

local声明变量的过程:

#!/bin/bash

echo "==OUTSIDE Function (global)=="
t=$(exit 1)
echo $?     # 1
            # 如预期一样.

echo
function0 ()
{
    echo "==INSIDE Function=="
    echo "Global"
    t0=$(exit 1)
    echo $?      # 1
                 # 如预期一样.

    echo
    echo "Local declared & assigned in same command."
    local t1=$(exit 1)
    echo $?      # 0
	# local t1=$(exit 1),等价于:t1=$(exit 1) local t1; local t1执行成功所以$?为0
    echo
    echo "Local declared, then assigned (separate commands)."
    local t2
    t2=$(exit 1)
    echo $?
}

function0

函数返回值

函数返回一个值,被称为退出状态码。这和一条命令返回的退出状态码类似。退出状态码可以由return 命令明确指定,也可以由函数中最后一条命令的退出状态码来指定(如果成功,则返回0,否则返回非0值)。可以在脚本中使用$?来引用退出状态码。 因为有了这种机制,所以脚本函数也可以像C函数一样有“返回值”。函数的返回值只能是0-255之间的整数,包含0和255。

来看一个函数的例子,取两个数中的最大值:

#!/bin/bash
# max.sh: 取两个Maximum of two integers.
E_PARAM_ERR=250    # 如果传给函数的参数少于两个时,就返回这个值。
EQUAL=251          # 如果两个参数相等时,就返回这个值。
#  任意超出范围的
#+ 参数值都可能传递到函数中。

max2 ()             # 返回两个数中的最大值。
{                   # 注意:参与比较的数必须小于250.
    if [ -z "$2" ]
    then
        return $E_PARAM_ERR
    fi

    if [ "$1" -eq "$2" ]
    then
        return $EQUAL
    else
        if [ "$1" -gt "$2" ]
        then
            return $1
        else
            return $2
        fi
    fi
}
echo -n "Please input first number:"
read first
echo -n "Please input second number:"
read second
max2 $first $second
return_val=$?

if [ "$return_val" -eq $E_PARAM_ERR ]
then
    echo "Need to pass two parameters to the function."
elif [ "$return_val" -eq $EQUAL ]
then
    echo "The two numbers are equal."
else
    echo "The larger of the two numbers is $return_val."
fi

exit 0

函数示例

示例1: