一、流程控制之for循环
1.1 语法
#===========》Shell风格语法
for 变量名 [ in 取值列表 ]
do
循环体
done
#===========》C语言风格语法
for ((初值;条件;步长))
do
循环体
done
#==============》Python风格
for i in (可迭代对象)
print(i)
shell风格的for,常用in列表方式
for i in 1 2 3
for i in {1,2,3}
for i in {1..9}
for i in {9..1}
for i in {a..z}
for i in {A..Z}
for i in {X..Z}
for i in $(命令) # 例如:for i in $(head -10 /etc/passwd);do echo $i|cut -d: -f1,2;done
for i in $(find ...)
1.2 for循环取值方式
for 循环默认使用空格为分隔符,可以使用 IFS 来自定义分隔符
以冒号做分隔符 IFS=:
以换行符做为字段分隔符IFS=$'\n'
默认是以空格为分隔符取值
[root@manager for]# cat test2.sh
for i in $(cat /etc/hosts)
do
echo $i
done
[root@manager for]# sh test2.sh
127.0.0.1
localhost
localhost.localdomain
localhost4
localhost4.localdomain4
::1
localhost
localhost.localdomain
localhost6
localhost6.localdomain6
修改为换行符取值
[root@manager for]# cat test2.sh
IFS=$'\n'
for i in $(cat /etc/hosts)
do
echo $i
done
[root@manager for]# sh test2.sh
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
1.3 for循环通过文件创建用户
[root@manager for]# cat user.txt
tom1:123
tom2:123
tom3:123
tom4:123
tom5:123
[root@manager for]# cat adduser.sh
for i in $(cat ./user.txt)
do
username=$(echo $i | awk -F ":" '{print $1}')
passwd=$(echo $i | awk -F ":" '{print $2}')
id $username
if [ $? -eq 0 ];then
echo "用户已存在"
continue
else
useradd $username
echo "$passwd" | passwd --stdin $username
echo "$username 创建成功"
fi
sleep 1
done
1.4 输出整数升序降序
[root@manager for]# cat sort.sh
a=10
b=0
for i in $(seq 1 10)
do
let a--
let b++
echo $a------$b
done
1.5 100以内能被3整除数之和
[root@manager for]# cat division.sh
sum=0
for i in {1..100}
do
remainder=$(echo $i%3 | bc )
if [ $remainder -eq 0 ];then
sum=$[ $sum + $i ]
fi
done
echo $sum
[root@manager for]# sh division.sh
1683
1.6 for循环计算1+2+3+n的值
[root@manager for]# cat sum.sh
sum=0
for i in {1..100}
do
sum=$[ $sum+$i ]
done
echo $sum
1.7 探测主机存活性
# 批量生成ip命令
[root@manager for]# seq 1 254 | sed -r 's/(.*)/172.16.1.\1/' > /tmp/ip.txt
[root@manager for]# cat ip.sh
for i in $(cat /tmp/ip.txt)
do
{
ping $i -c1 &>/dev/null
if [ $? -eq 0 ];then
echo "$i is ok"
else
echo "$i is unavaliabled"
fi
}&
done
wait
echo "done"
1.8 探测ip是否存活
1.判断是否存活;
2.存活,则直接输出;
3.不存活,在探测三次;
[root@manager for]# cat ip3.sh
# 定义探测函数
test_ip(){
for i in {1..3}
do
ping $1 -c1 &>/dev/null
if [ $? -eq 0 ];then
echo "$1尝试第$[ $i+1 ]次才通"
break
else
echo "$1尝试第$[ $i+1 ]次仍然不通 "
fi
done
}
for i in {1..254}
do
{
ip=172.16.1.$i
ping $ip -c1 &>/dev/null
if [ $? -eq 0 ];then
echo "$ip is ok"
else
test_ip $ip # 调用测试函数
fi
}&
done
wait
echo "done............................"
1.9 探测主机开放哪些端口
1.有一个ip.txt的文件,里面有很多IP地址。
2.有一个port.txt的文件,里面有很多端口号。
3.现在希望对ip.txt的每个IP地址进行port.txt文件中的端口号进行挨个探测。
4.最后将开放的端口和IP保存到一个ok.txt文件。
[root@manager for]# cat test.sh
for ip in $(cat ip.txt)
do
for port in $(cat port.txt)
do
nc -z $ip $port &>/dev/null # 探测端口
if [ $? -eq 0 ];then
echo "$ip:$port" >> /opt/ip_port.txt
if [ $port -eq 80 ];then
echo -e "\e[31m $ip 的 $port开放了,比较危险..\e[0m"
else
echo -e "\e[33m $ip 的 $port开放了\e[0m"
fi
fi
done
done
1.10 获取系统用户,并按照编号输出
[root@web02 ~]# cat print_user.sh
IFS=$'\n'
for i in $(cat /etc/passwd)
do
user=$(echo $i | awk -F ":" '{print $1}')
let k++
echo "This is user:$user $k"
done
1.11 获取普通用户的基本组及附加组
[root@manager ~]# cat group.sh
user=$(awk -F ":" '$3>=1000{print $1}' /etc/passwd)
for user in $user
do
flag=$(id $user | grep , )
if [ -z "$flag" ];then # 判断是否有附加组
group=$(id $user | awk -F "[ =]" '{print $NF}')
groups=null
else
group=$(id $user | awk -F "[ =,]" '{print $(NF-2)}')
groups=$(id $user | awk -F "[ =,]" '{print $(NF-1),$NF}')
fi
echo "$user的基本组为:$group"
echo "$user的附加组为:$groups"
done
# 添加两个基本用户,一个有附加组,一个没有
[root@manager for]# groupadd ops
[root@manager for]# groupadd dev
[root@manager for]# useradd tom -u 6666 -G dev,ops -c "good man" -s /bin/shell
[root@manager for]# id tom
uid=6666(tom) gid=6666(tom) groups=6666(tom),1005(ops),1006(dev)
[root@manager for]# useradd jack -u 6667
[root@manager for]# id jack
uid=6667(jack) gid=6667(jack) groups=6667(jack)
[root@manager ~]# sh group.sh
nfsnobody的基本组为:65534(nfsnobody)
nfsnobody的附加组为:null
tom5的基本组为:1004(tom5)
tom5的附加组为:null
tom的基本组为:6666(tom)
tom的附加组为:1005(ops) 1006(dev)
jack的基本组为:6667(jack)
jack的附加组为:null
1.12 备份数据库,分库
#!/usr/bin/bash
. /etc/init.d/functions
Db_Name=$(mysql -uroot -e "show databases;" | sed 1d | egrep -v "*_schema")
Date=$(date +%F)
DB_Dir=/backup/mysql/${Date}
# 确保备份的目录是存在的
if [ ! -d ${DB_Dir} ];then
mkdir -p ${DB_Dir}
fi
# 备份业务逻辑
for database in ${Db_Name}
do
mysqldump -uroot -B ${database} > ${DB_Dir}/${database}.sql
# 判断文件是否有内容
if [ -s ${DB_Dir}/${database}.sql ];then
action "${DB_Dir}/${database}.sql 备份成功" /bin/true
else
action "${DB_Dir}/${database}.sql 备份失败" /bin/true
fi
done
1.13 备份数据库,分库分表
#!/usr/bin/bash
. /etc/init.d/functions
DB_Name=$(mysql -uroot -e "show databases;" | sed 1d | egrep -v "*_schema")
Date=$(date +%F)
DB_Dir=/backup/mysql/${Date}
# 外循环,提取库的名称
for database in ${DB_Name}
do
# 创建库对应的备份目录
if [ ! -d $DB_Dir/$database ];then
mkdir -p "$DB_Dir/$database"
fi
# 提取表名称
TB_Name=$(mysql -e "use ${database};show tables;" | sed 1d)
# 内循环,基于库名称然后取获取表名称
for table in ${TB_Name}
do
mysqldump -uroot ${database} ${table} > $DB_Dir/${database}/${table}.sql
if [ -s $DB_Dir/${database}/${table}.sql ];then
action "$DB_Dir/${database}/${table}.sql 备份成功" /bin/true
else
action "$DB_Dir/${database}/${table}.sql 备份失败" /bin/false
fi
done
done
1.14 九九乘法表
1.python实现
for i in range(1, 10):
for j in range(1, i + 1):
print('{}*{}={} '.format(i, j, i * j), end="")
print()
2.shell实现
[root@manager for]# cat multi.sh
for i in {1..9}
do
for j in {1..9}
do
if [ $j -le $i ];then # 小于等于才打印
echo -n "$i*$j=$(echo "$i*$j" | bc) "
fi
done
echo '' #换行
done
[root@manager for]# sh multi.sh
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
1. 5 模拟删除tomcat 后恢复文件
[root@web01 /]# cd soft/
[root@web01 soft]# cp -rp apache-tomcat-9.0.52 /opt/ # 将tomcat拷贝一份到/opt 并做软连接
[root@web01 soft]# cd /opt/
[root@web01 opt]# ln -s apache-tomcat-9.0.52/ tomcat
[root@web01 opt]# /opt/tomcat/bin/startup.sh # 启动tomcat
[root@web01 opt]# ps aux | grep tomcat
root 41945 0.1 11.0 2275268 110012 ? Sl Sep19 14:55 /usr/local/jdk/bin/java -Djava.util.logging.config.file=/soft/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /soft/tomcat/bin/bootstrap.jar:/soft/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/soft/tomcat -Dcatalina.home=/soft/tomcat -Djava.io.tmpdir=/soft/tomcat/temp org.apache.catalina.startup.Bootstrap start
root 41829 0.0 0.0 112812 976 pts/0 S+ 20:05 0:00 grep --color=auto tomcat
[root@web01 opt]# rm -rf tomcat/ # 删除
[root@web01 opt]# ll /proc/41945/fd
可以看到,删除后文件描述符对应的文件闪烁,而且 都带有delete后缀
[root@web01 opt]# ll /proc/41945/fd | grep deleted
而且java是个多线程的程序
[root@web01 opt]# pstree -p 41945
java(41945)─┬─{java}(41946)
├─{java}(41947)
├─{java}(41948)
├─{java}(41949)
├─{java}(41950)
├─{java}(41951)
├─{java}(41952)
├─{java}(41953)
├─{java}(41954)
├─{java}(41955)
├─{java}(41956)
├─{java}(41957)
├─{java}(41961)
├─{java}(41962)
├─{java}(41963)
├─{java}(41964)
├─{java}(41984)
├─{java}(42007)
├─{java}(42008)
├─{java}(42009)
└─{java}(42016)
也就是说,我们还需要过滤出所有线程id 下对应的/proc/线程号/fd中带有deleted标志的文件
[root@web01 opt]# mkdir /opt/apache-tomcat-9.0.52/{lib,bin,logs} # 创建对应的目录
[root@web01 opt]# ll /proc/41945/fd | grep delete | awk '{print $9}' # 获取第9列的文件描述符
[root@web01 opt]# ll /proc/41945/fd | grep delete | awk '{print $11}' # 获取文件名
[root@web01 ~]# cat recorve.sh
#!/bin/bash
. /etc/init.d/functions
# java 进程id
pid=41945
# 过滤出java进程和所有线程id
thread_id=$(pstree -p 41945 | egrep -o "[0-9]+")
#遍历所有线程
for id in $thread_id
do
# 取每个线程下的文件描述符
fd=$(ls -l /proc/$id/fd | grep delete | awk '{print $9}')
for i in $fd
do
# 基于文件描述符获取文件名称
file_name=$(ls -l /proc/$id/fd/$i | awk '{print $11}')
#echo $file_name
# 恢复文件
cat /proc/$id/fd/$i > $file_name
# 获取恢复后文件的md5值
remd5=$(md5sum "$file_name" 2>/dev/null | awk '{print $1}')
#源文件名,真实情况下已删除后就不存在了
# 变量替换
old_file_name=${file_name/opt/soft}
#echo $old_file_name
# 获取源文件MD5值,真实情况不存在
old_md5=$(md5sum "$old_file_name" 2>/dev/null | awk '{print $1}')
if [ $remd5 == $old_md5 ];then
action "$file_name 恢复成功" /bin/true
else
action "$file_name 恢复失败" /bin/false
fi
done
done
运行部分截图:
二、流程控制之while循环
2.1 语法:
# 一、while语句结构:条件为真时,执行循环体代码
while 条件
do
循环体
done
# 二、until语法结构:条件为假时,一直执行循环体代码,直到条件变为真
until 条件
do
循环体
done
示例:
[root@manager while]# cat a.sh
#!/bin/bash
x=0
while (($x < 3))
do
echo $x
let x++
done
echo "================"
y=0
until (($y == 3))
do
echo $y
let y++
done
[root@manager while]# ./a.sh
0
1
2
================
0
1
2
2.1 降序输出10到1的数字
[root@manager while]# cat sort.sh
var=10
while (( $var>0 ))
do
echo $var
var=$[ $var-1 ]
done
2.2 网站探测
[root@manager while]# cat url.sh
#!/bin/bash
timeout=3
fails=0
success=0
url=$1
while true
do
echo "=====>$[ $fails+1]"
if [ $fails -eq 3 ]
then
echo "页面前访问3次均失败"
break
fi
wget --timeout=$timeout --tries=1 http://$url -q
if [ $? -ne 0 ]
then
let fails++
else
echo "页面访问成功"
break
fi
done
[root@manager while]# sh url.sh www.baidu.com
=====>1
页面访问成功
[root@manager while]# sh url.sh xxx.baidu.com
=====>1
=====>2
=====>3
=====>4
页面访问3次均失败,本次退出
2.3 猜数字
方法一: 通过random变量产生随机数 (0-32768)
echo $RANDOM
方法二: 通过openssl命令产生随机数
openssl rand -base64 10
方法三: 通过时间信息获取随机数
date +%S%N
方法四: 通过一个特殊设备文件生成随机数
head -c9 /dev/urandom|cksum
tr -dc 0-9 < /dev/urandom | head -c8
方法五: 利用UUID文件生成随机数
cat /proc/sys/kernel/random/uuid
# 代码实现
[root@manager while]# cat guess_age.sh
#!/bin/bash
num=`echo $((RANDOM%100+1))` # 对100取余,生成1到100的随机数
count=0
while :
do
[ $count -eq 3 ] && echo "猜的次数超过3次,退出" && exit
read -p "请输入[1-100]之间的一个数字:" x
[[ ! $x =~ ^[0-9]+$ ]] && echo "必须输入数字" && continue
if [ $x -gt $num ];then
echo "猜大了"
elif [ $x -lt $num ];then
echo "猜小了"
else
echo "猜对了"
break
fi
let count++
done
2.4 while c语言风格写法
[root@manager while]# cat 1.sh
#!/bin/bash
i=1
while ((i<10))
do
echo $i
((i++))
done
[root@manager while]# sh 1.sh
1
2
3
4
5
6
7
8
9
2.5 循环控制
在使用循环语句进行循环的过程中,有时候需要在未达到循环结束条件时强制跳出循环;那么 Shell 给我们提供了内置方法来实现该功能:exit、break、continue
- 当脚本碰到 exit 时,直接退出,剩余不管有多少代码都不执行
- 当脚本碰到 break 时,会跳出当前循环,但会执行循环之后的所有的代码。
- 当脚本碰到 continue 时,跳过当前循环,直接进入下一次循环,直到循环结束,然后继续执行循环之后的代码。
2.5.1 打印1-9,当数值为5则跳过本次循环,继续下一次循环。
请分别使用for和while实现
[root@manager while]# cat for.sh
for i in {1..9}
do
if [ $i -eq 5 ];then
continue
else
echo $i
fi
done
[root@manager while]# cat while.sh
i=0
while (($i<10))
do
#((i++))
let i++
if (($i==5));then
continue
fi
echo $i
done
2.5.2 破解随机字符串
字符串 efbaf275cd、4be9c40b8b、44b2395c46是通过对随机数变量RANDOM随机执行命令: echo $RANDOM|md5sum|cut -c1-10
后的结果请破解这些字符串对应的RANDOM值
[root@manager while]# cat track.sh
for i in efbaf275cd 4be9c40b8b 44b2395c46
do
num=0
while true
do
str=$(echo $num|md5sum|cut -c1-10)
if [ $str == "$i" ];then
echo "$num加密后的结果是$i"
break
fi
num=$[ $num + 1 ]
done
done
[root@manager while]# sh track.sh
15000加密后的结果是efbaf275cd
12000加密后的结果是4be9c40b8b
9000加密后的结果是44b2395c46
2.5.3 while 从文件读取添加用户
# for是按照空格作为分隔符读取
# while是按照行来读取的文件
while read line
do
user=$(echo $line | awk -F ':' '{print $1}')
pass=$(echo $line | awk -F ':' '{print $2}')
id $user &>/dev/null
if [ $? -ne 0 ];then
useradd $user
echo "$pass" | passwd --stdin $user &>/dev/null
echo "$user $pass is create ok"
else
echo "$user" 已经存在
fi
done<user.txt
2.5.4 防止ssh暴力破解脚本
如果使用 tom 用户登录系统,则立即将其踢出,然后将其拉入黑名单,以防止该用户继续使用该IP地
址进行登录。
将 sshd:username ip地址 内容 至/etc/hosts.deny 则可拒绝用户登陆系统。
#!/usr/bin/bash
. /etc/init.d/functions
index=0
while true
do
User=tom
login_user=$(who | grep ${User} | wc -l)
deny_file=/etc/hosts.deny
if [ $login_user -gt 0 ];then
# 说明用户是存在
login_user_ip=$(who | grep ${User} | awk '{print $NF}' | awk -F '[()]' '{print $2}'|sort |uniq)
# 写入限制
echo "sshd:${User} ${login_user_ip}" >> ${deny_file}
# 剔除用户
pkill -9 -u ${User}
action "探测 ${User} 已经登录系统, 已被剔除" /bin/false
else
index=$[ $index + 1]
action "探测 ${User} 是否登录系统, 正在进行第 ${index} 次探测,目前正常" /bin/true
sleep 3
continue
fi
done
三、select循环
select表达式是bash的一种扩展应用,擅长于交互式场合。用户可以从一组不同的值中进行选择
select var in ...
do
...
break
done
[root@manager select]# cat select1.sh
#!/bin/bash
PS3="choose one:" # select默认使用PS3变量的值做提示符
select var in {A..E}
do
echo "your chocie is $var"
break # 跳出select,否则是死循环
done
[root@manager select]# sh select1.sh
1) A
2) B
3) C
4) D
5) E
choose one:3
your chocie is C
若省略 in list 则select会把 $@ 当做列表项
[root@manager select]# cat select2.sh
#!/bin/bash
PS3="choose one:"
select var
do
echo "your chocie is $var"
break
done
[root@manager select]# sh select2.sh 香蕉 苹果 西瓜
1) 香蕉
2) 苹果
3) 西瓜
choose one:1
your chocie is 香蕉