文章目录
-
- [0 前言](#0 前言)
- [1 变量](#1 变量)
-
- [1.1 基本使用方式](#1.1 基本使用方式)
- [1.2 特殊变量](#1.2 特殊变量)
- [1.3 数组变量](#1.3 数组变量)
- [2 符号](#2 符号)
-
- [2.1 字符串符号](#2.1 字符串符号)
- [2.2 分号](#2.2 分号)
- [2.3 运算符号](#2.3 运算符号)
- [2.4 输入输出重定向符号](#2.4 输入输出重定向符号)
- [3 条件语句](#3 条件语句)
-
- [3.1 if-else](#3.1 if-else)
- [3.2 switch-case](#3.2 switch-case)
- [4 循环语句](#4 循环语句)
-
- [4.1 for循环](#4.1 for循环)
- [4.2 while循环](#4.2 while循环)
- [4.3 循环结束](#4.3 循环结束)
- [5 函数](#5 函数)
- [6 常用基本命令](#6 常用基本命令)
-
- [6.1 echo](#6.1 echo)
- [6.2 printf](#6.2 printf)
- [6.3 export](#6.3 export)
- [6.4 source](#6.4 source)
- [6.5 read](#6.5 read)
0 前言
因为工作需要频繁接触Linux,同时工作内容也和Linux相关,感觉有必要系统学习一下bash脚本的语法,特此记录。
本文以菜鸟教程为基础,提炼最常用内容,并结合工作经历进行补充,适合有一定编程基础的新手入门。
bash脚本基本格式
bash脚本第一行都需要加上#!/bin/bash,这个是用来指定脚本默认的执行程序的,所以理论上也可以选择其他的执行程序,比如#!/bin/sh。
1 变量
1.1 基本使用方式
-
定义变量 :
val="test"变量可以被重复定义,直接多次赋值即可,不需要其他操作。注意,等号两边不能加空格(不是语法问题,是可能导致值错误)。常用的变量主要有两种类型,一个是数值型,还有就是字符串型,后者要加引号。
-
使用变量 :
echo $val或者echo ${val}变量使用时需要加上一个美元符号,或者是使用大括号括起来,这样可以更加清晰地界定变量名称的范围。
-
删除变量 :
unset val删除该变量就不能再使用,如果要使用就需要重新定义。
-
只读变量 :
readonly val这样可以设置该变量为只读变量,不能对该变量重新定义修改。
1.2 特殊变量
$1,$2:类似这样一个美元符号加上一个数字的组合,是表示脚本执行时的参数。如果是在函数体内部,则表示函数的参数。如果数字大于10,则需要加大括号,如${10}$0:表示这个脚本文件名(包含路径)。$?:这个表示该指令上一条指令执行返回的结果。
1.3 数组变量
定义一个数组变量的基本格式如下
bash
array_name=(value0 value1 value2 value3)
注意,这里元素之间分隔是不要逗号的,直接用空格分隔,或者换行分隔也行。另外还可以单独定义数组的各个分量,比如:
bash
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
而且这个数组的下标范围不需要像C语言那样先设置好,这个是没有数字范围限制的。
如果要取数组中的元素,则可以通过${数组名[下标]}的方式来读取。这里面的中括号两侧不需要加空格。如果要获取数组的长度,可以按照下面这些方式来实现。
bash
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
length=${#array_name[n]}
这里如果不加
#,单纯使用${array_name[*]}或者${array_name[@]}得到的是数组中所有的元素。
这里顺便补充一下这个#的作用,除了可以获取数组的长度外,还可以获取字符串的长度,比如
bash
string="abcd"
echo ${#string} # 输出 4
关联数组/字典
bash还支持关联数组或者说字典,即索引内容可以是任意的字符串或者其他整数,比如
bash
site["google"]="www.google.com"
但这里要提前声明这个数组是关联数组,使用declare -A的方式。所以一般使用的格式如下所示。
bash
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"
然后和上面获取所有数组元素一样,这里也可以获取所有key:
bash
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"
echo "数组的键为: ${!site[*]}"
echo "数组的键为: ${!site[@]}"
2 符号
2.1 字符串符号
当需要给变量赋字符串时,就需要用引号引起来,既可以使用单引号,也可以使用双引号。如果使用单引号的话,字符串会原样输出,里面的变量和转义符号无效。而双引号则可以使用变量和转义符号等。
此外,字符串也可以直接在赋值的时候就拼接,这里既可以使用单引号也可以使用双引号。比如
bash
name="test"
greeting="hello, "$name" !"
# hello, test !
2.2 分号
bash代码不需要行末以分号结尾,但有时候仍然可以看到使用分号的情况,其实就是分隔命令,这样可以实现一行写好几条指令,方便在命令终端直接执行。
另外如果是两个分号;;,主要是用在case语句中,用来表示一种case结束,相当于C语言中的break。
2.3 运算符号
算术运算符
bash脚本是不支持数学运算的,如果要进行数学运算,需要使用指令,最常用的就是expr,举个例子
bash
val=`expr 2 + 2`
echo "两数之和为:$val"
这里有几个注意点:
- 表达式和运算符之间要有空格,即
2 + 2 - 表达式要使用
反引号给括起来,注意不是单引号
关系运算符
主要是比较数值的大小或判断是否相等,也就是形成条件表达式,主要有以下几种
| 运算符 | 说明 | 举例 |
|---|---|---|
| -eq | 检测两个数是否相等,相等返回 true。 | [ a -eq b ] 返回 false。 |
| -ne | 检测两个数是否不相等,不相等返回 true。 | [ a -ne b ] 返回 true。 |
| -gt | 检测左边的数是否大于右边的,如果是,则返回 true。 | [ a -gt b ] 返回 false。 |
| -lt | 检测左边的数是否小于右边的,如果是,则返回 true。 | [ a -lt b ] 返回 true。 |
| -ge | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | [ a -ge b ] 返回 false。 |
| -le | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | [ a -le b ] 返回 true。 |
同样,这里也有几个注意点:
- 和前面运算符一样,这里的中括号是需要加空格的。结合前面的内容,可以总结为符号两边都要加空格,除了赋值和美元符号(变量相关)
- 记忆方式:
eq是equal的缩写,然后带e的都表示有等号,lt是lower than的缩写,gt是greater than的缩写,那自然可以得到le是小于等于,ge是大于等于。
另外,如果想用最简单的< > <= >=这些符号,可以使用(( )),把比较表达式放在双小括号内,但记得也需要加空格。 比如 (( $a > 10 ))
布尔运算符
| 运算符 | 说明 | 举例 |
|---|---|---|
! |
非运算,表达式为 true 则返回 false,否则返回 true。 | [ ! false ] 返回 true。 |
-o |
或运算,有一个表达式为 true 则返回 true。 | [ a -lt 20 -o b -gt 100 ] 返回 true。 |
-a |
与运算,两个表达式都为 true 才返回 true。 | [ a -lt 20 -a b -gt 100 ] 返回 false。 |
逻辑运算符
| 运算符 | 说明 | 举例 |
|---|---|---|
&& |
逻辑的 AND | [[ a -lt 100 \&\& b -gt 100 ]] 返回 false |
| ` | ` |
字符串运算符
| 运算符 | 说明 | 举例 |
|---|---|---|
= |
检测两个字符串是否相等,相等返回 true。 | [ a = b ] 返回 false。 |
!= |
检测两个字符串是否不相等,不相等返回 true。 | [ a != b ] 返回 true。 |
-z |
检测字符串长度是否为0,为0返回 true。 | [ -z $a ] 返回 false。 |
-n |
检测字符串长度是否不为 0,不为 0 返回 true。 | [ -n "$a" ] 返回 true。 |
$ |
检测字符串是否不为空,不为空返回 true。 | [ $a ] 返回 true。 |
除了=判断字符串是否相等外,还可以使用双等号==,但一般需要加双中括号,[[ $s == "test.bin" ]],在单中括号中使用双等号是兼容性差的写法,部分sh可能不支持。 此外,双括号内还可以使用模式匹配和正则表达式。比如
bash
[[ $var == *.txt ]] # 模式匹配
[[ $var =~ ^[0-9]+$ ]] # 正则表达式
文件测试运算符
| 操作符 | 说明 | 举例 |
|---|---|---|
-d file |
检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] 返回 false。 |
-f file |
检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] 返回 true。 |
-r file |
检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回 true。 |
-w file |
检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
-x file |
检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回 true。 |
-s file |
检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] 返回 true。 |
-e file |
检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true。 |
2.4 输入输出重定向符号
输出重定向
bash
command1 > file1
这个是指将command1指令执行的结果保存在file1中,而且是完全覆盖;
如果是这样:
bash
command1 >> file1
则是指将内容追加在file1后面。
输入重定向
bash
command1 < file1
把文件作为参数传入,然后再执行指令。
特殊重定向
如果希望执行某个指令,但又不希望在屏幕上显示输出结果,就可以将输出重定向到/dev/null,他是一个特殊的文件,写入到他的内容都会被丢弃。
bash
$ command > /dev/null
3 条件语句
类比C语言,条件语句主要是两种,一个是if-else语句,一个是switch-case语句,bash脚本中也都有,下面分别介绍。
3.1 if-else
基本的语法格式如下所示。
bash
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
可以看到,条件选择语句没有大括号限定范围,而是使用fi,即if倒着写来表示语句的结束,后面要介绍的case语句也是一样。另外elif和else部分是可选的部分,不一定有。也可以写在同一行,不过语句后面要加上分号;
bash
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
3.2 switch-case
其基本的语法格式如下所示。
bash
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
注意,这里每个选项都需要加一个右括号,然后每个case最后用两个分号分隔,表示break语句,最后要加上esac和前面的case呼应,表示一个区域,等同于大括号的作用。
4 循环语句
和C语言一样,循环语句也具有两种,一种是for循环,一种是while循环。
4.1 for循环
其基本的代码格式如下所示。
bash
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
这里do...done就是用来表示大括号的,中间是循环体。
上面in后面的内容可以是多个变量,也可以是一个数组变量,或者通配符,比如
bash
for file in *
do
echo "处理: $file"
done
4.2 while循环
基本的语法格式如下所示
bash
while condition
do
command
done
4.3 循环结束
在bash中也有break和continue指令。
bash
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
break
;;
esac
done
注意这里的break指令不是跳出switch-case语句,而是跳出外面的while循环语句。
5 函数
shell脚本中也可以自定义函数,定义好之后就可以在shell脚本中随便调用。函数定义格式如下所示。
bash
[ function ] funname ()
{
action;
[return int;]
}
注意,这里中括号的部分是可选的,也就是说定义函数时可以不写 function ,也可以没有返回值。
函数调用时,直接使用函数名字即可,不需要加括号。
6 常用基本命令
6.1 echo
这个应该是最常用的bash指令了。除了最常见的输出信息到终端外,这个指令还可以结合其他符号实现一些高级功能。
常用参数
-e:识别转义字符,包括终端指定颜色的指令也需要这个-n:不换行输出,指这一行末尾不加换行符,默认是会加换行符号的。
结合管道运算符|
有时候需要调用函数或者另外的脚本且需要传递参数时,就可以使用echo para | other_script.sh这种格式来实现调用其他脚本并传递参数。
结合输入输出重定向符号>,>>
结合输入输出重定向符号,可以实现文本文件的写入和添加。
6.2 printf
除了echo指令外,printf也可以用来输出内容到终端,而且支持更加复杂的格式化输出,其用法和C语言中的printf差不多。
bash
# 整数
printf "Decimal: %d\nHex: %x\nOctal: %o\n" 255 255 255
# 浮点数
printf "Float: %f\nScientific: %e\n" 3.14159 3.14159
# 字符串
printf "Name: %s\n" "Bob"
# 字符
printf "First letter: %c\n" "A"
6.3 export
export 用于声明一个环境变量:export variable=value,该环境变量只在本进程和其子进程中可以访问。
如果是在系统级的配置文件中,如/etc/profile中 export 一个变量,那么这个变量就会在整个系统运行期间都起作用。即使新开shell会话连接甚至,重启了服务器仍可生效。
如果在用户级 ~/.bash_profile 之类的文件中,则每次启动shell 都会去读这个文件,所以每次打开shell也是可以取到这个值的。
如果在某一次运行中,手动 export 一个变量,则只在这次shell的使用中,才能访问这个变量,或者在该shell启动的启动程序中,也是可以访问这个变量的,因为它们是这个shell的子进程。新开shell连接及重启服务器都会失效。
总之,export 作用就是 把本地变量变成全局变量(实际中叫环境变量)
6.4 source
使shell读入指定的shell脚本文件并依次执行文件中的所有语句。
source命令通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录。
bash
source filename
. filename # 和上面等价
另外还需要注意source和直接执行脚本./other.sh的区别,后者会开启一个子shell,会继承父shell的环境变量,但子shell执行得到的环境变量不影响父shell。而source指令是把脚本复制到本窗口继续执行,不会新建shell,所以环境变量都是共用的,也会互相影响。
6.5 read
如果脚本中需要输入一些内容,就可以使用read指令,最常见的用法就是
bash
read -p "提示信息" val
指将输入的内容存在val变量中。其他用法可以参考这个链接。