@[TOC](第八章 SHELL脚本编程基础)
实验一:建立脚本并检查
⽬的
建立脚本。
前提
linux系统,centos6、centos7或ubuntu,连接网络。
命令介绍
1、创建shell脚本
第一步:使用文本编辑器来创建文本文件
第一行必须包括shell声明序列:#!
示例:#!/bin/bash
添加注释
注释以#开头
第二步:运行脚本
给予执行权限,在命令行上指定脚本的绝对或相对路径
直接运行解释器,将脚本作为解释器程序的参数运行
脚本规范
1、第一行一般为调用使用的语言
2、程序名,避免更改文件名为无法找到正确的文件
3、版本号
4、更改后的时间
5、作者相关信息
6、该程序的作用,及注意事项
7、最后是各版本的更新简要说明
【例1】编写⽣成脚本基本格式的脚本,包括作者,联系⽅式,版本,时间,描述等
[root@magedu ~]# vim .vimrc
set ignorecase
set cursorline
set autoindent
autocmd BufNewFile *.sh exec ":call SetTitle()"
func SetTitle()
if expand("%:e") == 'sh'
call setline(1,"#!/bin/bash")
call setline(2,"#")
call setline(3,"#********************************************************************")
call setline(4,"#Author: w")
call setline(5,"#QQ: 1****")
call setline(6,"#Date: ".strftime("%Y-%m-%d"))
call setline(7,"#FileName: ".expand("%"))
call setline(8,"#URL: http://www.magedu.com")
call setline(9,"#Description: The test script")
call setline(10,"#Copyright (C): ".strftime("%Y")." All rights reserved")
call setline(11,"#********************************************************************")
call setline(12,"")
endif
endfunc
autocmd BufNewFile * normal G
【例2】建⽴脚本
[root@Magedu ~]# vim hello.sh
#!/bin/bash
echo "hello world"
2、脚本调试
检测脚本中的语法错误
bash -n /path/to/some_script
调试执行
bash -x /path/to/some_script
【例3】测试脚本
[root@localhost ]# bash -n hello.sh
[root@localhost ]# bash -x hello.sh
+ echo 'hello world'
hello world
如果有错:
[root@Magedu ~]# vim hello.sh
#!/bin/bash
echo hello world"
root@localhost ]# bash -n hello.sh
hello.sh: line 2: unexpected EOF while looking for matching `"'
hello.sh: line 3: syntax error: unexpected end of file
[root@localhost ]# bash -x hello.sh
hello.sh: line 2: unexpected EOF while looking for matching `"'
hello.sh: line 3: syntax error: unexpected end of file
#提示:缺少一个"
【例4】运⾏脚本:
添加执行权限
chmod a+x hello.sh
直接运行bash 脚本名称
bash hello.sh
【例5】PATH变量的使⽤。
[root@localhost data]# hello.sh
bash: hello.sh: command not found...
[root@localhost data]# ./hello.sh
hello world
[root@localhost data]# PATH=.:$PATH #把.加入PATH变量
[root@localhost data]# echo $PATH
.:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost data]# hello.sh
hello world
[root@localhost data]# cd ..
[root@localhost /]# hello.sh
bash: hello.sh: command not found...
[root@localhost /]# PATH=/data:$PATH #把/data加入PATH变量
[root@localhost /]# echo $PATH
/data:.:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost /]# hello.sh
hello world
【例6】hello.sh,带颜⾊。
[root@localhost data]# vim hello.sh
#!/bin/bash
echo -e "\033[31mhello world\033[0"
3、变量:
脚本中的变量
Shell中变量命名法则:
1、不能使程序中的保留字:例如if, for
2、只能使用数字、字母及下划线,且不能以数字开头
3、见名知义
4、统一命名规则:驼峰命名法
Shell中命名建议规则:
1、变量名大写
2、局部变量小写
3、函数名小写
4、用英文名字,并体现出实际作用
【例7】关键字,不能⽤作变量
[root@localhost ~]# type while
while is a shell keyword
【例8】统⼀命名规则,根据公司规范执⾏
studentName
STUDENT_NAME
3.1、局部变量:
变量赋值:name=‘value’
可以使用引用value
(1) 可以是直接字串:name=“root"
(2) 变量引用:name="$USER"
(3) 命令引用:name=`COMMAND`
name=$(COMMAND)
变量引用:${name} 或者 $name
" " 弱引用,其中的变量引用会被替换为变量值
' ' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串
显示已定义的所有变量:set
删除变量:unset name
【例9】变量的定义和读取$
[root@localhost ~]# NAME=CHEN
[root@localhost ~]# echo $NAME
CHEN
[root@localhost ~]# echo "$NAME"
CHEN
[root@localhost ~]# echo '$NAME'
$NAME
单引号、双引号、反向单引号区别:
1、由单引号('),强引用,其中的变量会被替换未变量值。 2、由双引号("),弱引用,其中的变量不会被替换未变量值,而保持原字符串。 3、反向单引号(`)括起来的字符串被shell解释为命令行,在执行时,shell首先执行该命令行,并以它的标准输出结果取代整个反引号(包括两个反引号)部分。
【例10】多个变量的定义和读取$
[root@localhost ~]# g1=duan
[root@localhost ~]# g2=zhao
[root@localhost ~]# echo $g1$g2
duanzhao
[root@localhost ~]# echo group leader 1 is $g1
group leader 1 is duan
[root@localhost ~]# echo group leader 2 is $g2
group leader 2 is zhao
【例11】显⽰$100,⼀百美元
[root@localhost ~]# echo $100
00
[root@localhost ~]# echo '$100'
$100
[root@localhost ~]# echo \$100
$100
说明:$为转移符。
【例12】""移符。单数时等待输⼊,⽤Ctrl+c退出。双数时,显⽰出来⼀半。
[root@localhost ~]# echo "\"
> ^C
[root@localhost ~]# echo "\\"
\
[root@localhost ~]# echo "\\\"
> ^C
[root@localhost ~]# echo "\\\\"
【例13】区别变量名和变量,⽤{}或"分隔。
[root@localhost ~]# num=1
[root@localhost ~]# echo No$num
No1
[root@localhost ~]# echo $numNo
[root@localhost ~]# echo ${num}No
1No
[root@localhost ~]# echo ${num}
1
[root@localhost ~]# echo $"num"No
numNo
[root@localhost ~]# echo "$num"No
1No
【例14】变量赋值,m指向新的赋值,n不变
[root@localhost ~]# m=10
[root@localhost ~]# n=$m
[root@localhost ~]# echo $m
10
[root@localhost ~]# echo $n
10
[root@localhost ~]# m=20
[root@localhost ~]# echo $m
20
[root@localhost ~]# echo $n
10
【例15】输出时保留原格式
[root@localhost ~]# who
root :0 2019-05-14 10:04 (:0)
root pts/0 2019-05-14 15:27 (:0)
root pts/1 2019-05-14 16:15 (192.168.23.1)
[root@localhost ~]# userinfo=`who`
[root@localhost ~]# echo $userinfo
root :0 2019-05-14 10:04 (:0) root pts/0 2019-05-14 15:27 (:0) root pts/1 2019-05-14 16:15
(192.168.23.1)
[root@localhost ~]# echo "$userinfo"
root :0 2019-05-14 10:04 (:0)
root pts/0 2019-05-14 15:27 (:0)
root pts/1 2019-05-14 16:15 (192.168.23.1)
【例16】备份脚本,变量的应⽤。
[root@localhost data]# vim backup.sh
#!/bin/bash
SOURCE="/data/script37"
DEST=/data/backup-script`date +%F`
echo -e "\e[1;31mStarting backup...\e[0m"
sleep 3
cp -av $SOURCE $DEST
echo -e "\e[1;31mBackup is finished.\e[0m"
[root@localhost data]# bash backup.sh
Starting backup...
‘/data/script37’ -> ‘/data/backup-script2019-05-14’
Backup is finished.
[root@localhost data]# rm -fr backup-script2019-05-14/ #删除
[root@localhost data]# vim backup.sh #将颜色定为变量
#!/bin/bash
SOURCE="/data/script37"
DEST=/data/backup-script`date +%F`
BEGINCOLOR="\e[1;34m"
ENDCOLOR="\e[0m"
echo -e "${BEGINCOLOR}Starting backup...${ENDCOLOR}"
sleep 3
cp -av $SOURCE $DEST
echo -e "${BEGINCOLOR}Backup is finished.${ENDCOLOR}"
[root@localhost data]# bash backup.sh
Starting backup...
‘/data/script37’ -> ‘/data/backup-script2019-05-14/script37’
Backup is finished.
【例17】显⽰⾃定义变量,删除变量
[root@localhost data]# name=chen
[root@localhost data]# set|less
q键退出set|less
[root@localhost ~]# unset name #删除变量
[root@localhost data]# vim backup.sh
【例18】⼦进程不能使⽤⽗进程定义的普通变量。
[root@localhost ~]# name=chen
[root@localhost ~]# echo $name
chen
[root@localhost ~]# bash #开启子进程
[root@localhost ~]# $name
[root@localhost ~]# exit #退出子进程
exit
3.2、环境变量:
变量声明、赋值:
export name=VALUE
declare -x name=VALUE
变量引用:
$name, ${name}
显示所有环境变量:
env
printenv
export
declare -x
删除变量:
unset name
【例19】⾃定义环境变量,⽗进程的变量可以传给⼦进程。
[root@localhost ~]# VAR=test
[root@localhost ~]# export VAR #自定义环境变量
[root@localhost ~]# echo $VAR
test
[root@localhost ~]# export #查看所有环境变量
declare -x DISPLAY="localhost:10.0"
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
declare -x HOSTNAME="localhost.localdomain"
declare -x LANG="en_US.UTF-8"
declare -x LESSOPEN="||/usr/bin/lesspipe.sh %s"
declare -x LOGNAME="root"
···
declare -x TERM="xterm"
declare -x USER="root"
declare -x
XDG_DATA_DIRS="/root/.local/share/flatpak/exports/share/:/var/lib/flatpak/exports/share/:/u
sr/local/share/:/usr/share/"
declare -x XDG_RUNTIME_DIR="/run/user/0"
declare -x XDG_SESSION_ID="2"
declare -x VAR="test" #刚刚添加的变量
[root@localhost ~]# bash #开启子进程
[root@localhost ~]# echo $VAR #父进程的变量可以传给子进程。
test
[root@localhost ~]# exit #退出子进程
exit
【例20】⽗进程与⼦进程建⽴关系。
[root@localhost data]# vim father.sh
#!/bin/bash
echo "srart father.sh"
echo "father pid is $BASHPID"
/data/son.sh
[root@localhost data]# vim son.sh
#!/bin/bash
echo "srarting son.sh"
echo "son.sh pid is $BASHPID"
sleep 100
[root@localhost data]# chmod +x father.sh #给文件加执行权限
[root@localhost data]# chmod +x son.sh
[root@localhost data]# /data/father.sh #执行脚本
srart father.sh
father pid is 3228
srarting son.sh
son.sh pid is 3229
^C
【例21】⽗进程与⼦进程建⽴关系,普通变量不能传递。
[root@localhost data]# vim father.sh
#!/bin/bash
name=father
echo "srart father.sh"
echo "name=$name"
echo "father pid is $BASHPID"
/data/son.sh
[root@localhost data]# vim son.sh
#!/bin/bash
echo "srarting son.sh"
echo "son.sh pid is $BASHPID"
echo "name=$name"
sleep 100
[root@localhost data]# /data/father.sh
srart father.sh
name=father
father pid is 3353
srarting son.sh
son.sh pid is 3354
name=
^C
【例22】⽗进程与⼦进程建⽴关系,环境变量,可以传递。
[root@localhost data]# vim father.sh
#!/bin/bash
export name=father
echo "srart father.sh"
echo "name=$name"
echo "father pid is $BASHPID"
/data/son.sh
[root@localhost data]# vim son.sh
#!/bin/bash
echo "srarting son.sh"
echo "son.sh pid is $BASHPID"
echo "name=$name"
sleep 100
[root@localhost data]# /data/father.sh
srart father.sh
name=father
father pid is 3353
srarting son.sh
son.sh pid is 3354
name=father
^C
【例23】"( )"开启⼦进程
[root@localhost data]# (name=mage;echo $name)
mage
[root@localhost data]# echo $name
[root@localhost data]# (name=mage;echo $name);echo $name
mage
[root@localhost data]# name=wang;(name=mage;echo $name);echo $name
mage
wang
[root@localhost data]# name=wang;(echo $name);echo $name
wang
wang
[root@localhost data]# name=wang;echo $BASHPID;(echo $BASHPID;sleep 100);echo $name
2864
3561
^C
[root@localhost data]# pstree -p #同时列出每个进程的PID
3.3、只读变量:
只读变量:只能声明,但不能修改和删除
声明只读变量:
readonly name
declare -r name
查看只读变量:
readonly -p
【例24】声明,只读变量,不能修改。
[root@localhost data]# pi=3.14
[root@localhost data]# readonly pi
[root@localhost data]# echo pi
pi
[root@localhost data]# pi=3.1415
bash: pi: readonly variable
[root@localhost data]# unset pi
bash: unset: pi: cannot unset: readonly variable
[root@localhost data]# exit #退出,就没了
【例25】系统信息,脚本。
[root@localhost data]# vim systeminfo.sh
BEGINCOLOR="\e[1;35m"
ENDCOLOR="\e[0m"
echo -e "My hostname is ${BEGINCOLOR}`hostname`$ENDCOLOR"
echo -e "IP address is ${BEGINCOLOR}`ifconfig ens33 |grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}'|head -n1`$ENDCOLOR"
echo -e "OS version is ${BEGINCOLOR}`cat /etc/redhat-release`$ENDCOLOR"
echo -e "Kernel version is ${BEGINCOLOR}`uname -r`$ENDCOLOR"
echo -e "CPU type is ${BEGINCOLOR}`lscpu|grep "Model name" |cut -d: -f2 |tr -s " "`$ENDCOLOR"
echo -e "Memtotol is ${BEGINCOLOR}`cat /proc/meminfo |head -n1 |grep -Eo '[0-9]+.*'`$ENDCOLOR"
echo -e "Disk space is ${BEGINCOLOR}`lsblk |grep 'sda\>'|grep -Eo '[0-9]+[[:upper:]]'`$ENDCOLOR"
[root@localhost data]# bash systeminfo.sh
My hostname is localhost.localdomain
IP address is 192.168.23.118
OS version is CentOS Linux release 7.5.1804 (Core)
Kernel version is 3.10.0-862.el7.x86_64
CPU type is Intel(R) Core(TM) i5-3320M CPU @ 2.60GHz
Memtotol is 1827396 kB
Disk space is 200G
【例26】所有只读变量。-i 声明为整数;-a 声明未数组;-f 声明未函数;-r 声明未只读
[root@localhost data]# readonly
declare -r BASHOPTS="checkwinsize:cmdhist:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:login_shell:progcomp:promptvars:sourcepath"
declare -ir BASHPID="2361"
declare -r BASH_COMPLETION_COMPAT_DIR="/etc/bash_completion.d"
declare -ar BASH_REMATCH='()'
declare -ar BASH_VERSINFO='([0]="4" [1]="2" [2]="46" [3]="2" [4]="release" [5]="x86_64-
redhat-linux-gnu")'
declare -ir EUID="0"
declare -ir PPID="2359"
declare -r SHELLOPTS="braceexpand:emacs:hashall:histexpand:history:interactivecomments:monitor"
declare -ir UID="0"
3.4、位置变量:
位置变量:在脚本代码中调用通过命令行传递给脚本的参数
$1, $2, ... 对应第1、第2等参数,shift [n]换位置
$0 命令本身
$* 传递给脚本的所有参数,全部参数合为一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
注意:$@ $* 只在被双引号包起来的时候才会有差异
set -- 清空所有位置变量
【例27】位置变量脚本。
[root@localhost data]# vim arg.sh
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "10st arg is ${10}"
echo "all args are $*"
echo "all args are $@"
echo "arg number is $#"
echo "scriptname is `basename $0`"
[root@localhost data]# bash arg.sh a b c d
1st arg is a
2st arg is b
3st arg is c
10st arg is
all args are a b c d
all args are a b c d
arg number is 4
scriptname is arg.sh
3.5、特殊变量:
特殊变量:$? , $0 , $*, $@ , $# , $$
【例28】特殊变量,进程编号,⽤tree命令查询
[root@localhost ~]# echo $$ #进程名称
2361
[root@localhost ~]# echo $BASHPID #当前所在进程
2361
[root@localhost ~]# echo $PPID #当前父进程
2359
centos7,第一个进程。
centos6,第一个进程。
【例29】"$_"是什么意思?第⼆个变量。
[root@localhost data]# echo a b
a b
[root@localhost data]# echo $_
b
[root@localhost data]# alias ls
alias ls='ls --color=auto'
[root@localhost data]# ls
3 5 7 9 backup-script2019-05-14 correct.txt father.sh script37
4 6 8 backup-script backup.sh falt.txt hello.sh son.sh
[root@localhost data]# echo $_
--color=auto
说明:ls是别名,显⽰出第⼆段内容。
4、退出状态:
进程使用退出状态来报告成功或失败0 代表成功,1-255代表失败$? 变量保存最近的命令退出状态
bash自定义退出状态码
exit [n]:自定义退出状态码
【例30】退出状态$?。
[root@localhost ~]# ping -c1 -w1 hostdown &> /dev/null
[root@localhost ~]# echo $?
2
[root@localhost data]# grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@localhost data]# echo $?
0
[root@localhost data]# grep Root /etc/passwd
[root@localhost data]# echo $?
1
[root@localhost data]# ping www.baidu.com
PING www.a.shifen.com (220.181.112.244) 56(84) bytes of data.
64 bytes from 220.181.112.244 (220.181.112.244): icmp_seq=1 ttl=50 time=6.17 ms
^C
--- www.a.shifen.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 6.176/6.354/6.532/0.178 ms
[root@localhost data]# echo $?
0
[root@localhost data]# ping 192.168.2.1
PING 192.168.2.1 (192.168.2.1) 56(84) bytes of data.
^C
--- 192.168.2.1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1000ms
[root@localhost data]# echo $?
1
【例31】⾃定义,退出状态$?,的返回值。
[root@localhost data]# vim exit.sh
#!/bin/bash
ls
error
hostname
exit 10
[root@localhost data]# bash exit.sh
3 6 9 backup-script2019-05-14 exit.sh father.sh sendfile.sh
4 7 arg.sh backup.sh f1.sh hello.sh son.sh
5 8 backup-script correct.txt falt.txt script37 systeminfo.sh
exit.sh: line 13: error: command not found
localhost.localdomain
[root@localhost data]# echo $?
10
5、算术运算:
bash中的算术运算:help let
+, -, *, /, %取模(取余), **(乘方),乘法符号有些场景中需要转义
实现算术运算:
(1) let var=算术表达式
(2) var=$[算术表达式]
(3) var=$((算术表达式))
(4) var=$(expr arg1 arg2 arg3 ...)
(5) declare –i var = 数值
(6) echo ‘算术表达式’ | bc
bash有内建的随机数生成器变量:$RANDOM(0-32767)
示例:生成 0 - 49 之间随机数
echo $[$RANDOM%50]
【例32】算数let var=算术表达式
[root@localhost ~]# i=10
[root@localhost ~]# j=20
[root@localhost ~]# echo $i+$j
10+20
[root@localhost ~]# sum=$i+$j
[root@localhost ~]# echo $sum
10+20
[root@localhost ~]# let sum=$i+$j #算术表达式
[root@localhost ~]# echo $sum
30
[root@localhost ~]# let sum=i+j #算术表达式
[root@localhost ~]# echo $sum
30
【例33】var=((算术表达式))
[root@localhost ~]# sum=$[i+j]
[root@localhost ~]# echo $sum
30
[root@localhost ~]# sum=$((i+j))
[root@localhost ~]# echo $sum
30
【例34】 var=$(expr arg1 arg2 arg3 ...),"*"需要转义。
[root@localhost ~]# expr 2 +3
expr: syntax error #语法错误
[root@localhost ~]# expr 2 + 3
5
[root@localhost ~]# expr 2 * 3
expr: syntax error
[root@localhost ~]# expr 2 \* 3
6
[root@localhost ~]# expr i \* 3
expr: non-integer argument
[root@localhost ~]# expr $i \* 3
30
【例35】 declare –i var = 数值
[root@localhost ~]# declare -i x=i+j
[root@localhost ~]# echo $x
30
【例36】 ⽣成 0 - 49 之间随机数 echo RANDOM%50]
[root@localhost ~]# echo -e '\033[33mcentos\033[0m' #给centos加颜色
centos
[root@localhost ~]# echo $[RANDOM%7+31] #生成随机数
36
[root@localhost ~]# NUM=$[RANDOM%7+31];echo -e "\033[${NUM}mcentos\033[0m" #给centos加随机颜色
centos
5.1、赋值:
增强型赋值:
+=, -=, *=, /=, %=
let varOPERvalue
例如:let count+=3
自加3后自赋值
自增,自减:
let var+=1
let var++
let var-=1
let var--
【例37】赋值
[root@localhost ~]# i=100
[root@localhost ~]# let i++
[root@localhost ~]# echo $i
101
[root@localhost ~]# let i--
[root@localhost ~]# echo $i
100
[root@localhost ~]# let i--
[root@localhost ~]# echo $i
99
[root@localhost ~]# i=100 ;let j=i++;echo j=$j,i=$i
j=100,i=101
5.2、逻辑运算:
true, false
1, 0
与
1 与 1 = 1
1 与 0 = 0
0 与 1 = 0
0 与 0 = 0
或
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
非:!
! 1 = 0 ! true
! 0 = 1 ! false
【例38】true, false。
[root@localhost ~]# true
[root@localhost ~]# echo ?
?
[root@localhost ~]# echo $?
0
[root@localhost ~]# false
[root@localhost ~]# echo $?
1
【例39】与
[root@localhost ~]# echo $[8&12]
8
[root@localhost ~]# echo $[4&12]
4
【例40】或
[root@localhost ~]# echo $[4|12]
12
[root@localhost ~]# echo $[4|12]
12
【例41】⾮
[root@localhost ~]# ! true
[root@localhost ~]# echo $?
1
[root@localhost ~]# ! false
[root@localhost ~]# echo $?
0
短路运算
短路与
第一个为0,结果必定为0
第一个为1,第二个必须要参与运算
短路或
第一个为1,结果必定为1
第一个为0,第二个必须要参与运算
异或:^
异或的两个值,相同为假,不同为真
【例42】异或
[root@localhost ~]# a=10;b=20
[root@localhost ~]# echo $a $b
10 20
[root@localhost ~]# a=10;b=20;a=$[a^b];b=$[a^b];a=$[a^b];echo $a $b
20 10
5.3、条件测试:
判断某需求是否满足,需要由测试机制来实现。专用的测试表达式需要由测试命令辅助完成测试过程
评估布尔声明,以便用在条件性执行中
若真,则返回0
若假,则返回1
测试命令:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
注意:EXPRESSION前后必须有空白字符
【例43】test=[]
[root@localhost ~]# help test
test: test [expr]
Evaluate conditional expression.
Exits with a status of 0 (true) or 1 (false) depending on
the evaluation of EXPR. Expressions may be unary or binary. Unary
expressions are often used to examine the status of a file. There
are string operators and numeric comparison operators as well.
[root@localhost ~]# cat /etc/redhat-release |egrep -o '[0-9]+'|head -1 #取出版本号
7
[root@localhost ~]# os=/etc/redhat-release |egrep -o '[0-9]+'|head -1
[root@localhost ~]# [$os =7] #[]必须两边有空格
-bash: [: missing `]'
[root@localhost ~]# [ $os =7 ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ $os =!7 ]
[ $os =ping www.baidu.com ]
-bash: [: =ping: unary operator expected
[root@localhost ~]# echo $?
2
【例44】test=[[]]⽀持正则表达式
[root@localhost data]# touch f1.sh
[root@localhost data]# file=f1.sh ;[[ $file =~ \.sh$ ]]
[root@localhost data]# echo $?
0
[root@localhost data]# file=f1.shsh ;[[ $file =~ \.sh$ ]]
[root@localhost data]# echo $?
1
5.4、数值测试:
-v VAR
变量VAR是否设置
数值测试:
-gt 是否大于
-ge 是否大于等于
-eq 是否等于
-ne 是否不等于
-lt 是否小于
-le 是否小于等于
5.5、字符串测试:
= 是否等于
> ascii码是否大于ascii码
< 是否小于
!= 是否不等于
=~ 左侧字符串是否能够被右侧的PATTERN所匹配
注意: 此表达式一般用于[[ ]]中;扩展的正则表达式
-z "STRING“ 字符串是否为空,空为真,不空为假
-n "STRING“ 字符串是否不空,不空为真,空为假
注意:⽤于字符串⽐较时的⽤到的操作数都应该使⽤引号
【例45】-n "STRING“ 字符串是否不空,不空为真,空为假。建议加",不加容易出现语法错误。
[root@localhost data]# [ -n $centos ];echo $?
0
[root@localhost data]# [ -n "$centos" ];echo $?
1
[root@localhost data]# [ -n "" ];echo $?
1
[root@localhost data]# [ ];echo $?
1
[root@localhost data]# [ abc ];echo $?
0
[root@localhost data]# [ 0 ];echo $?
0
[root@localhost data]# [ -1 ];echo $?
0
说明:【】内有内容即为真。
【例46】数值测试:
[root@localhost data]# i=20;[ $i -gt 10 ] ;echo $?
0 i大于10,为真。
[root@localhost data]# i=20;[ $i -lt 10 ] ;echo $?
1 i小于10,为假。
[root@localhost data]# i=20;[ $i -ge 10 ] ;echo $?
0 i大于等于10,为真。
[root@localhost data]# i=20;[ $i -eq 10 ] ;echo $?
1 i等于10,为假。
5.6、⽂件存在性和类型测试
存在性测试
-a FILE:同-e
-e FILE: 文件存在性测试,存在为真,否则为假
存在性及类别测试
-b FILE:是否存在且为块设备文件
-c FILE:是否存在且为字符设备文件
-d FILE:是否存在且为目录文件
-f FILE:是否存在且为普通文件
-h FILE 或 -L FILE:存在且为符号链接文件
-p FILE:是否存在且为命名管道文件
-S FILE:是否存在且为套接字文件
【例47】存在性及类别测试
[root@localhost data]# touch f1.sh
[root@localhost data]# [ -d /etc/ ];echo $?
0
[root@localhost data]# [ -d f1.sh ];echo $?
1
5.7、⽂件权限测试:
-r FILE:是否存在且可读
-w FILE: 是否存在且可写
-x FILE: 是否存在且可执行
5.8、⽂件特殊权限测试
-u FILE:是否存在且拥有suid权限
-g FILE:是否存在且拥有sgid权限
-k FILE:是否存在且拥有sticky权限
5.9、⽂件属性测试
文件大小测试:
-s FILE: 是否存在且非空
文件是否打开:
-t fd: fd 文件描述符是否在某终端已经打开
-N FILE:文件自从上一次被读取之后是否被修改过
-O FILE:当前有效用户是否为文件属主
-G FILE:当前有效用户是否为文件属组
双目测试:
FILE1 -ef FILE2: FILE1是否是FILE2的硬链接
FILE1 -nt FILE2: FILE1是否新于FILE2(mtime)
FILE1 -ot FILE2: FILE1是否旧于FILE2
5.10、组合测试
第一种方式:
EXPRESSION1 -a EXPRESSION2 并且
EXPRESSION1 -o EXPRESSION2 或者
! EXPRESSION
必须使用测试命令进行,[[ ]] 不支持
第二种方式:
COMMAND1 && COMMAND2 并且,短路与,代表条件性的AND THEN
COMMAND1 || COMMAND2 或者,短路或,代表条件性的OR ELSE
! COMMAND 非
如:[ -f “$FILE” ] && [[ “$FILE”=~ .*\.sh$ ]]
【例48】⽂件权限测试,只能⽤[],不能⽤[[]]。
[root@localhost data]# ll f1.sh
-rw-r--r-- 1 root root 23 Mar 16 02:01 f1.sh
[root@localhost data]# [ -r f1.sh -o -w f1.sh ];echo $?
0
[root@localhost data]# [[ -r f1.sh -o -w f1.sh ]];echo $?
-bash: syntax error in conditional expression
-bash: syntax error near `-o'
[root@localhost data]# [ ! -r f1.sh ];echo $?
1
[root@localhost data]# [ -r f1.sh ];echo $?
0
【例49】组合测试
[root@localhost data]# [ -w f1.sh ] && echo f1.sh is writeable || echo f1.sh is not writeable
f1.sh is writeable
【例50】测试,主机是否开机?
[root@localhost data]# ping 192.168.23.118 -c1 -W1 && echo the host is up. ||echo the host is down.
PING 192.168.23.118 (192.168.23.118) 56(84) bytes of data.
64 bytes from 192.168.23.118: icmp_seq=1 ttl=64 time=0.124 ms
--- 192.168.23.118 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.124/0.124/0.124/0.000 ms
the host is up.
[root@localhost data]# ping 192.168.23.128 -c1 -W1 && echo the host is up. ||echo the host is down.
PING 192.168.23.128 (192.168.23.128) 56(84) bytes of data.
--- 192.168.23.128 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
the host is down.
#"&> /dev/null",不显示多余的内容、相当于丢到垃圾箱。
[root@localhost data]# ping 192.168.23.118 -c1 -W1 &> /dev/null && echo the host is up. ||echo the host is down.
the host is up.
[root@localhost data]# ping 192.168.23.128 -c1 -W1 &> /dev/null && echo the host is up. ||echo the host is down.
the host is down.
6、read命令:
使用read来把输入值分配给一个或多个shell变量
-p 指定要显示的提示
-s 静默输入,一般用于密码
-n N 指定输入的字符长度N
-d ‘字符’ 输入结束符
-t N TIMEOUT为N秒
read 从标准输入中读取值,给每个单词分配一个变量。所有剩余单词都被分配给最后一个变量
read -p "Enter a filename: " FILE
【例51】read命令:
[root@localhost data]# echo a b c| { read x y z ; echo $x $y $z; }
a b c
[root@localhost data]# read -p "Are you OK?" ANS
Are you OK?ok
[root@localhost data]# echo $ANS
ok
【例52】read命令:yesorno.使⽤read来把输⼊值分配给⼀个或多个shell变量,-p 指定要显⽰的提⽰.
[root@localhost data]# vim yesorno.sh
#!/bin/bash
read -p "Do you agree? (yes or no): " ANSWER
[[ "$ANSWER" =~ ^[Yy]([Ee][Ss])?$ ]] && { echo ok ; exit; }
[[ "$ANSWER" =~ ^[Nn][Oo]?$ ]] && { echo Not ok ; exit; }
echo "Your input is false."
[root@localhost data]# bash yesorno.sh
Do you agree? (yes or no): y
ok
[root@localhost data]# bash yesorno.sh
Do you agree? (yes or no): l
Your input is false.
【例53】使⽤read来把输⼊值分配给⼀个或多个shell变量,-s 静默输⼊,⼀般⽤于密码.
[root@localhost data]# read -s password
[root@localhost data]# echo $password
123456
【例54】使⽤read来把输⼊值分配给⼀个或多个shell变量,-n N 指定输⼊的字符长度N.
[root@localhost data]# read -n 3 name
abc[root@localhost data]#
【例55】使⽤read来把输⼊值分配给⼀个或多个shell变量,-d ʻ字符ʼ 输⼊结束符.
[root@localhost data]# read -d 0 name
asdasda0[root@localhost data]#
【例56】使⽤read来把输⼊值分配给⼀个或多个shell变量,-t N TIMEOUT为N秒.
[root@localhost data]# read -t 3 name
[root@localhost data]#
7、[ ]和[[ ]] 的区别。
[ ] 实际上是bash 中 test 命令的简写。即所有的 [ expr ] 等于 test expr。对 test 命令来说, 用
-eq 要进行数字比较,而你此时传入字符串,就报错了。
[[ expr ]] 是bash中真正的条件判断语句,其语法更符合编程习惯 (比如 &&, || 的用法),虽然我认
为在 [[ ]] 中 故意传字符串给 -eq 也应该像 test 一样报错,但是显然bash实现中直接把非整数的字符串直
接转换成了 0 (你可以自行验证,在 [[ ]] 中的,任何需要整数,但是提供的确又是其他不能转换成整数的字
符串,都变成了0)。 这应该是bash实现中的没有对 [[ ]] 中 -eq 操作符两边的内容进行检查导致的。
8、判断语句()、 (( ))和{}的区别。
1、单⼩括号 ()
①命令组。括号中的命令将会新开一个子shell顺序执行,所以括号中的变量不能够被脚本余下的部分使用。括
号中多个命令之间用分号隔开,最后一个命令可以没有分号,各命令和括号之间不必有空格。
②命令替换。等同于`cmd`,shell扫描一遍命令行,发现了$(cmd)结构,便将$(cmd)中的cmd执行一次,得到
其标准输出,再将此输出放到原来命令。有些shell不支持,如tcsh。
③用于初始化数组。如:array=(a b c d)
2、双⼩括号 (( ))
①整数扩展。这种扩展计算是整数型的计算,不支持浮点型。((exp))结构扩展并计算一个算术表达式的值,
如果表达式的结果为0,那么返回的退出状态码为1,或者 是"假",而一个非零值的表达式所返回的退出状态码将
为0,或者是"true"。若是逻辑判断,表达式exp为真则为1,假则为0。
②只要括号中的运算符、表达式符合C语言运算规则,都可用在$((exp))中,甚至是三目运算符。作不同进位
(如二进制、八进制、十六进制)运算时,输出结果全都自动转化成了十进制。如:echo $((16#5f)) 结果为95
(16进位转十进制)
③单纯用 (( )) 也可重定义变量值,比如 a=5; ((a++)) 可将 $a 重定义为6
④常用于算术运算比较,双括号中的变量可以不使用$符号前缀。括号内支持多个表达式用逗号分开。 只要括
号中的表达式符合C语言运算规则,比如可以直接使用for((i=0;i<5;i++)), 如果不使用双括号, 则为for i in
`seq 0 4`或者for i in {0..4}。再如可以直接使用if (($i<5)), 如果不使用双括号, 则为if [ $i -lt 5
]。(1)单小括号,(cmd1;cmd2;cmd3) 新开一个子shell顺序执行命令cmd1,cmd2,cmd3, 各命令之间用分号隔开,
最后一个命令后可以没有分号。
(2)单大括号,{ cmd1;cmd2;cmd3;} 在当前shell顺序执行命令cmd1,cmd2,cmd3, 各命令之间用分号隔开,
最后一个命令后必须有分号, 第一条命令和左括号之间必须用空格隔开。
3、⼤括号、花括号 {}
①大括号拓展。(通配(globbing))将对大括号中的文件名做扩展。在大括号中,不允许有空白,除非这个空白
被引用或转义。第一种:对大括号中的以逗号分割的文件列表进行拓展。如 touch {a,b}.txt 结果为a.txt
b.txt。第二种:对大括号中以点点(..)分割的顺序文件列表起拓展作用,如:touch {a..d}.txt 结果为
a.txt b.txt c.txt d.txt
# ls {ex1,ex2}.sh
ex1.sh ex2.sh
# ls {ex{1..3},ex4}.sh
ex1.sh ex2.sh ex3.sh ex4.sh
# ls {ex[1-3],ex4}.sh
ex1.sh ex2.sh ex3.sh ex4.sh
②代码块,又被称为内部组,这个结构事实上创建了一个匿名函数 。与小括号中的命令不同,大括号内的命令
不会新开一个子shell运行,即脚本余下部分仍可使用括号内变量。括号内的命令间用分号隔开,最后一个也必须
有分号。{}的第一个命令和左括号之间必须要有一个空格。
实验二:建里条件脚本
目的
建立条件脚本。
前提
linux系统,centos6、centos7或ubuntu,连接网络。
命令介绍
1、条件选择if语句
选择执行:
注意:if语句可嵌套
单分支
if 判断条件;then
条件为真的分支代码
fi
双分支
if 判断条件; then
条件为真的分支代码
else
条件为假的分支代码
fi
多分支
if 判断条件1; then
条件1为真的分支代码
elif 判断条件2; then
条件2为真的分支代码
elif 判断条件3; then
条件3为真的分支代码
else
以上条件都为假的分支代码
fi
逐条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句
【例57】成绩判断
[root@localhost data]# vim score.sh
#!/bin/bash
read -p "please input your score: " SCORE
if [[ ! $SCORE =~ ^[0-9]+$ ]]; then
echo "please input a digit!"
exit
elif [ $SCORE -lt 60 ];then
echo "no pass!"
elif [ $SCORE -lt 80 ];then
echo "so so!"
elif [ $SCORE -le 100 ];then
echo "very good!"
else
echo "bug!"
fi
[root@localhost data]# bash score.sh
please input your score: k
please input a digit!
[root@localhost data]# bash score.sh
please input your score: 65
so so!
[root@localhost data]# bash score.sh
please input your score: 101
bug!
【例58】鸡兔同笼,头37,脚94.
[root@localhost data]# vim chook_rabbit.sh
#!/bin/bash
read -p "please input the head number: " head
read -p "please input the foot number: " foot
rabbit=$[foot/2-head]
chook=$[head-rabbit]
echo "rabbit:" $rabbit
echo "chook:" $chook
[root@localhost data]# bash chook_rabbit.sh
please input the head number: 37
please input the foot number: 94
rabbit: 10
chook: 27
【例59】busybox
[root@localhost data]# vim busybox.sh
#!/bin/bash
FILE=`basename $0`
if [ "$FILE" = 'rm' ];then
echo rm
elif [ "$FILE" = 'cp' ];then
echo cp
elif [ "$FILE" = 'mv' ];then
echo mv
else
echo busybox
fi
[root@localhost data]# bash busybox.sh
busybox
2、条件判断:case语句
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
esac
case支持glob风格的通配符:
*: 任意长度任意字符
?: 任意单个字符
[]:指定范围内的任意单个字符
a|b: a或b
【例60】case的应⽤。
[root@localhost data]# help case
case: case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
Execute commands based on pattern matching.
Selectively execute COMMANDS based upon WORD matching PATTERN. The
`|' is used to separate multiple patterns.
Exit Status:
Returns the status of the last command executed.
[root@localhost data]# vim menu.sh
#!/bin/bash
cat << eof
1:大盘鸡
2:宫保鸡丁
3:烤鸭
4:龙虾
5:退出
eof
read -p "choose a number:" menu
case $menu in
1|2)
echo price is 50.
;;
3)
echo price is 100.
;;
4)
echo price is 200.
;;
*)
exit
esac
[root@localhost data]# bash menu.sh
1:大盘鸡
2:宫保鸡丁
3:烤鸭
4:龙虾
5:退出
choose a number:4
price is 200.
3、bash如何展开命令⾏
把命令行分成单个命令词
展开别名
展开大括号的声明({})
展开波浪符声明(~)
命令替换$() 和 ``)
再次把命令行分成命令词
展开文件通配(*、?、[abc]等等)
准备I/0重导向(<、>)
运行命令
4、防⽌扩展
反斜线(\)会使随后的字符按原意解释
echo Your cost: \$5.00
Your cost: $5.00
加引号来防止扩展
• 单引号(’’)防止所有扩展
• 双引号(”“)也可防止扩展,但是以下情况例外:
$(美元符号) 变量扩展
` ` (反引号) 命令替换
\(反斜线) 禁止单个字符扩展
!(叹号) 历史命令替换
【例61】扩展。
[root@localhost data]# history
1 vim /etc/sysconfig/selinux
2 systemctl stop firewalld
3 systemctl disable firewalld
4 yum install autofs -y
5 ping www.baidu.com
6 yum clean all
7 ping www.baidu.com
8 yum install autofs -y
[root@localhost data]# vim history.sh
#!/bin/bash
ls
hostname
history
[root@localhost data]# bash history.sh
3 8 backup.sh f1.sh menu.sh son.sh
4 9 busybox.sh falt.txt score.sh systeminfo.sh
5 arg.sh chook_rabbit.sh father.sh script36.zip yesorno.sh
6 backup-script correct.txt hello.sh script37
7 backup-script2019-05-14 exit.sh history.sh sendfile.sh
localhost.localdomain
说明:history未显⽰。
5、bash的配置⽂件。按⽣效范围划分,存在两类:
全局配置:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人配置:
~/.bash_profile
~/.bashrc
修改profile和bashrc文件后需生效
1重新启动shell进程
2 . 或source
【例62】.bashrc和.bash_profile
[root@localhost ~]# cat .bashrc
# .bashrc
# User specific aliases and functions
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
[root@localhost ~]# cat .bash_profile
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
export PATH
6、set 命令
$- 变量
h:hashall,打开这个选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h
选项关闭
i:interactive-comments,包含这个选项说明当前的 shell 是一个交互式的shell。所谓的交互式
shell,在脚本中,i选项是关闭的。
m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等。
B:braceexpand,大括号扩展
H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的一
个历史命令,“!n”返回第 n 个历史命令
set 命令
-u 在扩展一个没有设置的变量时,显示错误信息,等同set –o nounset
-e 如果一个命令返回一个非0退出状态值(失败)就退出,等同set –o errexit
【例63】set 命令,-u 在扩展⼀个没有设置的变量时,显⽰错误信息,等同set –o nounset。
[root@localhost ~]# vim set.sh
#!/bin/bash
#set -u
set -e
echo $var
echo continue
[root@localhost ~]# bash set.sh
continue
[root@localhost ~]# vim set.sh
#!/bin/bash
set -u
set -e
echo $var
echo continue
[root@localhost ~]# bash set.sh
set.sh: line 14: var: unbound variable