shell总结

参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$#     # 是传给脚本的参数个数
$@ # 是传给脚本的所有参数的列表
$0 # 是脚本本身的名字

$1 # 是传递给该shell脚本的第一个参数
$2 # 是传递给该shell脚本的第二个参数

$? # 上一个命令的返回值
$! # 上一个命令的PID

# 脚本文件所在的目录的全路径. 例如/root/abc
SCRIPT_DIR=$(cd "$(dirname "$0")";pwd)

# 当前目录的名称
CURRENT_DIR_NAME=${PWD##*/}

变量赋值

变量名=变量 (等号两边不能有空白字符)
本地变量:作用域为整个bash进程 varname=value
局部变量:作用域只对当前代码段有效 local varname=value
环境变量:作用域为当前shell进程及其子进程 export varname=value "导出"

bash脚本

bash脚本开头第一行是 #!/bin/bash
常用命令:

  • set -e
    如果有命令返回非零, 则立即退出, 并返回该错误码
  • set -o pipefail
    如果管道上命令有错误, 则直接返回该值( 默认是返回该行命令, 非管道命令的返回值 )
  • shopt -s nullglob
    对于nullglob来说, 就是在使用shell 的通配符匹配文件时,如果没有匹配到任何文件时,那就会输出null string,而不是通配符字符本身。 在使用来匹配一个文件时,如果该目录下并没有这个文件,这个通配符*就会作为一个字符,被当做一个文件来处理,这是很危险的。因此,在匹配file 时,nullglob是很有必要enable的。
    http://blog.csdn.net/ciedecem/article/details/11978277

tips

alpine 里面用的 busybox 的sh, 不支持 [[ release-asdf == release-* ]] 的字符串匹配, 可用 ${ver:0:8} 截取字符串后进行匹配

语法

if

if条件返回值是0时表示成立, true

1
2
3
4
5
6
7
if [ $A == 1 ]; then
echo 1
elif [ $A == 2 ]; then
echo 2
else
echo no
fi

条件

test命令

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
test 支持的运算符有下面这些
# 表达式运算符
expression # 表达式为真(任何命令)
! # 表达式为假
-a # 逻辑与 表达式1 –a 表达式2 两个表达式都为真
-o # 逻辑或 表达式1 –o 表达式2 两个表达式有一个为真
# 字符串运算符
-n # 字符串的长度为非零
-z # 字符串的长度为零
= # 字符串相等
!= # 字符串不等
# 整数比较运算符
-eq # int1 -eq int2 如果 int1 等于 int2,则为真
-ge # int1 -ge int2 如果 int1 大于或等于 int2,则为真
-gt # int1 -gt int2 如果 int1 大于 int2,则为真
-le # int1 -le int2 如果 int1 小于或等于 int2,则为真
-lt # int1 -lt int2 如果 int1 小于 int2,则为真
-ne # int1 -ne int2 如果 int1 不等于 int2,则为真
# 文件运算符
-ef # File1 –ef File2 两个文件具有同样的设备号和i结点号
-nt # File1 –nt File2 文件1比文件2新
-ot # File1 –ot File2 文件1比文件2旧
–b # 文件存在并且是块设备文件
–c # 文件存在并且是字符设备文件
–d # 文件存在并且是目录
–e # 文件存在
–f # 文件存在并且是正规文件
–g # 文件存在并且是设置了组ID
–G # 文件存在并且属于有效组ID
–h # 文件存在并且是一个符号链接(同-L)
–k # 文件存在并且设置了sticky位
–b # 文件存在并且是块设备文件
–L # 文件存在并且是一个符号链接(同-h)
–o # 文件存在并且属于有效用户ID
–p # 文件存在并且是一个命名管道
–r # 文件存在并且可读
–s # 文件存在并且是一个套接字
–t # FD 文件描述符是在一个终端打开的
–u # 文件存在并且设置了它的set-user-id位
–w # 文件存在并且可写
–x # 文件存在并且可执行

方括号 [ ]

方括号相当于执行test命令, 注意方括号内的空格

1
[ a == b ]  # 比较字符串相等

双方括号 [[ ]]

双方括号的主要功能是可以满足其内表达式的转义需求,就是说可以像在其他语言中一样使用出现的比较符号。
比如 <,>,==,!= 等,而这些符号在单方括号中只能用做字符串的比较。
比如 &&,|| ,在单方括号中是不能使用的。
单/双方括号的区别

双括号 (( ))

双括号用来支持算术表达式, 只支持整型

1
2
3
4
5
6
7
8
9
# 各种其他语言中可用的用算
(( 2 * 3 == 6 ))
(( 2 * 3 == 6 ? 1 : 0 ))
((a++))

# 循环
for (( i = 0; i < 5; i++)); do
echo $i;
done

  • 单括号
    1. 启动一个 subshell 来执行括号内的代码,subshell 有独立的环境变量
    2. 将命令的结果作为变量值 例如 a=$(echo aaa)
    3. 初始化数组array=(a b c d)

demo

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 单方括号内可以使用变量
SWAP_DEVICE=/dev/sda1
if [ ${SWAP_DEVICE} == "/dev/sda1" ]; then
echo "单方括号内可以使用变量"
fi

# 双方括号内可以使用变量
SWAP_DEVICE=/dev/sda1
if [[ ${SWAP_DEVICE} == "/dev/sda1" ]]; then
echo "双方括号内可以使用变量"
fi

# 此处4被当作字符串进行比较
if [ 4 == 4 ]; then
echo "[ 4 == 4 ] (true)"
fi

echo -e "\n***单方括号支持 test 命令的所有运算符,还支持以下的关系运算符 >,<,=,==,!="
echo -e "***这些关系运算符只限于字符串使用"
echo -e "***除此之外其他的符号都不支持"
echo -e "\n"

# 此处的数字同样被当作字符串处理,所以条件返回真
# 字符串的比较总是从头开始比较第一个不相同的字符
if [[ 44 > 211 ]]; then
echo "[[ 44 > 211 ]] (true)"
fi

# 此处的数字同样被当作字符串处理,所以条件返回假
if [[ 4 == 2*2 ]]; then
echo "[[ 4 == 2*2 ]] (true)"
else
echo "[[ 4 == 2*2 ]] (false)"
echo "等同于 [ 4 == 2*2 ] (false)"
echo "[[ 4 == 2 * 2 ]] (error)"
fi

# && 符号不是单方括号支持的符号,但是是双方括号支持的符号
if [[ abc = abc && def = def ]]; then
echo "[[ abc = abc && def = def ]]"
echo "[ abc = abc && def = def ] (error)"
fi

echo -e "\n***双方括号只比单方括号多支持了2个符号 && 和 ||\n"

if (( 2 * 3 == 6 )); then
echo "(( 2 * 3 == 6 )) (true)"
fi

if (( 2 * 3 == 6 ? 1 : 0 )); then
echo "(( 2 * 3 == 6 ? 1 : 0 ))"
echo "双括号支持三目运算符"
fi

# 双括号在变量赋值中的使用
a=5; ((a++))
echo "++操作 $a"

# 双括号在 for 循环中的使用
for (( i = 0; i < 5; i++)); do
echo "循环中${i}";
done

# 如何判断空的变量?
# 这里会出现一个误区 [ ${emptyvar} = "" ],这个等于 [ = "" ],所以会报错
# 而下面的2个方法都是可行的
emptyvar=
if [ "${emptyvar}" = "" ]; then
echo "empty var"
fi
if [ -z ${emptyvar} ]; then
echo "empty var"
fi

case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
case 字符串 in
    模式)
        语句
        ;;
    模式2 | 模式3)
         语句
         ;;
    *)
         默认执行的 语句
         ;;
esac

# 例如
case "$(uname -m)" in
        *64)
                ;;
        *)
                echo >&2 'Error: you are not using a 64bit platform.'
                echo >&2 'Daomonit currently only supports 64bit platforms.'
                exit 1
                ;;
esac

for

  • 格式一
    for 变量
    do
    语句
    done

格式二

  • for 变量 in 列表
    do
    语句
    done

格式三

  • for ((变量=初始值; 条件判断; 变量变化))
    do
    语句
    done

例如, 导出所有数据库

1
2
3
4
5
#!/bin/bash
for db in $(cat dbname)
do
mysqldump -uroot -p123 ${db} > ${db}.sql
done

正则

1
2
3
4
if [[ ! $CI_COMMIT_REF_NAME  =~ $v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo not a tag
exit 2
fi

参考:

https://www.cnblogs.com/the-capricornus/p/5009976.html
https://www.cnblogs.com/lonelywolfmoutain/p/5950439.html

shell总结

0%