1、条件判断
条件测试:界定程序执行环境;
实现条件测试方法:
(1) 根据运行的命令的状态结果;
例:运行id命令,如果成功为真,失败为假
(2) 测试表达式
test+表达式
[ 表达式 ]:命令
[[ 表达式 ]]:关键字
注:表达式两端需有空格,否则为语法错误
整数测试:隐含着做数值大小比较,所以不要给变量引用加引用;
$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":是否不空;不空则“真”,空则为“假”
注意:应该使用[[ 表达式 ]]
文件测试:测试文件的存在性以及属性;
-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;
2、bash之条件判断(选择执行):
if/then, case
if CONDITION; then CONDITION:条件
if-true-分支
fi
if CONDITION; then
if-true-分支
else
if-false-分支
fi
! CONDITION: 取反
练习:写一个脚本
如果某路径不存在,则将其创建为目录;否则显示其存在,并显示内容类型;
#!/bin/bash # filename="/tmp/x/y/z/testdir" if [ -e $filename ]; then echo "$filename exists." file $filename else mkdir -p $filename fi [root@centos6 tmp]# bash -x 3.sh + filename=/tmp/x/y/z/testdir + '[' -e /tmp/x/y/z/testdir ']' + mkdir -p /tmp/x/y/z/testdir [root@centos6 tmp]# tree /tmp /tmp ├── 1.sh ├── 2.sh ├── 3.sh ├── backup ├── lost+found ├── x │ └── y │ └── z │ └── testdir
脚本参数(位置参数变量):
# ./script.sh /etc/fstab /etc/grub2.cfg $0 $1 $2
位置参数变量:$1, $2, ...
${10}两位数字以后要加花括号
特殊变量:
$?: 命令的状态结果;
$#: 传递给脚本或函数的参数的个数;
$*和$@: 引用传递给脚本或函数的参数列表;
shift [n]:轮替
与用户交互:
read命令:
read [options] VAR...
-p "PROMPT" :添加提示语句
-t timeout :添加超时时间
示例:由用户输入一个用户名,如果有输入创建,没有输入创建myuser用户
#!/bin/bash # read -p "Plz enter a username: " -t 5 username if [ -z "$username" ]; then username="myuser" fi if id $username &> /dev/null; then echo "$username exists." else useradd $username fi [root@centos6 tmp]# bash 4.sh please enter a username:myname is exist [root@centos6 tmp]# bash 4.sh please enter a username:test1 [root@centos6 tmp]# id test1 uid=3013(test1) gid=3013(test1) groups=3013(test1)
命令引用:
`COMMAND`, $(COMMAND)
引用命令的执行结果;
示例
(1) ls `which cat`
[root@centos6 tmp]# ll `which cat` -rwxr-xr-x. 1 root root 45224 Oct 15 2014 /bin/cat
(2) lines=$(wc -l /etc/fstab | cut -d' ' -f1)
[root@centos6 tmp]# lines=$(wc -l /etc/fstab | cut -d' ' -f1) [root@centos6 tmp]# echo $lines 20
(3)给一个文件路径输出文件行数
#!/bin/bash # if [ -f $1 ]; then lines=$(wc -l $1 | cut -d' ' -f1) echo "$1 has $lines lines." else echo "$1 not exists or not a file." fi [root@centos6 tmp]# ./5.sh /etc/fstab /etc/fstab has 20 lines.
练习:写一个脚本,完成如下功能;
判断给定的两个数值,孰大孰小;
给定数值的方法:脚本参数,命令交互;
#!/bin/bash # read -p "Plz enter two integer: " -t 10 num1 num2 if [ -z "$num1" ]; then echo "Plz give two integers." exit 1 fi if [ -z "$num2" ]; then echo "Plz give tow integers." exit 1 fi if [ $num1 -ge $num2 ]; then echo "Max: $num1, Min: $num2." else echo "Max: $num2, Min: $num1." fi
3、循环语句:
for, while, until
循环:将循环体代码执行0、1或多次;
进入条件:进入循环的条件;
退出条件:循环终止的条件;
格式:
for VARIABLE in LIST; do
循环体
done
LIST:是由一个或多个空格或换行符分隔开的字符串组成;
把列表的每个字符串逐个赋值给VARIABLE表示的变量;
例:
for username in user1 user2 user3; do
循环体
done
进入条件:列表非空;
退出条件:列表遍历结束;
示例:
添加10个用户,user1-user10;
#!/bin/bash # for username in user1 user2 user3 user4 user5; do if id $username &> /dev/null; then echo "$username exists." else useradd $username echo "Add user $username finished." fi done
LIST的生成方法:
(1) 整数列表
(a) {start..end}
(b) $(seq [start [[step]] end)
(2) 直接给出列表
(3) glob
(4) 命令生成
示例:数值列表
#!/bin/bash # for i in {1..10}; do if id user$i &> /dev/null; then echo "user$i exists." else useradd user$i echo "Add user user$i finished." fi done
示例:glob
#!/bin/bash # for filename in /var/log/*; do file $filename done
示例:命令生成列表
#!/bin/bash # for username in $(cut -d: -f1 /etc/passwd); do echo "$username primary group: $(id -n -g $username)." done
算术运算:
+, -, *, /, %, **
使用格式:
(1) $[$A+$B]
(2) $(($A+$B))
(3) let VARIABLE=$A+$B
(4) VARIABLE=$(expr $A + $B)
示例:求100以内所以正整数之和;
#!/bin/bash # declare -i sum=0 for i in {1..100}; do sum=$[$sum+$i] done echo $sum
练习:求100以内所有偶数之和;
使用至少三种方法实现;
方法一
#!/bin/bash # declare -i sum=0 for i in $(seq 0 2 100); do sum=$(($sum+$i)) done echo "Even sum: $sum."
方法二
#!/bin/bash # declare -i sum=0 for i in {1..100}; do if [ $[$i%2] -eq 0 ]; then sum=$[$sum+$i] fi done echo "Even sum: $sum."
增强型赋值:
+=
sum=$[$sum+$i]
let sum+=$i
-=, *=, /=, %=
let count=$[$count+1] --> let count+=1 --> let count++
let count=$[$count-1] --> let count-=1 --> let count--
示例:显示/etc目录下所有普通文件列表,而后统计一共有多少个文件;
#!/bin/bash # declare -i count=0 for file in /etc/*; do if [ -f $file ]; then let count++ echo "$count $file" fi done echo "Total: $count files."
测试表达式:
整数测试:-gt, -lt, -ge, -le, -eq, -ne
字符串测试:==, >, <, !=, -z, -n, =~
注意:
(1) 字符串等会比较测试:[ "$hostname" == 'localhost' ]
(2) 模式匹配测试:[[ "STRING" =~ PATTERN ]]
4、组合测试条件:
条件间逻辑运算:
与:多个条件要同时满足;
或:多个条件满足其一即可;
非:对指定的条件取反;
表达式组合:
与:[[ CONDITION1 -a CONDITION2 ]]
或:[[ CONDITION1 -o CONDITION2 ]]
非:[ ! CONDITION ]
命令组合:
与:COMMAND1 && COMMAND2 <-- [ EXPR1 ] && [ EXPR2 ]
或:COMMAND1 || COMMAND2
非:! COMMAND
短路操作符:&&
false && true = false
false && false = false
true && false = false
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
示例:写一个脚本实现如下功能;
获取当前主机的主机名;
如果当前主机的主机名为空,或者为localhost,则将其修改为www.magedu.com
#!/bin/bash # hostname=$(hostname) if [ -z "$hostname" -o "$hostname" == "localhost" ]; then hostname www.magedu.com fi
练习:写一个脚本,打印九九乘法表;
循环嵌套
1X1=1
1X2=2 2X2=4
1X3=3 2X3=6 3X3=9
#!/bin/bash # for j in {1..9}; do for i in $(seq 1 $j); do echo -n -e "${i}X${j}=$[$i*$j]\t" done echo done
5、多分支的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
示例:通过脚本参数传递一个文件路径给脚本,判断其类型;
#!/bin/bash # if [ $# -lt 1 ]; then echo "Usage: $0 <path>" exit 1 fi if [ -f $1 ]; then echo "Rgular file." elif [ -d $1 ]; then echo "Directory." elif [ -h $1 ]; then echo "Symbolic link." elif [ -b $1 ]; then echo "Block special." elif [ -c $1 ]; then echo "Charactoer special." elif [ -S $1 ]; then echo "Socket file." else echo "file not exist or unknown type." fi
示例:脚本可接受四个参数
start: 创建文件/var/lock/subsys/SCRIPT_NAME
stop: 删除此文件
restart: 删除此文件并重新创建
status: 如果文件存在,显示为"running",否则,显示为"stopped"
basename命令:
取得路径的基名;
#!/bin/bash # prog=$(basename $0) lockfile="/var/lock/subsys/$prog" #echo $lockfile if [ $# -lt 1 ]; then echo "Usage: $prog start|stop|restart|status" exit 1 fi if [ "$1" == "start" ]; then if [ -f $lockfile ]; then echo "$prog is started yet." else touch $lockfile && echo "Starting $prog ok..." || echo "Starting $prog failed..." fi elif [ "$1" == 'stop' ]; then if [ -f $lockfile ]; then rm -f $lockfile && echo "Stop $prog ok...." || echo "Stop $prog failed..." else echo "$prog is stopped yet." fi elif [ "$1" == 'restart' ]; then if [ -f $lockfile ]; then rm -f $lockfile && touch $lockfile && echo "Restarting $porg ok..." else touch $lockfile && echo "$prog is stopped, Starting $prog ok...." fi elif [ "$1" == 'status' ]; then if [ -f $lockfile ]; then echo "Running..." else echo "Stopped..." fi else echo "Usage: $prog start|stop|restart|sttus" exit 1 fi
6、case语句
简洁版多分支if语句;
使用场景:判断某变量的值是否为多种情形中的一种时使用;
语法:
case $VARIABLE in
PATTERN1)
分支1
;;
PATTERN2)
分支2
;;
PATTERN3)
分支3
;;
...
*)
分支n
;;
esac
PATTERN可使用glob模式的通配符:
*: 任意长度的任意字符;
?: 任意单个字符;
[]: 指定范围内的任意单个字符;
a|b: 多选1;
示例:提示键入任意一个字符;判断其类型;
#!/bin/bash # read -p "Plz enter a character: " char case $char in [a-z]) echo "A character." ;; [0-9]) echo "A digit." ;; *) echo "A special character." ;; esac
示例:脚本可接受四个参数
start: 创建文件/var/lock/subsys/SCRIPT_NAME
stop: 删除此文件
restart: 删除此文件并重新创建
status: 如果文件存在,显示为"running",否则,显示为"stopped"
#!/bin/bash # prog=$(basename $0) lockfile="/var/lock/subsys/$prog" #echo $lockfile if [ $# -lt 1 ]; then echo "Usage: $prog start|stop|restart|status" exit 1 fi case $1 in start) if [ -f $lockfile ]; then echo "$prog is started yet." else touch $lockfile && echo "Starting $prog ok..." || echo "Starting $prog failed..." fi ;; stop) if [ -f $lockfile ]; then rm -f $lockfile && echo "Stop $prog ok...." || echo "Stop $prog failed..." else echo "$prog is stopped yet." fi ;; restart) if [ -f $lockfile ]; then rm -f $lockfile && touch $lockfile && echo "Restarting $porg ok..." else touch $lockfile && echo "$prog is stopped, Starting $prog ok...." fi ;; status) if [ -f $lockfile ]; then echo "Running..." else echo "Stopped..." fi ;; *) echo "Usage: $prog start|stop|restart|sttus" exit 1 esac
7、流程控制:
循环语句:for, while, until
(1)while循环:
while CONDTION; do 循环体 done
进入条件:当CONDITION为“真”;
退出条件:当CONDITION为“假”;
while CONDITION; do 循环体 控制变量的修正表达式 done
示例:求100以内所有正整数之和;
#!/bin/bash # declare -i sum=0 declare -i i=1 while [ $i -le 100 ]; do let sum+=$i let i++ done echo "Sum: $sum."
示例:打印九九乘法表
#!/bin/bash # declare -i i=1 declare -i j=1 while [ $j -le 9 ]; do while [ $i -le $j ]; do echo -e -n "${i}X${j}=$[$i*$j]\t" let i++ done echo let i=1 let j++ done
(2)unitl循环:
until CONDITION; do 循环体 循环控制变量的修正表达式 done
进入条件:当CONDITION为“假”时
退出条件:当CONDITION为“真”时
示例:求100以内所有正整数之和
#!/bin/bash # declare -i sum=0 declare -i i=1 until [ $i -gt 100 ]; do let sum+=$i let i++ done echo "Sum: $sum."
循环控制:
continue [n]:提前结束本轮循环,而直接进入下一轮;
break [n]:提前结束循环;
while循环:
while CONDITION; do
.......
if CONDITION2; then
break [n]
fi
done
while CONDITION; do
......
if CONDITION2; then
continue [n]
fi
......
done
示例:求100以内所有偶数之和;
#!/bin/bash # declare -i sum=0 declare -i i=0 while [ $i -le 100 ]; do let i++ if [ $[$i%2] -eq 1 ]; then echo "$i is a odd." continue fi let sum+=$i done echo "Sum: $sum."
死循环:
while true; do
循环体
if CONDTION; then
break
fi
done
until false; do
循环体
if CONDITION; then
break
fi
done
示例:每隔3秒钟查看当前系统上是否有名为“gentoo”的用户登录;
如果某次查看gentoo登录了,则显示gentoo已经登录;
如果未登录,就显示仍然未来,并显示这是已经是第多少次查看了;
#!/bin/bash # username=$1 declare -i count=0 while true; do if who | grep "^$username" &> /dev/null; then echo "$username is logged." break else let count++ echo "$count $username is not login." fi sleep 3 done
#!/bin/bash # declare -i count=0 username=$1 if [ $# -lt 1 ]; then echo "At lease one argument." exit 1 fi if ! id $username &> /dev/null; then echo "No such user." exit 2 fi until who | grep "^$username" &> /dev/null; do let count++ echo "$count $username is not login." sleep 3 done echo "$username is logged on."
while循环的特殊用法:
遍历文件的每一行:
while read VARIABLE; do
循环体
done < /PATH/FROM/SOME_FILE
示例:找出UID为偶数的所有用户,显示其用户名和ID号;
#!/bin/bash # while read line; do userid=$(echo $line | cut -d: -f3) if [ $[$userid%2] -eq 0 ]; then echo $line | cut -d: -f1,3 fi done < /etc/passwd
for循环的特殊用法:
for ((expr1;expr2;expr3)); do
循环体
done
expr1: 定义控制变量,并初始赋值;
expr2: 循环控制条件;
进入条件:控制条件为“真”
退出条件:控制条件为“假”
expr3: 修正控制变量
示例:求100以内所有正整数之和;
#!/bin/bash # declare -i sum=0 for ((i=1;i<=100;i++)); do let sum+=$i done echo "Sum: $sum."
8、函数:
function: 功能
把一段具有独立功能代码封装在一起,并给予命名;后续用到时,可直接通过给定函数名来调用整体代码;
函数作用:
代码重用;
模块化编程;
函数的使用方法:
先定义:编写函数代码
后调用:给出函数名,还可按需传递参数
定义方法:
(1) function f_name {
函数体
}
(2) f_name() {
函数体
}
调用函数:
f_name [argu1, argu2, ...]
自定义函数状态返回值:
return [#]
0: 成功
1-255:失败
注意:函数代码执行时,一旦遇到return,函数代码终止运行,函数返回;
示例:此前的服务脚本
#!/bin/bash # prog=$(basename $0) lockfile="/var/lock/subsys/$prog" #echo $lockfile if [ $# -lt 1 ]; then echo "Usage: $prog start|stop|restart|status" exit 1 fi start() { if [ -f $lockfile ]; then echo "$prog is started yet." else touch $lockfile && echo "Starting $prog ok..." || echo "Starting $prog failed..." fi } stop() { if [ -f $lockfile ]; then rm -f $lockfile && echo "Stop $prog ok...." || echo "Stop $prog failed..." else echo "$prog is stopped yet." fi } restart() { if [ -f $lockfile ]; then rm -f $lockfile && touch $lockfile && echo "Restarting $porg ok..." else touch $lockfile && echo "$prog is stopped, Starting $prog ok...." fi } status() { if [ -f $lockfile ]; then echo "Running..." else echo "Stopped..." fi } case $1 in start) start ;; stop) stop ;; restart) restart ;; status) status ;; *) echo "Usage: $prog start|stop|restart|sttus" exit 1 esac
示例:判断用户的ID号的奇偶性;
#!/bin/bash # evenid() { if [ $# -lt 1 ]; then return 1 fi if ! id $1 &> /dev/null; then return 2 fi userid=$(id -u $1) if [ $[$userid%2] -eq 0 ]; then echo "$1, Even user ID." else echo "$1, Odd user ID." fi } evenid root evenid echo $? evenid rooooooooooooot echo $?
模块化编程
功能:把脚本文件中的代码分隔为多段,放在不同的文件中
假设/root/bin/srv目录有两个文件:
(1) 函数文件
(2) 脚本文件
为脚本使用配置文件
一个文件中只定义变量
脚本文件source此变量定义的文件
变量的作用域:
局部变量:
local VARIABLE=value
存活时间:
函数执行开始,至函数返回结束;