Shell 编程
1. HelloWorld
1.1. 第一个 Shell 脚本
创建一个 Shell 脚本,输出“hello world!”。内容如下:
#!/bin/bash
echo "hello world!" # echo 用于输出
#!
告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序。在一般情况下,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh
,它同样也可以改为 #!/bin/bash
。
注释
以 #
开头的行就是注释,会被解释器忽略。
多行注释
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
EOF 也可以使用其他符号:
:<<'
注释内容...
注释内容...
注释内容...
'
:<<!
注释内容...
注释内容...
注释内容...
!
1.2. 执行 Shell 脚本
Shell 脚本后缀为 .sh;
脚本名称一般使用英文,表示脚本功能,单词使用下划线连接;
脚本名称不能使用特殊符号、空格、纯数字。
1.2.1. 方式一:作为可执行程序
将上面的代码保存为 hello.sh,并 cd 到相应目录:
- 脚本需要有可执行权限,赋予权限:
chmod 744 hello.sh
- 执行脚本:
./hello.sh
**注意:**一定要写成 ./hello.sh
,而不是 hello.sh
,运行其它二进制的程序也一样,直接写 hello.sh,linux 系统会去 PATH 里寻找有没有叫 hello.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 hello.sh 是会找不到命令的,要用 ./test.sh
告诉系统,就在当前目录找。
1.2.2. 方式二:作为解释器参数
直接运行解释器,其参数就是 shell 脚本的文件名,如:sh 脚本的绝对路径或相对路径
。
这种方式运行的脚本,不需要在第一行指定解释器信息,也不需要赋予脚本执行权限。(不推荐)
2. 变量
2.1. 定义变量
基本语法:变量名=变量值
注意:等号左右不能由空格!
变量命名规则:
- 命名只能使用英文字母、数字和下划线,且不能以数字开头;
- 不能使用关键字;
- 一般为大写(此为规范,非规则)。
2.2. 使用变量
使用变量即取变量值
基本语法:$变量名
或 ${变量名}
说明:变量名外面的花括号是可选的,加花括号是为了帮助解释器识别变量的边界。推荐给所有变量加上花括号,这是个好的编程习惯。
2.3. 删除变量
基本语法:unset 变量名
说明:变量被删除后不能再次使用。
2.4. 只读变量
基本语法:readonly 变量名
说明:使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
2.5. 命令替换
命令替换是指将命令返回值赋给变量。
基本语法:变量名=
命令`` 或 变量名=$(命令)
说明:$()
仅在 Bash Shell 中有效,而````可在多种 Shell 中使用。
MY_DATE=`date`
RESULT=`ls -l /home`
2.6. 作用域
根据作用域划分,Shell 变量可以分为:局部变量、全局变量、环境变量。
2.6.1. 局部变量
只能在函数内部使用的变量称为局部变量(local variable)。
Shell 函数中定义的变量默认也是全局变量,需要使用local
关键字进行修饰,具体可见 9. 函数 。
2.6.2. 全局变量
只能在当前 Shell 进程的变量称为全局变量(global variable)。
在 Shell 中定义的变量,默认就是全局变量。
2.6.3. 环境变量
除了在当前 Shell 进程,还可以在子进程使用的变量称为环境变量(environment variable)。
变量需要使用export
导出才是环境变量,不然子进程无法使用,如下所示。
export 变量名=变量值
# 或
变量名=变量值
export 变量名
xmh@xmh:~$ a=22 #定义一个全局变量
xmh@xmh:~$ echo $a #在当前Shell中输出a,成功
22
xmh@xmh:~$ bash #进入Shell子进程
xmh@xmh:~$ echo $a #在子进程中输出a,失败
xmh@xmh:~$ exit #退出Shell子进程,返回上一级Shell
exit
xmh@xmh:~$ export a #将a导出为环境变量
xmh@xmh:~$ bash #重新进入Shell子进程
xmh@xmh:~$ echo $a #在子进程中再次输出a,成功
22
xmh@xmh:~$ exit #退出Shell子进程
exit
xmh@xmh:~$ exit #退出父进程,结束整个Shell会话
注意:
- 两个没有父子关系的 Shell 进程是不能传递环境变量的,并且环境变量只能向下传递而不能向上传递,即“传子不传父”。
- 通过
export
导出的环境变量只对当前 Shell 进程以及所有的子进程有效,如果最顶层的父进程被关闭了,那么环境变量也就随之消失了,其它的进程也就无法使用了,所以说环境变量也是临时的。 - 如果希望环境变量在所有 Shell 进程中都有效,需要将变量写入 Shell 配置文件中才能达到这个目的!Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件中,那么每次启动进程都会定义这个变量。在 Linux 中,内置了一些环境变量,可以使用
env
命令可以显示所有环境变量。- PATH:命令搜索路径
- USER:当前用户
- HOME:当前用户的主目录
- SHELL:当前使用的 Shell
- UID:当前用户的 UID
- HOSTNAME:主机名
- TERM:当前使用的终端类型
- PWD:当前工作路径
3. 特殊变量
特殊变量是 shell 官方已经定义好的变量,可以直接在 shell 脚本中使用。特殊变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行。
特殊变量有以下几种:
- 特殊状态变量
- 特殊位置变量
- 特殊扩展变量
3.1. 特殊状态变量
$$
:当前执行Shell脚本的进程的进程号(PID)。$!
:上一个后台运行的进程的进程号(PID)。$?
:上一个命令的执行状态返回值(0成功,非0为失败)。具体细节如下:- 判断命令、脚本或者函数等程序是否执行成功。
- 在脚本中调用执行
exit 数字
,会返回这个数字给$?
变量。 - 在函数中,会通过
return 数字
,把数字以函数返回值的形式传给$?
。
$_
:在此之前执行的命令或脚本的最后一个参数。
3.2. 特殊位置变量
$0
:当前执行的 Shell 脚本文件名,如果执行脚本时候加了路径,那就包含路径路径跟脚本文件名一起输出。$n
:当前执行的Shell脚本或函数的第 n 个参数。$1- 9 代表第 1 个到第 9 个参数,后面的参数表示时需用 ‘ ‘ 包裹,如 ‘ 9 代表第1个到第9个参数,后面的参数表示时需用`{}`包裹,如` 9代表第1个到第9个参数,后面的参数表示时需用‘‘包裹,如‘{10}` 。可由此传递参数。$#
:当前执行的Shell脚本或函数接收参数的个数。$*
:当前执行的Shell脚本或函数接收的所有参数,**$***
** 把所有的参数看成一个整体,此变量是一个数组。**$@
:当前执行的Shell脚本或函数接收的所有参数,**$@**
** 把每个参数区分对待,此变量是一个数组。**
3.3. 特殊扩展变量
${parameter:-word}
:如果 parameter 的变量值为空或者未赋值,则返回 word 字符串并代替变量的值(防止变量为空或没定义报错)${parameter:=word}
:同上${parameter:?word}
:如果 parameter 的变量值为空或者未赋值,则 word 字符串就作为标准错误输出(捕捉由于变量未定义导致的错误,并退出)${parameter:+word}
:如果 parameter 不为空或者未赋值,则使用 word 字符串将代替变量的值,否则什么也不做。
4. 字符串
字符串有两种定义方式,用单引号 ''
或 双引号 ""
定义。
4.1. 单引号
- 单引号中所有的字符都会原样输出,也就是说单引号中的变量是无效的。
- 单引号中不能再出现单引号(对单引号转义符也无效),但可成对出现,作为字符串拼接使用。
4.2. 双引号
- 双引号中可以有变量。
- 可以使用转义符。
STR='test line'
echo 'It is a ${STR}' # 输出:It is a ${STR}
echo "It is a \"${STR}\"" # 输出:It is a "test line"
4.3. 操作字符串
4.3.1. 拼接字符串
- 双引号字符串拼接
STR='test line'
# 方式一:使用双引号
echo "It is a "test line"" # 输出:It is a test line
echo "It is a "${STR}"" # 输出:It is a test line
# 方式二:引用变量
echo "It is a ${STR}" # 输出:It is a test line
- 单引号字符串拼接
STR='test line'
# 只能使用单引号拼接
echo 'It is a '${STR}'' # 输出:It is a test line
4.3.2. 获取字符串长度
基本语法:${#变量名}
STR='test line'
echo ${#STR} # 输出:9
4.3.3. 提取子字符串
基本语法:${变量名:起始索引:结束索引}
注:第一个字符索引值为 0 。
STR='test line'
echo ${STR:1:3} # 输出:est
4.3.4. 使用 expr 操作字符串
expr
是 evaluate expressions 的缩写,译为“表达式求值”。使用expr
操作字符串,索引从 1 开始。
- 获取字符串长度:
expr length 字符串
- 提取子字符串:
expr substr 字符串 起始位置 结束位置
- 获取某字符在字符串中首次出现位置(依次寻找字符序列中的字符,返回先找到的字符位子):
expr index 字符串 字符序列
- 模式匹配(正则表达式默认带有
^
;成功返回匹配字符长度,否则为 0):expr match 字符串 正则表达式
- 模式匹配(正则表达式默认带有
^
;成功返回匹配字符长度,否则为 0):expr 字符串 : 正则表达式
str="you are the best"
expr length "$str"
16
expr substr "$str" 13 16
best
expr index "$str" vut
3
expr match "$str" ".*a"
5
5. 数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
索引由 0 开始编号。获取数组中的元素要利用索引,索引可以是整数或算术表达式,其值应大于或等于 0。
5.1. 定义数组
基本语法:
数组名=(值1 值2 ... 值n)
数组名[索引值]=元素值
# 形式一:
ARR1=("ARR1_first" "ARR1_second" "ARR1_third")
# 形式二:
ARR2=(
"ARR2_first"
"ARR2_second"
"ARR2_third"
)
# 形式三:
ARR3[0]="ARR3_first"
ARR3[1]="ARR3_second"
ARR3[9]="ARR3_tenth"
5.2. 读取数组
基本语法:${数组名[下标]}
注:${数组名[@]}
、${数组名[*]}
可以获取数组的所有元素值
echo ${ARR1[0]}
echo ${ARR1[@]}
echo ${ARR2[0]}
echo ${ARR2[@]}
echo ${ARR3[0]}
echo ${ARR3[4]}
echo ${ARR3[9]}
echo ${ARR3[@]}
# 输出
ARR1_first
ARR1_first ARR1_second ARR1_third
ARR2_first
ARR2_first ARR2_second ARR2_third
ARR3_first
ARR3_tenth
ARR3_first ARR3_second ARR3_tenth
5.3. 获取元信息
- 取得数组元素的个数:
${#数组名[@]}
- 取得数组元素的个数:
${#数组名[*]}
- 取得数组单个元素的长度:
${#数组名[索引值]}
6. 数学表达式
6.1. 算术运算符
加减乘除等
+
:加法(正号)-
:减法(负号)*
:乘法/
:除法%
:取余(取模)**
:幂运算++
:自增--
:自减
比较
==
:相等。用于比较两个数字,相同则返回 true。!=
:不相等。用于比较两个数字,不相同则返回 true。<
:小于<=
:小于等于>
:大于>=
:大于等于
赋值
=
+=
-=
*=
/=
%=
位运算
<<
:向左移位>>
:向右移位~
:按位取反|
:按位或&
:按位与^
:按位异或
6.2. 逻辑运算符
&&
:逻辑与||
:逻辑或!
:逻辑非
6.3. 命令
在 Shell 中,每一个变量的值都是字符串,无论给变量赋值时带不带引号,变量之都会以字符串的形式存储。因此要进行数学计算,需要使用数学计算命令。
6.3.1. (())
(())
是 bash 中专门进行整数运算(只能进行整数运算,不能对浮点数或者字符串进行运算)的命令,效率高,写法灵活。
- 基本语法:
((数学表达式))
。 (())
中的表达式可以有 1 个或多个,多个表达式使用逗号,
分割。对于多个表达式的情况,以最后一个表达式的值作为整个(())
命令的执行结果。- 在
(())
中使用变量无需加上$
前缀,(())
会自动解析变量名。 - 在
(())
前面加上$
符号获取命令的执行结果。 (())
也可以进行逻辑运算。
xmh@WSL:~$ ((a=10+5))
xmh@WSL:~$ echo $a
15
xmh@WSL:~$ ((b=10+1,c=10+2))
xmh@WSL:~$ echo $((b=10+1,c=10+2))
12
xmh@WSL:~$ echo $((c=a+b))
26
xmh@WSL:~$ echo $((a==b && b==c))
0
xmh@WSL:~$ echo $((a++))
15
xmh@WSL:~$ echo $((++a))
17
6.3.2. let
let
只能进行整数运算(只能进行整数运算,不能对浮点数或者字符串进行运算)。
- 基本语法:
let 数学表达式
或者let "数学表达式"
或者let "数学表达式"
- 当表达式中含有 Shell 特殊字符(例如
|
)时,需要用双引号""
或者单引号''
将表达式包围起来。 let
中的表达式可以有 1 个或多个,多个表达式使用逗号空格分割。- 在
let
中使用变量无需加上$
前缀,let
会自动解析变量名。 let
命令会将计算结果丢弃,需要使用变量在let
中接收结果。let
支持逻辑运算。
xmh@WSL:~$ let a=10+5
xmh@WSL:~$ echo $a
15
xmh@WSL:~$ let b=a+1 c=a+2
xmh@WSL:~$ echo $b
16
xmh@WSL:~$ echo $c
17
xmh@WSL:~$ let d=a++
xmh@WSL:~$ echo $d
15
xmh@WSL:~$ let "e=(15==15&&16==16)"
xmh@WSL:~$ echo $e
1
6.3.3. $[]
$[]
只能进行整数运算。
- 基础语法:
$[数学表达式]
$[]
只支持 1 个表达式,不能有多个表达式。$[]
使用变量时,可加$
,也可以不加。- 不能单独使用
$[]
,必须接收其计算结果。 $[]
支持逻辑运算。
xmh@WSL:~$ $[5+6]
11: command not found
xmh@WSL:~$ echo $[5+6]
11
xmh@WSL:~$ echo $[5==6]
0
xmh@WSL:~$ echo $[5==6||6==6]
1
6.3.4. expr
- 基本语法:
expr 数学表达式
- 只能计算 1 个表达式。
- 出现在表达式中的运算符、数字、变量和小括号的左右两边至少要有一个空格,否则会报错。
- 有些特殊符号必须用反斜杠
\
进行转义,比如乘号*
和小括号()
,如果不用\
转义,那么 Shell 会把它们误解为正则表达式中的符号(*
对应通配符,()
对应分组)。 - 使用变量时要加
$
前缀。 - 只支持
+
-
*
/
%
>
>=
!=
=
<=
<
&
|
xu_menghao@WSL:~$ expr 2 + 3
5
xu_menghao@WSL:~$ expr 4 * 5
expr: syntax error: unexpected argument ‘test’
xu_menghao@WSL:~$ expr 4 \* 5
20
xu_menghao@WSL:~$ expr 4 / 2
2
xu_menghao@WSL:~$ expr 4 - 2
2
xu_menghao@WSL:~$ expr 4 \* \( 2 + 5 \)
28
6.3.5. bc
Linux 下的一个计算器程序,可以处理整数和浮点数。
Linux bc命令:一款数学计算器
6.3.6. declare -i
使用declare -i
将变量定义为整数,然后再进行数学运算。
- 仅支持最基本的数学运算:加减乘除和取余,不支持逻辑运算、自增自减等。
- 除了将参与运算的变量定义为整数,还得将承载结果的变量定义为整数,而且只能用整数类型的变量来承载运算结果,不能直接使用
echo
输出。
declare -i m n ret
m=10
n=30
ret=$m+$n
echo $ret
40
ret=$n/$m
echo $ret
3
7. 条件表达式
7.1. test 与 [
在 Shell 中,可以使用内置命令test
与[
处理条件表达式。[
是test
的近似命令,只有一处不同,**[**
需要以**]**
作为参数结尾以匹配**[**
,因此**[**
命令在形式上如下所示:**[ 条件表达式 ]**
**。**所以只需要会test
,同样就会使用[
。
test
命令、[
命令提供四类操作符:关系操作符、字符串操作符、文件操作符、其他操作符。
test
命令、[
命令对条件表达式求值,如果求值为true
,则返回 0,如果求值为false
或给出无效参数,则返回 1 。
7.1.1. 关系操作符
关系操作符只支持数字,不支持字符串,除非字符串的字面量是数字。
ARG1 -eq ARG2
:如果两个数相等,则为 true。ARG1 -ne ARG2
:如果两个数不相等,则为 true。ARG1 -gt ARG2
:如果 ARG1 大于 ARG2 ,则为 true。ARG1 -lt ARG2
:如果 ARG1 小于 ARG2 ,则为 true。ARG1 -ge ARG2
:如果 ARG1 大于等于 ARG2 ,则为 true。ARG1 -le ARG2
:如果 ARG1 小于等于 ARG2 ,则为 true。
7.1.2. 字符串操作符
STRING1 = STRING2
:如果两个字符串相等,则为 true。STRING1 != STRING2
:如果两个字符串不相等,则为 true。STRING1 < STRING2
:如果 STRING1 按字典顺序排在 STRING2 之前,则为 true。STRING1 > STRING2
:如果 STRING1 按字典顺序排在 STRING2 之后,则为 true。-z STRING
:如果字符串长度为 0,则为 true。-n STRING
:如果字符串长度不为 0,则为 true。
注意:使用>
、<
需要使用\
转义。
7.1.3. 文件操作符
用于检测文件的各种属性。如果 FILE 是软链接,则除-h
和-L
之外的所有文件操作符都作用于软链接的目标,而不是软链接本身。
-a FILE
:如果文件存在,则为 true。-b FILE
:如果文件是块设备文件,则为 true。-c FILE
:如果文件是字符设备文件,则为 true。-d FILE
:如果文件是目录,则为 true。-e FILE
:如果文件(包括目录)存在,则为 true。-f FILE
:如果文件是普通文件(既不是目录,也不是设备文件),则为 true。-g FILE
:如果文件设置了 SGID 位,则为 true。-h FILE
:如果文件存在并且是一个符号链接,则为 true。-L FILE
:如果文件存在并且是一个符号链接,则为 true。-k FILE
:如果文件设置了粘着位(Sticky Bit),则为 true。-p FILE
:如果文件是有名管道,则为 true。-r FILE
:如果文件可读,则为 true。-s FILE
:如果文件不为空(文件大小大于0),则为 true。-S FILE
:如果文件是 socket,则为 ture。-t FD
:如果在终端上打开 FD,则为 true.-u FILE
:如果文件设置了 SUID 位,则为 true。-w FILE
:如果文件可写,则为 true。-x FILE
:如果文件可执行,则为 true。-O FILE
:如果该文件实际上为你所有,则为 true。-G FILE
:如果该文件实际上为你的用户组所有,则为 true。-N FILE
:如果文件自上次读取以来已被修改,则为 true。FILE1 -nt FILE2
:如果 FILE1 比 FILE2 更新(根据修改日期),则为 true。FILE1 -ot FILE2
:如果 FILE1 比 FILE2 更老(根据修改日期),则为 true。FILE1 -ef FILE2
:如果 FILE1 是到 FILE2 的硬链接,则为 true。
7.1.4. 其他操作符
-o OPTION
:如果 Shell 选项 option 被启用,则为true。-v VAR
:如果设置了 Shell 变量 VAR ,则为 true。-R VAR
:如果设置了 Shell 变量 VAR 并且是一个名称引用,则为 true。EXPR1 -a EXPR2
:如果 EXPR1 和 EXPR2 都为 true 则为 true。EXPR1 -o EXPR2
:如果 EXPR1 或 EXPR2 为 true 则为 true。! EXPR
:如果 EXPR 为 false 则为 true。
7.2. [[
[[
也是内置命令,用于条件表达式求值。与**[**
类似,**[[**
需要以**]]**
作为参数结尾以匹配**[[**
,因此**[[**
命令在形式上如下所示:**[[ 条件表达式 ]]**
。
[[
支持上述关系操作符、字符串操作符、文件操作符,但不支持-a
、-o
,-a
功能使用&&
替代,-o
功能使用||
替代,且&&``||
分别是短路与、短路或。
[[
还支持使用小括号()
包裹表达式以分隔:( EXPRESSION )
,会返回 EXPRESSION 的值。
[[
支持更多的字符串操作。
- 使用
==
和!=
操作符时,将操作符右侧的字符串用作模式,并执行模式匹配。 - 使用
=~
操作符时,将操作符右侧的字符串作为正则表达式进行匹配。
test
命令、[
命令对条件表达式求值,如果求值为true
,则返回 0,如果求值为false
或给出无效参数,则返回 1 。
注:[[]]
对数字的比较仍然不友好,因此使用if
判断条件时,用(())
来处理整型数字,用[[]]
来处理字符串或者文件。
8. 流程控制
8.1. 分支结构
8.1.1. if-elif-else
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi # fi 为 if 倒序
condition
表示条件,一般使用第 7 节中的条件表达式,还可以使用(())
。如果对应条件成立,则执行相应的语句。
#!/bin/bash
str1="abc"
str2="edf"
if [[ $str1 < $str2 ]]
then
echo true
else
echo false
fi
#!/bin/bash
num1=12
num2=15
if ((num1<num2))
then
echo true
else
echo false
fi
8.1.2. case…esac
case expression in
pattern1)
command1
command2
...
commandN
;;
pattern2)
command1
command2
...
commandN
;;
*)
command1
command2
...
commandN
;;
esac # esac 为 case 倒序
case
、in
和 esac
都是 Shell 关键字,expression
表示表达式,pattern
表示匹配模式。
expression
既可以是一个变量、一个数字、一个字符串,还可以是一个数学计算表达式,或者是命令的执行结果,只要能够得到expression
的值就可以。pattern
可以是一个数字、一个字符串,甚至是一个简单的正则表达式。
case
会将expression
的值与pattern1
、pattern2
逐个进行匹配:
- 如果
expression
和某个模式(比如pattern2
)匹配成功,就会执行此模式后面对应的所有语句(该语句可以有一条,也可以有多条),直到遇见双分号;;
才停止;然后整个case
语句就执行完了,程序会跳出整个case
语句,执行esac
后面的其它语句。 - 如果
expression
没有匹配到任何一个模式,那么就执行*)
后面的语句,直到遇见双分号;;
或者esac
才结束。*)
相当于多个if
分支语句中最后的else
部分。最后一个分支*)
并不是什么语法规定,它只是一个正则表达式,*
表示任意字符串,所以不管expression
的值是什么,*)
总能匹配成功。没有也不会影响语句结束。
pattern
部分支持简单的正则表达式,具体来说,可以使用以下几种格式:
*
表示任意字符串。[abc]
表示 a、b、c 三个字符中的任意一个。比如,[15ZH]
表示 1、5、Z、H 四个字符中的任意一个。[m-n]
表示从 m 到 n 的任意一个字符。比如,[0-9]
表示任意一个数字,[0-9a-zA-Z]
表示字母或数字。|
表示多重选择,类似逻辑运算中的或运算。比如,abc | xyz
表示匹配字符串 “abc” 或者 “xyz”。
如果不加以说明,Shell 的值都是字符串,expression
和pattern
也是按照字符串的方式来匹配的。
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac
#!/bin/sh
site="baidu"
case "$site" in
"baidu") echo "百度"
;;
"google") echo "谷歌"
;;
"bing") echo "必应"
;;
esac
8.2. 循环结构
8.2.1. for
# 基本语法1
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
# 基本语法2
for var in ${数组名[*]}
do
command1
command2
...
commandN
done
# 基本语法3
for ((i=1; i<5; i++))
do
command1
command2
...
commandN
done
#!/bin/bash
for str in This is a string
do
echo $str
done
#!/bin/bash
arr=("Hello" "World" "!")
for var in ${arr[*]}
do
echo $var
done
8.2.2. while
while condition
do
command
done
当条件满足时,while
重复地执行一组语句,当条件不满足时,就退出while
循环。
先对condition
进行判断,如果该条件成立,就进入循环,执行while
循环体中的语句,也就是do
和done
之间的语句。这样就完成了一次循环。
每一次执行到done
的时候都会重新判断condition
是否成立,如果成立,就进入下一次循环,继续执行do
和done
之间的语句,如果不成立,就结束整个while
循环,执行done
后面的其它代码。
8.2.3. until
until condition
do
command
done
until
循环和while
循环恰好相反,当判断条件不成立时才进行循环,一旦判断条件成立,就终止循环。
8.2.4. 无限循环
# 方式一
while :
do
command
done
# 方式二
while true
do
command
done
# 方式三
for (( ; ; ))
8.2.5. 跳出循环:break、continue
break
:跳出所有循环(终止执行后面的所有循环)。continue
:跳出当前循环。
9. 函数
shell 编程和其它编程语言一样,有系统函数,也可以自定义函数。
9.1. 常用系统函数
9.1.1. basename
函数功能:返回完整路径最后 /
的部分,常用于获取文件名。
函数使用:basename [pathname] [suffix]
删掉所有的前缀包括最后一个 /
字符,然后将字符串显示出来。如果 suffix 被指定了,basename 会将 pathname 中的 suffix 去掉。
9.1.2. dirname
函数功能:返回完整路径最后 /
的前面的部分,常用于返回路径部分。
函数使用:dirname 文件绝对路径
从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录的部分)。
9.2. 自定义函数
[ function ] funname()
{
action;
[return int;]
}
说明:
[]
内表示可选。return
可选,后跟数字,范围为 0-255 。如果不加return
,将以最后一条命令运行结果,作为返回值。
实例:
#!/bin/bash
# 计算输入两个参数的和
function getSum(){
SUM=$[$NUM1+$NUM2]
echo "$SUM"
}
read -p "请输入第一个数n1" NUM1
read -p "请输入第二个数n2" NUM2
# 调用getSum并传参
getSum $Num1 $Num2
也可以这样写
#!/bin/bash
function getSum(){
read -p "请输入第一个数n1" NUM1
read -p "请输入第二个数n2" NUM2
SUM=$[$NUM1+$NUM2]
echo $SUM
}
# 调用getSum
getSum
10. 文件包含
和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
Shell 文件包含的语法格式如下:
# 方式一
. filename # 注意点号(.)和文件名中间有一空格
# 方式二
source filename
创建两个 shell 脚本文件。
test1.sh 代码如下:
#!/bin/bash
url="http://www.baidu.com"
test2.sh 代码如下
#!/bin/bash
#使用 . 号来引用test1.sh 文件
. ./test1.sh
# 或者使用以下包含文件代码
# source ./test1.sh
echo "百度:$url"
接下来,我们为 test2.sh 添加可执行权限并执行:
$ chmod +x test2.sh
$ ./test2.sh
百度:http://www.baidu.com
注:被包含的文件 test1.sh 不需要可执行权限。
常用命令:read、echo、print、awk、sed、find、grep、eval