shell参数传递与默认值

# 处理参数

几个特殊字符用来处理参数:

参数处理 说明
$0 当前脚本的文件名
$n 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。
$# 传递到脚本或函数的参数个数
$* 以一个单字符串显示所有向脚本传递的参数
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。0是为正常退出,其他为不正常退出

退出状态码大体分两种:

  • 一种是命令正确执行的状态码,该状态码为:0
  • 一种是命令错误执行的状态码,为1-255

Linux退出状态码

状态码 描述
0 命令成功结束
1 通用未知错误
2 误用shell命令
126 命令不可执行
127 没找到命令
128 无效退出参数
128+x Linux信号x的严重错误
130 命令通过Ctrl+C终止
255 退出状态码越界

状态码取值范围为0-255,如果在指定的状态码大于255,那么shell会通过模(模就是256)运算得到相应的退出状态码。

# 默认参数(变量默认值)

# if 繁琐方式【要点】

注意这里有空格;

if [ ! $1 ]; then
    $1='default'
fi
1
2
3
# if [ ! $1 ]; then
#   isCache=false
# else
#  isCache=$1
# fi
isCache=false
if [ $1 ]; then
 isCache=$1
fi
1
2
3
4
5
6
7
8
9

示范:

#!/bin/bash
PRO_NAME=autorun-pro
PRO_VERSION=0.1.0
isCache=false

if [ $1 ]; then
 isCache=$1
fi

if [ $isCache == true ]; then
  docker build --no-cache -t $PRO_NAME .
else
  docker build -t $PRO_NAME .
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# - 变量为null

取默认值

  • 变量 为 null
${vari-defaultValue}
1

实践

[root@linuxidc /]# unset name
[root@linuxidc /]# echo ${name}

[root@linuxidc /]# echo ${name-samy}
samy
[root@linuxidc /]# name=""
[root@linuxidc /]# echo ${name-samy}

[root@linuxidc /]# echo ${name}
1
2
3
4
5
6
7
8
9

# = 变量为null时, 同时改变变量值

[root@linuxidc /]# unset name
[root@linuxidc /]# echo ${name=samy}
samy
[root@linuxidc /]# echo $name
samy
[root@linuxidc /]# name=""
[root@linuxidc /]# echo ${name=samy}

[root@linuxidc /]#
1
2
3
4
5
6
7
8
9

# :- 变量为null 或 空字符串

取默认值

  • 变量为null
  • 变量为空字符串
${vari:-defaultValue}
1

# := 变量为null 或 空字符串, 同时改变变量值

{$vari:=defaultValue}
1

测试 null

[root@linuxidc /]# unset name

[root@linuxidc /]# echo ${name:=samy}
samy
[root@linuxidc /]# echo ${name}
samy
[root@linuxidc /]#
1
2
3
4
5
6
7

测试 空字符串

[root@linuxidc /]# name=""
[root@linuxidc /]# echo ${name:=samy}
samy
[root@linuxidc /]# echo $name
samy
1
2
3
4
5

# :? 变量为null 或 空字符串时报错并退出

[root@linuxidc /]# unset name
[root@linuxidc /]# echo ${name:?samy}
-bash: name: samy
[root@linuxidc /]# name=""
[root@linuxidc /]# echo ${name:?samy}
-bash: name: samy
[root@linuxidc /]# name="guest"
[root@linuxidc /]# echo ${name:?samy}
guest
1
2
3
4
5
6
7
8
9

# :+ 变量不为空时使用默认值

:- 相反

[root@linuxidc /]# name="guest"
[root@linuxidc /]# echo ${name:+samy}
samy
[root@linuxidc /]# echo $name
guest
1
2
3
4
5

# $$使用

echo $$
29949
1
2

# 脚本参数

# $0,$1,$2..

优点:获取参数更容易,执行脚本时需要的输入少

缺点:必须按照顺序输入参数,如果中间漏写则参数对应就会错误

#!/bin/bash
echo "File Name: $0"
echo "First Parameter : $1"
echo "First Parameter : $2"
echo "Quoted Values: $@"
echo "Quoted Values: $*"
echo "Total Number of Parameters : $#"
1
2
3
4
5
6
7

运行结果:

$./test.sh samy zh
File Name : ./test.sh
First Parameter : Zara
Second Parameter : zh
Quoted Values: samy zh
Quoted Values: samy zh
Total Number of Parameters : 2
1
2
3
4
5
6
7

下面是参数超过10个的情况,在test.sh文件写入

#!/bin/bash
echo "脚本名$0"
echo "第一个参数$1"
echo "第二个参数$2"
echo "第三个参数$3"
echo "第四个参数$4"
……
echo "第十个参数$10"
echo "第十个参数${10}"
1
2
3
4
5
6
7
8
9

在shell中执行脚本,结果如下

$ ./test.sh a b c d e f g h i j
#shell中将会输出:
脚本名./test.sh
第一个参数a
第二个参数b
第三个参数c
第四个参数d
第五个参数e
第六个参数f
第七个参数g
第八个参数h
第九个参数i
第十个参数a0
第十个参数j
1
2
3
4
5
6
7
8
9
10
11
12
13
14

可以看到${10}正确读取到了第十个参数,而$10被分成$1读取到第一个参数a然后拼接字符串0,于是输出a0。

# getopts【要点】

# bash 内置的 getopts;外部强大的参数解析工具:getopt

优点:由于使用了-a加参数值的方式进行一一匹配,所以不会参数匹配错误,同时也可以缺省参数,并不会导致参数错误,同时也便于后期参数的扩展和移植

缺点:脚本执行时参数需要的输入会增多

语法格式:getopts [option[:]] [DESCPRITION] VARIABLE

  • option:表示为某个脚本可以使用的选项
  • ":":如果某个选项(option)后面出现了冒号(":"),则表示这个选项后面可以接参数(即一段描述信息DESCPRITION)
  • VARIABLE:表示将某个选项保存在变量VARIABLE中

同样新建一个test.sh文件, 目前发现只能识别一个字母;

#!/bin/bash
while getopts "a:b:c:" opt; do
    case $opt in
    a)
        echo "参数a的值$OPTARG"
        ;;
    b)
        echo "参数b的值$OPTARG"
        ;;
    c)
        echo "参数c的值$OPTARG"
        ;;
    ?)
        echo "未知参数"
        exit 1
        ;;
    esac
done
echo "全部执行"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

用一个while循环加case分支获取不同参数,a:b:c相当于定义参数的变量名,有时候可能会有未知参数,所以增加一个?的分支。

$ ./test.sh -a 1 -b 2 -c 3
参数a的值1
参数b的值2
参数c的值3
1
2
3
4
$ ./test.sh -a 1 -c 3
参数a的值1
参数c的值3
1
2
3
$ ./test.sh -a 1 -c 3 -d 4
参数a的值1
参数c的值3
未知参数
1
2
3
4

实践:

情况一:

#!/bin/bash
#选项后面的冒号表示该选项需要参数,没有设置的话,后面的$OPTARG获取不到参数;
while getopts "a:bc" arg 
do
  case "$arg" in
  a)
    echo "a's arg:$OPTARG" #参数存在$OPTARG中
    ;;
  b)
    echo "b"
    ;;
  c)
    echo "c"
    ;;
  ?)  #当有不认识的选项的时候arg为?
  echo "unkonw argument"
  exit 1
  ;;
    esac
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

运行:注意-b -c -bc参数;

bash sh ./test.sh -a arg -b -c  
a's arg:arg
b
c
➜  bash sh ./test.sh -a arg -bc    
a's arg:arg
b
c
1
2
3
4
5
6
7
8

其他例子:

#!/bin/bash
while getopts 'd:Dm:f:t:' OPT; do
    case $OPT in
        d)
            DEL_DAYS="$OPTARG";;
        D)
            DEL_ORIGINAL='yes';;
        f)
            DIR_FROM="$OPTARG";;
        m)
            MAILDIR_NAME="$OPTARG";;
        t)
            DIR_TO="$OPTARG";;
        ?)
            echo "Usage: `basename $0` [options] filename"
    esac
done

shift $(($OPTIND - 1))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

情况二:【推荐】

#!/bin/bash
echo 初始 OPTIND: $OPTIND

while getopts "a:b:c" arg #选项后面的冒号表示该选项需要参数
do
    case $arg in
        a)
			echo "a's arg:$OPTARG" #参数存在$OPTARG中
			;;
        b)
			echo "b's arg:$OPTARG"
			;;
        c)
			echo "c's arg:$OPTARG"
			;;
        ?)  #当有不认识的选项的时候arg为?
			echo "unkonw argument"
			exit 1
		;;
    esac
done

echo 处理完参数后的 OPTIND$OPTIND
echo 移除已处理参数个数:$((OPTIND-1))
shift $((OPTIND-1))
echo 参数索引位置:$OPTIND
echo 准备处理余下的参数:
echo "Other Params: $@"
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

运行:

bash bash getopts2.sh -a 1 -b 2 -c 3  test -oo xx -test
初始 OPTIND: 1
a's arg:1
b's arg:2
c's arg:
处理完参数后的 OPTIND:6
移除已处理参数个数:5
参数索引位置:6
准备处理余下的参数:
Other Params: 3 test -oo xx -test
1
2
3
4
5
6
7
8
9
10

情况三:【重点】

sh build.sh -c true -p true

PRO_NAME=autorun-pro
PRO_VERSION=latest
isCache=false
isPush=false
#选项后面的冒号表示该选项需要参数
while getopts "c:p:" opt; do
    case $opt in
    c)
        echo "参数C的值$OPTARG"
        isCache=$OPTARG
        ;;
    p)
        echo "参数P的值$OPTARG"
        isPush=$OPTARG
        ;;
    ?)
        echo "未知参数"
        exit 1
        ;;
    esac
done

if [ $isCache == true ]; then
  docker build -t $PRO_NAME:$PRO_VERSION .
else
  docker build --no-cache -t $PRO_NAME:$PRO_VERSION .
fi
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

# 输入参数默认【推荐】

其他参数详细见【06.shell参数传递与默认值】说明

#!/bin/bash
v=${1:-'1.0.0'}
h=${2:-'test demo'}
echo ${v}
echo ${h}
1
2
3
4
5
[root@samy ~]# bash test.sh 
1.0.0
test demo
[root@samy ~]# bash test.sh  abc 123
abc
123
1
2
3
4
5
6

# 默认参数

其他参数详细见【06.shell参数传递与默认值】说明

通常shell中我们需要给变量设置默认值,可能会写出如下代码

#!/bin/bash 
if [ ! $1 ]; then 
	$1='default' 
fi 
1
2
3
4

显然这种方式在变量少的时候没啥问题,一旦变量多起来,我们可能就有大量的重复劳动(if判断)

有没有比较优雅的方式,不用写一大堆if判断,显然答案是有的

#当变量a为null时则var=b 
var=${a-b} 
1
2

# 函数参数

# 各个比较

#!/bin/bash
funWithParam(){
    echo "第一个参数为 $1 !"
    echo "第二个参数为 $2 !"
    echo "第十个参数为 $10 !"
    echo "第十个参数为 ${10} !"
    echo "第十一个参数为 ${11} !"
    echo "参数总数有 $# 个!"
    echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
1
2
3
4
5
6
7
8
9
10
11

输出结果:

第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
1
2
3
4
5
6
7

# $* 和 $@ 的区别

$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" … "$n" 的形式输出所有参数。

但是当它们被双引号(" ")包含时,"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数;"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数。

$*和$@详细区别请看此处

 #!/bin/bash
echo "-----------------"
for key in "$@"
do
 echo '$@' $key
done
echo "-----------------------------"
for key2 in $*
do
 echo '$*' $key2
done
1
2
3
4
5
6
7
8
9
10
11

1、带引号执行及结果:

[root@localhost ~]# bash file.sh linux "python c"
-----------------
$@ linux
$@ python c
-----------------------------
$* linux
$* python
$* c
1
2
3
4
5
6
7
8

2、不带引号执行及结果:

[root@localhost ~]# bash file.sh linux python c
-----------------
$@ linux
$@ python
$@ c
-----------------------------
$* linux
$* python
$* c
1
2
3
4
5
6
7
8
9

# 函数返回值

两种方式

  • 用变量接收函数返回值,函数用echo等标准输出将要返回的东西打印出来。
  • 用 $? 来接收函数的执行状态,但是 $? 要紧跟在函数调用处的后面
function getEnvConf() {
  param=$1
  echo $param
}
echo $(getEnvConf isPro) #可以返回字符串
#isPro
1
2
3
4
5
6
#!/bin/sh
 
function getStr(){
	return "string"
}
 
#方法一
echo `getStr` 
#方法二
echo $(getStr) 
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
function getResult(){
    echo "这是我的第一个 shell 函数!"
    return `expr 1 + 1`
}
getResult
echo $?
echo $?
1
2
3
4
5
6
7
8

输出结果:

这是我的第一个 shell 函数!
2
0
1
2
3

createDir.sh创建目录

#!/bin/bash
function createDir(){
	if [ ! -d $1 ]; then
		mkdir -p $1
	fi
}
 
DIR="temp/"
# 两者二先一
#$(createDir $DIR)
$(createDir "temp/")
1
2
3
4
5
6
7
8
9
10
11

# for跟case的配合使用

all.sh

#!/bin/bash

buildAdmin() {
    echo "buildAdmin"
}
buildWeb() {
    echo "buildWeb"
}
buildCapacity() {
    echo "buildCapacity"
}
buildFiles() {
    echo "buildFiles"
}

if [ $# -gt 0 ]; then
    for i in "$@"; do
        case $i in
        admin | a)
            buildAdmin
            ;;
        web | w)
            buildWeb
            ;;
        capacity | c)
            buildCapacity
            ;;
        files | f)
            buildFiles
            ;;
        *)
            echo "Ignorant"
            ;;
        esac
    done
else
    echo 'all pro build'.
fi
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
➜  build-sh ./all.sh a w c f   
admin
web
capacity
files
➜  build-sh ./all.sh         
all pro build.
1
2
3
4
5
6
7

参数时区处理:

param=zh
datePath=app/utils/date-utils.js

varKey=defaultTz
timeZone='Asia\/Shanghai'
# 初始化默认中文
sed -i "" "s/\($varKey =\).*/\1 '$timeZone';/" $datePath
echo "参数的值$1"
if [ $1 ]; then
  param=$1
  case $1 in
  zh)
      timeZone='Asia\/Shanghai'
      ;;
  en)
      timeZone='America\/Los_Angeles'
      ;;
  eu)
      timeZone='Europe\/London'
      ;;
  *)
      echo "param not found!!"
      exit 1;
      ;;
  esac
  sed -i "" "s/\($varKey =\).*/\1 '$timeZone';/" $datePath
fi
echo "param参数的值$param"
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

调用:

sh start.sh 

sh start.sh en
1
2
3

# 参考链接

  • https://www.runoob.com/linux/linux-shell-func.html

  • https://blog.51cto.com/steed/2443313

上次更新: 2022/04/15, 05:41:30
×