一、shell编程-流程控制
shell编程-条件结构
测试 test 条件 条件为真返回 0,条件为假返回 1 [ 条件 ] test 能够理解3种类型的表达式 1.文件测试 2.字符串比较 3.数字比较 字符串 -n STRING # -n 字符串长度不为零 -z STRING # -z 字符串长度为0 STRING1 = STRING2 # = 判断两个字符串是否一样 STRING1 != STRING2 # != 判断两个字符串是否不一样 数字 eq 等于 ne 不等于 # ge 大于等于 le 小于等于 # gt 大于 lt 小于 # 文件 test -f 存在且是普通文件 # 重要 -d 存在且是目录 # -h 存在且是符号链接 -b 块设备 -c 字符设备 -e 文件存在 #
shell分支if语句
流控制: •在一个shell脚本中的命令执行顺序称作脚本的流。大多数脚本会根据一个或多个条件来改变它们的流。 •流控制命令:能让脚本的流根据条件而改变的命令称为条件流控制命令 •exit语句:退出程序的执行,并返回一个返回码,返回码为0正常退出,非0为非正常退出,例如: •exit 0 条件判断 If代码返回0表示真,非0为假 if语句语法如下: 1 if [ $1 -eq 1 ] then echo '等于1' elif [ $1 -eq 2 ] then echo '等于二' else echo '既不等一,也不等于二' fi 例: #!/bin/bash read -p "请输入号码: " num if [ $num = 1 ];then echo "1" elif [ $num = 2 ];then echo "2" else echo "输入有误!" fi 例:脚本if.sh,必须在脚本后加上适当的参数脚本才能正确执行 #!/bin/bash if [ "$1" = "hello" ]; then echo "Hello! How are you ?" elif [ "$1" = "" ]; then echo "You MUST input parameters" else echo "The only accept parameter is hello" fi 练习(选做): 1)检测apache是否运行,如果没有运行则启动,并记录启动的时间,保存到日志中。 2)测试ip地址主机位从2到100的机器是否存活,并把存活的机器记录到文本文件alivehost.txt内。(使用ping命令) 多个条件联合 逻辑与 if [ $condition1 ] && [ $condition2 ] if [ $condition -a $condition2 ] if [[ $condition1 && $condition2 ]] 逻辑或 if [ $condition1 ] || [ $condition2 ] if [ $condition -o $condition2 ] if [[ $condition1 || $condition2 ]] # test 和 [] 中 我们可以使用 [ $condition1 ] && [ $condition2 ] 或者 [ $condition -a $condition2 ] # 在 [[]] 这种情况,我们可以直接使用[[ $condition1 && $condition2 ]] 建议在if中直接使用[[]]这种方式,这种方式更加稳定。[[]] shell的一个命令。 -a && 逻辑与 and 两端的条件都可以成立 -o || 逻辑或 or 两端的条件有一段成立就行 练习: 编写脚本port.sh,执行脚本后显示系统的httpd、ftp、ssh、sendmail这些服务是否开启 case case 语句是 shell 中流控制的第二种方式,语法如下: case $word in pattern1) list1 ;; pattern2) list2 ;; patternN) listN ;; *) list* ;; esac 命令;;表明流应该跳转到case语句的最后,类似C语言中的break指令。 练习:建立脚本case.sh,当执行时,要求我们在键盘输入适当的值(one|two|three),当输入正确时并打印,当输入错误 时会提示你,应该输入正确的值。
shell 分支case语句
case $var in 模式1) 执行1 ;; 模式2) 执行2 ;; 模式3) 执行3 ;; *) 执行4 esac 第一行: 声明case关键字调用case语法, 紧跟的“变量”一般为用户的输入值, in代表从下方的各个模式进行匹配 第2-4行: 匹配到“模式1”后进行命令的输出或执行, 模式1: 一般为字符或数值 第11-12行: 当用户输入的字符不存在匹配模式时, 直接执行或打印*)下的命令或语句
示例:
[root@bavdu shell_scripts]# vim system_tools #!/usr/bin/env bash cat <<-EOF +-------------------------------------------------------------------------+ | System_tools V1.0 | +-------------------------------------------------------------------------+ | a. Stop And Disabled Firewalld. | | b. Disabled SELinux Secure System. | | c. Install Apache Service. | | d. Quit | +-------------------------------------------------------------------------+ EOF echo "Please input your select: " && read var case "$var" in "a") systemctl stop firewalld && systemctl disable firewalld ;; "b") setenforce 0 ;; "c") yum -y install httpd httpd-tools ;; "d") exit ;; *) printf "请按照上方提供的选项输入!!!\n" ;; esac
二、shell编程-循环结构
shell循环-for语句
for i in {取值范围} # for 关键字 i 变量名 in 关键字 取值范围格式 1 2 3 4 5 do # do 循环体的开始 循环体 done # done 循环体的结束#!/usr/bin/env bash # # Author: # Date: 2019/**/** for i in {1..100} do echo $i done#!/bin/bash # 类似c语言的循环方式 for (( i=1;i <= 5;i++ )) do echo "$i" done测试生产环境的主机存活性
#!/usr/bin/env bash # # Author: #{}& 并发 >ip_alive.txt # 在执行脚本之前,先将文件清空 >ip_down.txt segment="192.168.161" for i in {2..254} do { ping -c1 $segment.$i &>/dev/null if [ $? -eq 0 ];then printf "alive: $segment.$i\n" >>ip_alive.txt else printf "down: $segment.$i\n" >>ip_down.txt fi }& done wait #等待循环结束后,执行下面操作 echo "finish..."for循环批量创建用户
#!/bin/bash while : #死循环 do read -p "请设置用户前缀/数量/密码: " prefix num pass cat <<-EOF # 打印到屏幕 用户前缀:$prefix 用户数量:$num 用户密码:$pass EOF read -p "是否确认创建:[Y/N]" action if [ $action = Y ];then "starting create users..." break fi done for i in `seq -w $num` do user=$prefix$i id $user &>/dev/null if [ $? -eq 0 ];then echo "$user is already exist" else useradd $user echo $pass | passwd --stdin $user &>/dev/null # 用户密码设置不需要交互 fi done
shell 循环while语句
while 条件 # while 关键字 条件 [ $1 -lt 10 ] ,while循环,条件为真的情况下,会循环 do 循环体 done完善系统工具的输出及操作性
创建一个文件里面的用户 #!/bin/bash while read user do id $user &>/dev/null if [ $? -eq 0 ];then echo "$user is already exists" else useradd $user echo "create $user successfully" fi done < user.txt #!/usr/bin/env bash # # Author: while 1>0 do cat <<-EOF +-------------------------------------------------------------------------+ | System_tools V1.0 | +-------------------------------------------------------------------------+ | a. Stop And Disabled Firewalld. | | b. Disabled SELinux Secure System. | | c. Install Apache Service. | | d. Quit | +-------------------------------------------------------------------------+ EOF echo " Please input your select: " && read var case "$var" in "a") systemctl stop firewalld && systemctl disable firewalld ;; "b") sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config ;; "c") yum -y install httpd httpd-tools ;; "d") exit ;; *) echo "请按照上方提供的选项输入!!!" ;; esac if [ $? -eq 0 ];then clear else echo "Warning: Your program exist ERROR!!!" break fi done 练习题: 输出用户输入的参数,直到用户输入 "end" 结束循环 #!/usr/bin/bash while 2>1 do read -p "请输入: " word if [ $word != 'end' ];then echo "$word" else echo "退出循环" break fi done
shell循环until语句
until 条件 # 当后面的条件表达式,为假的时候进行循环,当他为真了就停止循环了。 do 循环体 done[root@newrain ~]# cat until.sh #!/bin/bash x=1 until [ $x -ge 10 ] do echo $x x=`expr $x + 1` done x=1 while [ ! $x -ge 10 ] do echo $x x=`expr $x + 1` done
shell 循环控制shift、continue、break、exit
shift命令 位置参数可以用shift命令左移。比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1、$2、$3丢 弃,$0不移动。不带参数的shift命令相当于shift 1。 对于位置变量或命令行参数,其个数必须是确定的,或者当 Shell 程序不知道其个数时,可以把所有参数一起赋值给变量 $*。 若用户要求 Shell 在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在 $1 后为 $2,在 $2 后面为 $3 等,则需要用shift把所有参数变成$1 #测试 shift 命令(x_shift3.sh) [root@newrain shell]# cat x_shift3.sh #!/bin/bash shift echo "第一个位置参数: $1" [root@newrain shell]# bash x_shift3.sh 2 3 第一个位置参数: 3 #测试 shift 命令(x_shift.sh) #!/bin/bash until [ $# -eq 0 ] do echo "第一个参数为: $1 参数个数为: $#" shift done 执行以上程序x_shift.sh: $./x_shift.sh 1 2 3 4 结果显示如下: 第一个参数为: 1 参数个数为: 4 第一个参数为: 2 参数个数为: 3 第一个参数为: 3 参数个数为: 2 第一个参数为: 4 参数个数为: 1 从上可知 shift 命令每执行一次,变量的个数($#)减一,而变量值提前一位 用 until 和 shift 命令计算所有命令行参数的和。 #shift 上档命令的应用(x_shift2.sh) sum=0 until [ $# -eq 0 ] do sum=`expr $sum + $1` shift done echo "sum is: $sum" 执行上述程序: $x_shift2.sh 10 20 15 其显示结果为: 45 continue、break、exit命令 Linux脚本中的break continue exit return break 结束并退出本次循环 continue 在循环中不执行continue下面的代码,转而进入下一轮循环 exit 退出脚本 常带一个整数给系统,如 exit 0 可理解为:break是立马跳出循环;continue是跳出当前条件循环,继续下一轮条件循环;exit是直接退出整个脚本 例如: 在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。 break命令 break命令允许跳出所有循环(终止执行后面的所有循环)。 下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。 代码如下: #!/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 continue命令 continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。 对上面的例子进行修改: 代码如下: #!/bin/bash while : do echo -n "请输入1-5之间的数字 " read aNum case $aNum in 1|2|3|4|5) echo "您的数字是 $aNum!" ;; *) echo "您输入的数字不再1-5中间,游戏结束!";、 continue ;; esac done 运行代码发现,当输入大于5的数字时,该例中的循环不会结束. break和exit的区别 [root@newrain shell]# cat case07.sh #!/bin/bash while true do read -p "请输入[1/2]" num1 case $num1 in 1) echo $num1 ;; 2) while true do read -p "再次输入[1/2]:" num2 case $num2 in 1) echo $num2;; 2) break; #将此处换成exit,再次尝试 esac done esac done
实战-shell版本jumpserver开发(堡垒机)
ps:整理自己的思路,完善不足的地方
#!/usr/bin/env bash # # Author: #可以先添加上账密验证环节 while : # 死循环 do #trap ':' INT EXIT TSTP TERM HUP # 拒绝 ctrl+c ctrl+d 其他的退出方式 clear cat <<-EOF +-------------------------------------+ | JumpServer @Version1.0 | +-------------------------------------+ | a. WebServer Apache. | | b. MySQL Databases Server. | | c. PHP Development Computer. | | d. Quit | +-------------------------------------+ EOF read -p "请输入你要登录的服务器编号: " computer case $computer in a) ssh jumper@192.168.161.129 # 可以嵌套子case循环 ;; b) ssh jumper@192.168.161.130 ;; c) ssh jumper@192.168.161.131 ;; d) exit ;; *) printf "ERROR: Please redo your select!" ;; esac done
三、shell 编程-函数
function (功能) 功能函数 完成特定功能的代码片段 函数必须先定义才能使用 优点:避免重复的代码 定义函数 1 怎么定义 2 定义什么东西 调用函数 1 可以本地调用,也可以调用别的脚本的函数 2 在不调用之前,它相当于没有 取消函数 1 把函数名想象成一个变量名 函数传参 1 跟脚本传参很类似 命名空间 1 在shell语言中命名空间函数内和函数外是一致的,函数内外不能赋值同样名字的变量 local 1 声明一个本地变量(局部变量) 返回值 return value value不能超过0-255
shell 函数function
函数声明
function_name () { list of commands }函数名 function_name,这就是你将使用它从其他地方在你的脚本调用。
取消函数
unset myfunc //取消函数myfunc() //函数定义 { echo “This is my first shell function” } myfunc //函数调用产生以下执行结果
./test.sh This is my first shell function函数必须提前定义测试 [root@newrain fun]# cat fun05.sh #!/bin/bash fun () { echo "hello" } fun unset fun fun [root@newrain fun]# bash fun05.sh hello fun05.sh: line 8: fun: command not found函数的返回值,返回的是函数体内最后一条命令是否成功的返回值 [root@newrain fun]# systemctl stop httpd [root@newrain fun]# cat fun03.sh #!/bin/bash fun() { systemctl status httpd &>/dev/null systemctl status vsftpd &>/dev/null } fun echo $? [root@newrain fun]# systemctl stop vsftpd [root@newrain fun]# bash fun03.sh 3函数传参 在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数
示例
[root@newrain fun]# cat fun06.sh #!/bin/bash fun() { echo $[$1*$2*$3] } fun 1 2 3 修改版: [root@newrain fun]# cat fun06.sh #!/bin/bash if [ ! $# -eq 3 ];then echo "Must Input Three number: p1 p2 p3" exit fi fun() { echo $[$1*$2*$3] } fun $1 $2 $3四、shell 编程-数组
普通数组:只能用整数作为数组的索引 关联数组:可以使用字符串作为数组的索引
数组定义
普通数组定义: [root@newrain shell]# books=( linux shell awk sed ) 引用: [root@newrain shell]# echo ${books[0]} linux [root@newrain shell]# echo ${books[1]} shell [root@newrain shell]# echo ${books[2]} awk 关联数组需要提前声明 declare -A myarry1 [root@newrain shell]# declare -A myarry1 [root@newrain shell]# myarry1=([name]=newrain [sex]=man [age]=26) [root@newrain shell]# echo ${myarry1[name]} newrain [root@newrain shell]# echo ${myarry1[age]} 26
定义方法1: # declare -a myarry=(5 6 7 8) # echo ${myarry[2]} 显示结果为 7 定义方法2: # array=( one two three four five six ) # array2=(tom jack alice) # array3=(`cat /etc/passwd`) # array4=(tom jack alice "bash shell") # array5=(1 2 3 4 5 6 7 "linux shell" [20]=saltstack) 定义方法3: # 普通数组下标只能是数字 #!/bin/bash area[11]=23 area[13]=37 area[51]="UFOs"
访问数组
当设置任何数组变量时,可以访问它
[root@newrain shell]# aa=(haha heihei baibai) [root@newrain shell]# echo ${aa[0]} //访问数组中的第一个元素 [root@newrain shell]# echo ${aa[@]} //访问数组中所有的元素,等同与echo ${aa[*]} [root@newrain shell]# echo ${#aa[@]} //统计元素的个数 [root@newrain shell]# echo ${!aa[@]} //统计索引
${array_name[index]} //引用
示例
#!/bin/bash NAME[0]="BJ" NAME[1]="SH" NAME[2]="SZ" NAME[3]="GZ" NAME[4]="HZ" NAME[5]="ZZ" echo "First Index: ${NAME[0]}" echo "Second Index: ${NAME[1]}" echo "sixth Index: ${NAME[5]}"输出结果为
$./test.sh First Index: BJ Second Index: SH sixth Index: ZZ您可以访问数组中的所有项目通过以下方式之一:
${array_name[*]} ${array_name[@]}示例
#!/bin/sh NAME[0]="BJ" NAME[1]="SH" NAME[2]="SZ" NAME[3]="GZ" NAME[4]="HZ" echo "First Index: ${NAME[*]}" echo "Second Index: ${NAME[@]}"输出结果
$./test.sh First Index: BJ SH SZ GZ HZ Second Index: BJ SH SZ GZ HZ疑难点 shell数组中"*" 和 "@" 区别
关于在shell脚本中数组变量中 “*”跟 “@” 区别 “*”当变量加上“” 会当成一串字符串处理. “@”变量加上“” 依然当做数组处理. 在没有加上“” 的情况下 效果是等效的.示例
#!/usr/bin/env bash array=(gz cloud 19) echo "case 1" for line in "${array[@]}" do echo $line done echo "case 2" for line in "${array[*]}" do echo $line done echo "case 3" for line in ${array[*]} do echo $line done echo "case 4" for line in ${array[@]} do echo $line done执行结果
case 1 gz cloud 19 case 2 gz cloud 19 case 3 gz cloud 19 case 4 gz cloud 19遍历数组while [root@newrain array]# cat array01.sh #!/bin/bash #++ i 是先自加1后赋值;i ++ 是先赋值后自加1。 while read line do host[i++]=$line # 观察i++ 和 ++i的区别 done </etc/hosts for i in ${!host[@]} # 数组的元素索引 do echo "$i:${host[i]}" done 遍历数组for [root@newrain array]# cat array02.sh #!/bin/bash IFS='' for line in `cat /etc/hosts` # 读取文件中的每一行 do host[j++]=$line done for i in ${!host[@]} do echo ${host[i]} done 以上两个脚本都是读取文件中的行,然后加到一个数组中并进行遍历。 练习题:统计shell的种类和数量 思路:最后一列的sh种类不同,我们可以单独取出最后一列 /etc/passwd
五、正则表达式RE
正则表达式基本元字符
正则表达式拓展元字符
用来处理文本
正则表达式(Regular Expression, RE)是一种字符模式, 用于在查找过程中匹配指定的字符. 在大多数程序里, 正则表达式都被置于两个正斜杠之间;
例如/l[oO]ve/就是由正斜杠界定的正则表达式, 它将匹配被查找的行中任何位置出现的相同模式. 在正则表达式中,元 字符是最重要的概念
元字符使正则表达式具有处理能力。所谓元字符就是指ß那些在正则表达式中具有特殊意义的专用字符,可以用来规定 其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。
No.1 正则表达式基本元字符
基本正则表达式元字符 元字符 示例 功能 ^ 行首定位符 ^love $ 行尾定位符 love$ . 匹配单个字符 l..e * 匹配前导符0到多次 ab*love .* 匹配任意多个字符 (贪婪匹配 [] 匹配方括号中任意一个字符 [lL]ove [ - ] 匹配指定范围内的一个字符 [a-z0-9]ove [^] 匹配不在指定组里的字符 [^a-z0-9]ove \ 用来转义元字符 love\. \< 词首定位符 #由数组或字母组成的 \<love \> 词尾定位符 love\> \(\) 匹配后的标签 # 在vim中测试
No.2正则表达式拓展元字符
= 等于 != 不等于 =~ 匹配 扩展正则表达式元字符 + 匹配一个或多个前导字符 [a-z]+ove ? 匹配零个或一个前导字符 lo?ve a|b 匹配a或b love|hate () 组字符loveable|rs love(able|rs) ov+ ov+ (ov)+ (..)(..)\1\2 标签匹配字符 # (love)able\1er x{m} 字符x重复m次 o{5} x{m,} 字符x重复至少m次 o{5,} x{m,n} 字符x重复m到n次 o{5,10}