bash编程
bash脚本编程是过程式解释编程,其实就是linux命令的堆砌,既然是编程就应该有相应编程语法。
与任何过程式编程一样,过程式编程的特点就是:顺序执行,选择执行,循环执行。
过程式编程:以指令为中心,设计算法,数据服务于算法,过程式编程的灵魂:算法。
1.变量:数值变量,字符变量
bash环境:
本地变量:当前shell进程;
环境变量:当前shell进程及其子进程;
局部变量:某个函数执行过程;
位置参数变量:在脚本中引用传递给脚本的参数;在函数中引用传递给函数的参数;
特殊变量:$? :上一条命令的执行状态结果
$*, [email protected]:引用传递给脚本或函数的参数列表
$#:传递给脚本或函数的参数的个数
$$
1.1bash的变量使用特点:弱类型、无须事先声明;
定义变量名:只能包含数字、字母和下划线;且不能以数字开头;
引用变量:${name}, $name
引用:
弱引用: "", 其内部的变量引用会被替换为变量值;注意:字符变量常用
强引用:‘ ‘,其变量的变量引用会保持原有字符;
命令引用:`COMMAND`, $(COMMAND),引用命令的执行结果;常用 $(COMMAND)
bash变量默认是字符
数值变量,声明为整型:
declare -i name[=value]
let name=value
查看所有变量:set
销毁变量:
自动销毁:shell进程终止;
手动销毁:unset name
环境变量:
被“导出”的本地变量
export name[=value]
declare -x name[=value]
查看所有环境变量:env, printenv, export
销毁环境变量:
unset name
bash脚本编程,其实就是文本文件,要运行脚本,其实就是运行一个bash进程,由bash进程解释并运行。
启动脚本:
(1) # bash /PATH/TO/SCRIPT_FILE
(2) 给bash脚本一个执行权限, 在命令行输入:
# ./PATH/TO/SCRIPT_FILE
shebang:语法,脚本文件顶格输入 #!/bin/bash
第一行:顶格给出shebang
注释行:#
bash的常用选项:
-n: 检查脚本中的语法错误;
-x:调试执行脚本;
bash脚本编程中,对条件的判断非常重要,这也是选择,循环执行语句的基础
条件测试:
界定程序执行环境;
(1) 根据运行的命令的状态结果;
(2) 测试表达式
test EXPRESSION
[ EXPRESSION ] 注意:[ ] 两边留空格
[[ EXPRESSION ]]
整数测试:隐含着做数值大小比较,所以不要给变量引用加引用;
$A -gt $B:是否大于;是则为“真”,否则为“假”;
$A -ge $B: 是否大于等于;
$A -lt $B:是否小于;
$A -le $B: 是否小于等于;
$A -eq $B: 是否等于;
$A -ne $B:是否不等于;
字符串测试:ASCII数值越大,字符比较时其值越大;
"$A" > "$B":是否大于;
"$A" < "$B":是否小于;
"$A" == "$B":是否等于;
"$A" != "$B":是否不等于;
-z "$A":是否为空;空则为“真”,否则为“假”
-n "$A":是否不空;不空则“真”,空则为“假”
注意:应该使用[[ EXPRESSION ]]
文件测试:测试文件的存在性以及属性;
-e $file: 是否存在;存在则为“真”,否则为“假”;
-a $file: 同上;
-f $file:文件是否存在且为普通文件;
-d $file:文件是否存在且为目录;
-h $file:是否存在且为符号链接文件;
-L $file: 同上
-b $file:是否存在且为块设备文件;
-c $file:是否存在且为字符设备文件;
-S $file:是否存在且为套接字文件;
-p $file: 是否存在且为管道文件;
-r $file: 当前用户对文件是否拥有读权限;
-w $file:当前用户对文件是否拥有写权限;
-x $file:当前用户对文件是否拥有执行权限;
-u $file:文件是否拥有SUID权限;
-g $file:文件是否拥有SGID权限;
-k $file:文件是否拥有sticky权限;
-O $file: 当前用户是否为指定文件的属主;
-G $file: 当前用户是否为指定文件的属组;
双目操作符:
$file1 -nt $file2: file1是否新于file2, file1的最近一次的修改时间戳是否晚于file2的;
$file1 -ot $file2: file1是否旧于file2, file1的最近一次的修改时间戳是否早于file2的;
$file1 -ef $file2:file1与file2是否指向了同一个inode;测试二者是否为同一个文件的硬链接;
特殊设备:
/dev/null: 空,bit buckets,吞下所有数据,并直接丢弃;
/dev/zero:吐出一堆0;
脚本参数(位置参数变量):
# ./script.sh /etc/fstab /etc/grub2.cfg
$0 $1 $2
位置参数变量:$1, $2, ...
${10}
特殊变量:
$?: 命令的状态结果;
$#: 传递给脚本或函数的参数的个数;
$*和[email protected]: 引用传递给脚本或函数的参数列表;
shift [n]:轮替
与用户交互:
read命令:
read [options] VAR...
-p "PROMPT"
-t timeout
命令引用:
`COMMAND`, $(COMMAND)
引用命令的执行结果;
算术运算:
+, -, *, /, %, **
(1) $[$A+$B]
(2) $(($A+$B))
(3) let VARIABLE=$A+$B
(4) VARIABLE=$(expr $A + $B)
增强型赋值:
+=
sum=$[$sum+$i]
let sum+=$i
-=, *=, /=, %=
let count=$[$count+1] --> let count+=1 --> let count++
let count=$[$count-1] --> let count-=1 --> let count--
测试表达式:
整数测试:-gt, -lt, -ge, -le, -eq, -ne
字符串测试:==, >, <, !=, -z, -n, =~
注意:
(1) 字符串等会比较测试:[ "$hostname" == ‘localhost‘ ]
(2) 模式匹配测试:[[ "STRING" =~ PATTERN ]]
组合测试条件:
条件间逻辑运算:
与:多个条件要同时满足;
或:多个条件满足其一即可;
非:对指定的条件取反;
表达式组合:
与:[[ CONDITION1 -a CONDITION2 ]]
或:[[ CONDITION1 -o CONDITION2 ]]
非:[ ! CONDITION ]
命令组合:
与:COMMAND1 && COMMAND2 <-- [ EXPR1 ] && [ EXPR2 ]
或:COMMAND1 || COMMAND2
非:! COMMAND
短路操作符:&&
false && true = false
false && false = false
true && false = true
true && true = true
if COMMAND1; then
COMMAND2
fi
短路操作符:||
true || true = true
true || false = true
false || true = true
false || false = false
if ! COMMAND1; then
COMMAND2
fi
COMMAND1 && COMMAND2 || COMMAND3
if COMMAND1; then
COMMAND2
else
COMMAND3
fi
流程控制:选择执行和循环语句
多分支的if语句:
单分支:
if CONDITION; then
if-true-分支
fi
双分支:
if CONDITION; then
if-true-分支
else
if-false-分支
fi
多分支:
if CONDITION1; then
if-CONDITION1-true-分支
elif CONDTION2; then
if-CONDITIO2-true-分支
...
else
if-ALL-false-分支
fi
! CONDITION: 取反
case语句
简洁版多分支if语句;
使用场景:判断某变量的值是否为多种情形中的一种时使用;
语法:
case $VARIABLE in
PATTERN1)
分支1
;;
PATTERN2)
分支2
;;
PATTERN3)
分支3
;;
...
*)
分支n
;;
esac
PATTERN可使用glob模式的通配符:
*: 任意长度的任意字符;
?: 任意单个字符;
[]: 指定范围内的任意单个字符;
a|b: 多选1;
循环语句:for, while, until
for循环:将循环体代码执行0、1或多次;
进入条件:进入循环的条件;
退出条件:循环终止的条件;
for VARIABLE in LIST; do
循环体
done
LIST:是由一个或多个空格或换行符分隔开的字符串组成;
把列表的每个字符串逐个赋值给VARIABLE表示的变量;
for username in user1 user2 user3; do
循环体
done
进入条件:列表非空;
退出条件:列表遍历结束;
while循环:
while CONDTION; do
循环体
done
进入条件:当CONDITION为“真”;
退出条件:当CONDITION为“假”;
while CONDITION; do
循环体
控制变量的修正表达式
done
unitl循环:
until CONDITION; do
循环体
循环控制变量的修正表达式
done
进入条件:当CONDITION为“假”时
退出条件:当CONDITION为“真”时
死循环:
while true; do
循环体
if CONDTION; then
break
fi
done
until false; do
循环体
if CONDITION; then
break
fi
done
函数:
既模块化编程
功能:把脚本文件中的代码分隔为多段,放在不同的文件中
假设/root/bin/srv目录有两个文件:
(1) 函数文件
(2) 脚本文件
为脚本使用配置文件
一个文件中只定义变量
脚本文件source此变量定义的文件
变量的作用域:
局部变量:
local VARIABLE=value
存活时间:
函数执行开始,至函数返回结束;
function: 功能
把一段具有独立功能代码封装在一起,并给予命名;后续用到时,可直接通过给定函数名来调用整体代码;
函数作用:
代码重用;
模块化编程;
函数的使用方法:
先定义:编写函数代码
后调用:给出函数名,还可按需传递参数
定义方法:
(1) function f_name {
函数体
}
(2) f_name() {
函数体
}
调用函数:
f_name [argu1, argu2, ...]
自定义函数状态返回值:
return [#]
0: 成功
1-255:失败
注意:函数代码执行时,一旦遇到return,函数代码终止运行,函数返回;
练习:
1、写一个脚本:如果某路径不存在,则将其创建为目录;否则显示其存在,并显示内容类型;(不要怀疑,就是这么简单)
2、写一个脚本,完成如下功能;判断给定的两个数值,孰大孰小;给定数值的方法:脚本参数,命令交互;(使用read,依然如此简单)
3、求100以内所有奇数之和(至少用3种方法。是的这是我们的作业^_^)
4、写一个脚本实现如下功能:
(1) 传递两个文本文件路径给脚本;
(2) 显示两个文件中空白行数较多的文件及其空白行的个数;
(3) 显示两个文件中总行数较多的文件及其总行数;
5、写一个脚本
(1) 提示用户输入一个字符串;
(2) 判断:
如果输入的是quit,则退出脚本;
否则,则显示其输入的字符串内容;
6、写一个脚本,打印2^n表;n等于一个用户输入的值;(不好意思,我调皮了)
7、写一个脚本,写这么几个函数:函数1、实现给定的两个数值的之和;函数2、取给定两个数值的最大公约数;函数3、取给定两个数值的最小公倍数;关于函数的选定、两个数值的大小都将通过交互式输入来提供。