目录

6.6 函数和参数传递

函数和参数传递

/images/linux_mt/linux_mt.jpg

本节我们来学习 bash shell 编程的第六部分参数传递与函数,包括以下内容:

  1. 如何向脚本传递参数
  2. bash 函数
  3. 局部作用域

1. 参数传递

1.1 位置参数

所谓位置参数是 bash 中,脚本或函数接收参数的形式,除了位置参数,脚本中还内置了一些特殊参数,用于保存特定的值。参数的对应关系如下所示

myscript.sh argu1 argu2....

  • 位置参数:
    • $1: 表示第一个位置参数 argu1
    • $2: 表示第二个位置参数 argu2
    • ${10}:表示第 10 个位置参数 argu10
    • ${11}:表示第 11 个位置参数 argu10,其他以此类推
  • 特殊变量:
    • $0:脚本文件路径本身;
    • $#:脚本参数的个数;
    • $*:由空格分隔的所有参数的字符串 “$1 $2 $n”
    • $@:所有参数组成的列表 “$1”,"$2","$3","$n"

1.2 参数轮替

shift [n]

  • 作用:造成参数变量的号码偏移,即整体参数的右移
  • n:数字,默认为1,代表拿掉最前面几个参数的意思

2. 函数

2.1 bash 函数特性

函数是主要作用是实现代码重用,其在每次被调用时创建,返回时终止。bash 中的函数与 bash 脚本的使用方式基本是类似的。

函数的返回值

函数的返回值也包括执行结果返回值和状态返回值

  • 函数的执行结果返回值为代码的输出包括
    1. 函数中的打印语句:echo, print
    2. 函数中调用的系统命令执行后返回的结果
  • 执行状态返回值:
    • 默认是函数体中最后一次执行的命令状态结果
    • 使用 return [0-255] 自定函数执行状态的返回值,不能使用 exit, exit 会直接退出脚本

函数参数

函数也通过位置接收传递进来的参数,并且表示方法与脚本完全相同。因此函数内的 $n 参数并不是脚本中的 $n 参数。向函数传递参数时,在函数名后跟空白分隔参数列表即可,testfunc arg1 arg2 arg3 ...

函数作用域

bash 函数默认与脚本共享同一作用域,函数内可以直接访问和修改脚本内变量的值。要想创建局部变量必需使用 local VARIABLE=VALUE。因此 bash 中的变量有三种:

  1. 局部变量:作用域是函数内;在函数结束时被自动销毁,因此不会影响脚本内同名变量的值
  2. 本地变量:作用域是当前shell脚本程序文件,在运行脚本的shell进程结束时被销毁
  3. 环境变量:作用域是当前进程及其子进程

因为函数内能直接修改脚本内变量的值,所以函数最好都使用局部变量,以免函数调用非预期的更改脚本内变量的值,引入难以调试的 bug。

2.2 定义语法:

1
2
3
4
5
6
7
8
9
# 方式一
function F_NAME {  # 函数名后必需要有空格
  函数体
}

# 方式二
F_NAME() {         # ()后必需要有空格
  函数体
}

3. 函数使用示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 练习:写一个脚本,完成如下功能(使用函数):
# 1、提示用户输入一个可执行命令;
# 2、获取这个命令所依赖的所有库文件(使用ldd命令);
# 3、复制命令至/mnt/sysroot/对应的目录中
#   解释:假设,如果复制的是cat命令,其可执行程序的路径是/bin/cat,那么就要将/bin/cat复制到/mnt/sysroot/bin/目录中,如果复制的是useradd命令,而useradd的可执行文件路径为/usr/sbin/useradd,那么就要将其复制到/mnt/sysroot/usr/sbin/目录中;
# 4、复制各库文件至/mnt/sysroot/对应的目录中;


#!/bin/bash
#
target=/mnt/sysroot/

[ -d $target ] || mkdir $target

preCommand() {
    if which $1 &> /dev/null; then
      commandPath=`which --skip-alias $1`
      return 0
    else
      echo "No such command."
      return 1
    fi
}

commandCopy() {
    commandDir=`dirname $1`
    [ -d ${target}${commandDir} ] || mkdir -p ${target}${commandDir}
    [ -f ${target}${commandPath} ] || cp $1 ${target}${commandDir}
}

libCopy() {
    for lib in `ldd $1 | egrep -o "/[^[:space:]]+"`; do
      libDir=`dirname $lib`
      [ -d ${target}${libDir} ] || mkdir -p ${target}${libDir}
      [ -f ${target}${lib} ] || cp $lib ${target}${libDir}
    done
}

read -p "Plz enter a command: " command

until [ "$command" == 'quit' ]; do

  if preCommand $command &> /dev/null; then
    commandCopy $commandPath
    libCopy $commandPath
  fi

  read -p "Plz enter a command: " command
done