Simple Introduction to Shell

在shell脚本的开头使用#!/bin/bash ,系统会自动使用bash作为解释器进行解释执行。

一、变量

1. 定义、赋值和使用

1
your_name="wl"

注意:

  • 变量名和等号之间不能有空格
  • 只能使用英文字母、数字和下划线,不能以数字开头,不能使用保留关键字

在变量名前加上$即可以使用该变量

1
2
3
my_name="wl"
echo $my_name
echo ${my_name}

变量是否使用{}包起来都是可以的,只是大括号可以更好地区分变量名的边界,比如在下面情况下不使用{}则会误以为引用的是skillScript变量

1
2
3
for skill in Ada Coffe Action Java; do
echo "I am good at ${skill}Script"
done

变量赋值和定义类似:

1
2
3
my_name="wl"
my_name="wangliu"
echo ${my_name}

使用readonly关键字可以定义只读变量,无法对变量进行修改和删除

1
2
3
my_name="wl"
readonly my_name
my_name="wangliu" # 报错

可以使用unset删除变量

1
2
3
my_name="wl"
unset my_name
echo ${my_name} # 无任何输出

2. shell字符串

单引号和双引号
1
str='this is a string'

单引号限制:

  • 原样输出,单引号字符串中的变量是无效的
  • 不可以单独出现单引号,即使转义也不OK,但是用单引号做字符串拼接除外

对应的,双引号的优点:

  • 可以用变量
  • 可以出现转义字符

字符串拼接时可以使用单引号或者双引号,比如:

1
2
3
4
5
6
7
8
9
your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1 # hello, runoob ! hello, runoob !
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2 $greeting_3 # hello, runoob ! hello, ${your_name} !
字符串操作

使用#获取长度

1
2
string="abcd"
echo ${#string} #输出 4

使用 :截取字符串

1
2
string="runoob is a great site"
echo ${string:1:4} # 输出 unoo

查找字符串

1
2
string="runoob is a great site"
echo `expr index "$string" io` # 输出 4

3. shell 数组

bash只支持一维数组,不支持多维数组。

在shell中用括号表示数组,使用空格分隔数组元素,使用[]下标获取数组元素,下标从0开始,@表示所有,不支持获取连续下标;#可以获取数组长度。

1
2
3
4
5
6
names=("wangliu" "wl")
echo ${names} # wangliu
echo ${names[0]} # wangliu
echo ${names[1]} # wl
echo ${names[@]} # @获取整个数组 wangliu wl
echo ${#names[@]} # 2

二、命令行参数处理

处理命令行参数的主要几种用法如下所示:

1
2
3
4
5
6
7
8
#!/bin/bash

echo "参数个数:$#"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "第三个参数:$3"
echo "所有参数:$*"
echo "所有参数:$@"

运行结果为:

1
2
3
4
5
6
7
$ ./test.sh 1 2 3
参数个数:3
第一个参数:1
第二个参数:2
第三个参数:3
所有参数:1 2 3
所有参数:1 2 3

$*$@的区别:

  • 仅仅在双引号中使用两者时才有区别
  • $*会以"$1 $2 $3"的形式输出,是一个参数
  • $@会以"$1" "$2" "$3"的形式输出,是三个参数

示例代码为:

1
2
3
4
5
6
7
8
9
echo "-- \$* 演示 ---"
for i in "$*"; do
echo $i
done

echo "-- \$@ 演示 ---"
for i in "$@"; do
echo $i
done

运行结果为:

1
2
3
4
5
6
7
$ ./test.sh 1 2 3
-- $* 演示 ---
1 2 3
-- $@ 演示 ---
1
2
3

三、运算符

原生的bash不支持简单的数学运算,可以通过awk和expr命令实现,其中expr较为常见。

1. 算数运算符

普通加减乘除取余运算符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
a=10
b=20

# 注意,在expr使用运算符时,运算符前后必须有空格(a + b),a+b是非法的

echo "a + b = `expr $a + $b`" # 加法
echo "a + b = `expr $a + $b`" # 减法

val=`expr $a \* $b` # 乘法 注意,*需要加转义字符
echo "a * b = $val"

echo "b / a = `expr $b / $a`" # 整数除

echo "a % b = `expr $a % $b`" # 取余

运行结果:

1
2
3
4
5
6
$ ./test.sh
a + b = 30
a + b = 30
a * b = 200
b / a = 2
a % b = 10

条件表达式 == 和 != 需要放在方括号之间使用,只用于数字的比较,且数字前后必须有空格。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a=10
b=20
c=20

if [ $a == $b ]
then
echo "a == b"
fi
if [ $a != $b ]
then
echo "a != b"
fi
if [ $b == $c ]
then
echo "b == c"
fi
1
2
3
$ ./test.sh
a != b
b == c

2. 关系运算符

关系运算符只是用于数字之间的比较,值非数字的字符串不支持。和条件表达式类似,数字前后均需要有空格。

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
a=10
b=10
c=20

if [ $a -eq $b ]
then
echo "a == b"
fi
if [ $a -ne $c ]
then
echo "a != c"
fi
if [ $c -gt $a ]
then
echo "c > a"
fi
if [ $a -ge $b ]
then
echo "a >= b"
fi
if [ $a -lt $c ]
then
echo "a < c"
fi
if [ $a -le $b ]
then
echo "a <= b"
fi
1
2
3
4
5
6
7
$ ./test.sh
a == b
a != c
c > a
a >= b
a < c
a <= b

3. 布尔运算符

主要包括 !(非)、-o(或)、-a(与)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
a=10
b=20

if ! [ $a == $b ]
then
echo "!{$a == $b} is true"
else
echo "!{$a == $b} is false"
fi
if [ $a -gt $b -o $a -lt $b ]
then
echo "$a -gt $b -o $a -lt $b is true"
fi
if [ $a -gt $b -a $a -lt $b ]
then
echo "$a -gt $b -o $a -lt $b is true"
else
echo "$a -gt $b -o $a -lt $b is false"
fi
1
2
3
4
$ ./test.sh
!{10 == 20} is true
10 -gt 20 -o 10 -lt 20 is true
10 -gt 20 -o 10 -lt 20 is false

四、echo和printf

echo

显示换行等需要使用 -e 开启转义字符。

1
2
echo -e "test \n"
echo "test \n"
1
2
3
test 

test \n

输出raw字符串,不进行转义和取变量:使用单引号。

1
echo '$name \"'

显示为

1
$name \"

printf

printf类似C中的printf函数,可以格式化字符串、设定对齐方式,且不自动添加换行符,而echo是会自动添加换行符的。

1
2
printf "%-6s %-6s %-10s\n" name age height
printf "%-6s %-6d %-10.2f\n" wl 25 176.0
1
2
3
$ ./test.sh      
name age height
wl 25 176.00
  • %s %d %f的用法和其他语言是相同的,分别表示字符串、整数、浮点数。
  • -表示左对齐,如果缺省则表示右对齐。
  • 6和10表示宽度为6字符和10字符,待显示字符串不足时则以空格填充,超过仍然会显示。
  • .2f的用法和其他语言也相同,表示保留两位小数。

注意:

  • 如果参数数量超过格式化字符串中的占位符,则多出的参数仍然会输出,且重复利用之前的格式
  • 如果参数数量少于格式化字符串中的占位符,则 %s 使用 NULL 替代, %d 使用 0 替代。
  • 格式化字符可以用双引号,单引号,也可以都不使用。
1
2
3
4
# 双引号
printf "%s %d\n" wl 25 dan 26 wangl
# 单引号
printf '%d %s\n' 25 dan 26
1
2
3
4
5
wl 25
dan 26
wangl 0
25 dan
26

五、test

test一般用于判断某个条件是否成立,可以进行数值、字符串和文件的判断。

数值

数值判断的参数:

  • -eq 相等为真
  • -ne 不等于为真
  • -gt 大于为真
  • -ge 大于等于为真
  • -lt 小于为真
  • -le 小于等于为真
1
2
3
4
5
6
7
8
age1=10
age2=10
if test $age1 -eq $age2
then
echo equal
else
echo not equal
fi
1
2
$ ./test.sh
equal

字符串

字符串判断时,只可以使用以下四种参数:

  • = 或 == 等于为真(这个与大多数编程里不同)
  • != 不等于为真
  • -z 字符串长度为0时为真
  • -n 字符串长度不为0时为真
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
name1=wl
name2=wang
name3=wl
if test $name1 != $name2
then
echo "$name1 != $name2"
fi

if test $name1 = $name3
then
echo "$name1 == $name2"
fi
if test -n $name1
then
echo "$name1 is not NULL"
fi
if test -z $name4
then
echo "$name4 is NULL"
fi
1
2
3
4
5
$ ./test.sh
wl != wang
wl == wang
wl is not NULL
is NULL

如果需要进行字符串之间的比较,可以使用转义形式,即 \<\>进行判断:

1
2
3
4
5
6
7
8
name1=wl
name2=wang
if [ $name1 \< $name2 ]
then
echo "$name1 < $name2"
else
echo "$name1 > $name2"
fi
1
2
$ ./test.sh
wl > wang

文件

文件判断时,主要的参数有:

  • -e 文件存在为真
  • -r 文件存在且可读为真
  • -w 文件存在且可写为真
  • -x 文件存在且可执行为真
  • -d 文件存在且为目录为真
  • -f 文件存在且为普通文件为真
1
2
3
4
5
6
7
8
9
10
11
12
13
cd ../
if test -e ./shell_learn/test.sh
then
echo "test.sh exist"
fi
if test -x ./shell_learn/test.sh
then
echo "test.sh executable"
fi
if test -d ./shell_learn
then
echo "./shell_learn directory exist"
fi
1
2
3
4
$ ./test.sh
test.sh exist
test.sh executable
./shell_learn directory exist

逻辑操作符和[]

test一般会与if一起使用,可以使用[]代替test,比如

1
2
3
4
5
6
7
8
9
cd ../
if test -d ./shell_learn
then
echo "./shell_learn directory exist"
fi
if [ -d ./shell_learn ]
then
echo "./shell_learn directory exist"
fi
1
2
3
$ ./test.sh
./shell_learn directory exist
./shell_learn directory exist

在test和[]判断的条件中,可以使用与(-a)、或(-o)和非(!)进行条件逻辑连接,优先级方面,非(!)最高,与(-a)其次,或(-o)最低。

1
2
3
4
5
6
7
8
9
cd ../
if [ -d ./shell_learn -a -e ./shell_learn/test.sh ]
then
echo "./shell_learn directory exist and test.sh exist"
fi
if [ -d ./shell_learn -o -e ./shell_learn/xxxtest.sh ]
then
echo "./shell_learn directory exist or test.sh exist"
fi
1
2
3
$ ./test.sh
./shell_learn directory exist and test.sh exist
./shell_learn directory exist or test.sh exist