shell使用循环
1. for循环
脚本在执行任务的时候,总会遇到需要循环执行的时候,比如说我们需要脚本每隔五分钟执行一次ping的操作,除了计划任务,我们还可以使用脚本来完成,那么我们就用到了循环语句。
1.1 for循环语法1 -- for .. in ..
for var in value1 value2 ...
do
要执行的代码块
done
#!/bin/bash
#1 for直接赋值
#for var in 1 2 3 4 5
#2 使用命令赋值
#for var in `seq 1 2 9`
#3 使用字符串赋值(这个字符串可以是使用命令后的结果)
for var in hello linux, hello c,java and more
do
echo $var
sleep 0.5
done
#上面第3个的执行结果如下:
[root@haha shell_0411]# sh for_1.sh
hello
linux,
hello
c,java
and
more
也可以 for ..;do 连起来使用
#!/bin/bash
for var in 1 2 3 4 5;do
echo $var
sleep 0.5
done
1.2 for循环语法2 -- C式
for ((变量;条件;自增减运算 ))
do
要执行的代码块
done
#!/bin/bash
#for循环使用单个变量
for ((i=1;i<10;i++))
do
echo $i
done
echo "for sysle with more var"
#for循环使用多个变量
for ((a=1,b=9;a<10,b>0;a++,b--))
do
echo $a -- $b
done
得出来的结果如下:
[root@haha shell_0411]# sh for_c_style.sh
1
2
3
4
5
6
7
8
9
for sysle with more var
1 -- 9
2 -- 8
3 -- 7
4 -- 6
5 -- 5
6 -- 4
7 -- 3
8 -- 2
9 -- 1
1.3 for死循环
for ((;;))
do
要执行的代码块
done
1.4 for循环break和continue
continue:跳过循环中的某次循环,继续执行下次循环
break:跳出当前循环,继续执行循环体之外的代码
break N:break后面跟数字,可以一次性跳出N层循环。嵌套循环从内到外依次是第1层、第2层、第3层....例如 break 2,就表示跳出内外两层循环(当前循环以及外面一层循环)。
continue和break的基本用法如下:
#!/bin/bash
# 用户输入什么就输出什么,如果输入为0就跳过,输入为q就退出
for ((;;))
do
read -p "char: " ch
if [ $ch -eq 0 ]
then
continue
elif [ $ch == "q" ]
then
break
else
echo "当前输入为:$ch"
fi
done
break N 的用法:脚本中的 break 2,就表示跳出本层以及本层之外的那层循环,总共两层循环, 如:
#!/bin/bash
#如果用户输入0就跳出当前循环,如果输入q程序直接退出
for ((;;))
do
echo "外循环开始>>>"
for ((;;))
do
read -p "内循环请输入:" ch
if [ $ch == "q" ]
then
break 2
elif [ $ch == 0 ]
then
break
else
echo "您输入的为:$ch"
fi
done
done
for循环练习:
例1: 写一个监控某个主机是否存活的脚本
脚本中的命令说明:
1、ping -c1 -w1 中 -c1是指ping的次数,-w是指执行的超时时间,单位为秒
2、&>/dev/null 是指标准输出和错误输出都输出到/dev/null上,而不在界面上显示
#!/bin/bash
#监控某个主机存活的脚本
for ((;;))
do
ping -c1 $1 &>/dev/null
if (($? == 0))
then
echo -e "$1的主机还存活着,当前时间为: `date "+%F %H:%M:%S"`"
else
echo -e "$1的主机已经失去联系,当前时间为: `date "+%F %H:%M:%S"`"
fi
sleep 5
done
执行脚本: bash monitor_service.sh 192.168.17.137
输出结果如下:
[root@haha shell_0413]# bash monitor_service.sh 192.168.17.137
192.168.17.137的主机还存活着,当前时间为: 2020-04-16 00:00:04
192.168.17.137的主机还存活着,当前时间为: 2020-04-16 00:00:09
例2: 输入一个ip文件,检查该文件中有哪些能用的ip,并记录下来
串行版:
注意: >ip.log 先清日志内容
#!/bin/bash
#输入一个ip文件,检查该文件中有哪些能用的ip,并记录下来
>ip.log
for ip in `cat $1`
do
ping_flag="down"
ping -c1 -w3 $ip &>/dev/null
if [ $? -eq 0 ]; then
ping_flag="up"
fi
echo "the $ip is $ping_flag!" | tee -a ip.log
done
并行版:
注意:{}& 是采用并行的方式并放在后台执行;
wait 是等待后台所有ip都检查完再往下走。
#!/bin/bash
#输入一个ip文件,检查该文件中有哪些能用的ip,并记录下来
>ip.log
for ip in `cat $1`
do
{
ping_flag="down"
ping -c1 -w3 $ip &>/dev/null
if [ $? -eq 0 ]; then
ping_flag="up"
fi
echo "the $ip is $ping_flag!" | tee -a ip.log
}&
done
wait
echo "finish..."
例3: 按照用户文件来添加用户和密码
注意:1. IFS=$':' 或 $'-' 或 $'/' 或... 来重新定义分隔符;
2. 为什么要重新定义分隔符?
for line in `cat adduser.txt` ==> 转为for循环时为 for line in user1 123123 user2 123456 user3 123321,默认的分隔符为 空格, 不重新定义分隔符的话就按照空格分开,则 $password 为空。
#!/bin/bash
#添加adduser.txt中的用户和密码
# 判断是否有参数传进来
if [ $# == 0 ]; then
echo "error info: `basename $0 need a detail argument!`"
exit 1
fi
# 判断传进来的参数是不是一个具体文件
if [ ! -f $1 ]; then
echo "error info: it mast be a file, not directory!"
exit 2
fi
# 希望for处理文件时按照回车符分割,而不是空格或tab空格,可以用 IFS 重新定义分隔符
# 以换行为分隔符,这块必须是单引号,不然会被转义
IFS=$'\n'
for line in `cat $1`
do
if [ ${#line} == 0 ]; then
echo "Nothing to do"
continue
fi
user=`echo $line | awk '{print $1}'`
password=`echo $line | awk '{print $2}'`
id $user &>/dev/null
if [ $? == 0 ]; then
echo "user $user already exists!"
else
useradd $user
echo $password | passwd --stdin $user &>/dev/null
if [ $? == 0 ]; then
echo "$user is created"
fi
fi
done
对于处理文件的操作时,while 比 for 循环好用,不用考虑分隔符,while 是逐行读文件,对行数据进行操作。
2. while循环
2.1 while 语法
while [ condition ] #注意,条件为真while才会循环,条件为假,while停止循环
do
commands
done
2.2 while 或 for 取舍?
知道循环次数就可以用for,比如说一天需要循环24次;如果不知道代码要循环多少次,那就用while。
2.3 用while处理文件更方便
用for循环处理文件,在遍历时会因为空格的存在,要重新指定分隔符;但是while处理文件时,逐行读取文件内容。如上 for 循环的最后一个例子。
使用 while 循环对for循环的例3重新编写:
#!/bin/bash
#使用while对adduser.txt内容处理,添加用户
while read line
do
if [ ${#line} == 0 ]; then
echo "Nothing to do!"
continue
fi
user=`echo $line | awk '{print $1}'`
password=`echo $line | awk '{print $2}'`
id $user &>/dev/null
if [ $? == 0 ]; then
echo "user $user is already exists!"
else
useradd $user
echo $password | passwd --stdin $user &>/dev/null
if [ $? == 0 ]; then
echo "$user has been created"
fi
fi
done < adduser.txt
2.4 while死循环 -- while : 或 while true
注意:let start++ 或 start=$((start+1)) 都可以实现累加。
#!/bin/bash
start=1
while [ $start -le 10 ]
do
if [ $start == 5 ]; then
# let start++
start=$((start+1))
continue
else
echo "start = $start"
fi
# let start++
start=$((start+1))
done
3. shell内置命令 -- shift
shift:将所有的位置参数向左移n位,默认是shift 1,即位置参数向左移一位;shift 2时,位置参数个数必须是2的整数倍。
#!/bin/bash
# 用shift实现位置参数左移
while [ $# -ne 0 ];do
let sum+=$1
shift
done
echo "sum is : $sum"
调用脚本时, sh filename.sh 1 2 3 4 5,第一个位置参数$1为1,每执行一次while循环,遇到while,就会将参数1 2 3 4 5向左移一位,变成2 3 4 5,第一个位置参数$1变成2,以此类推,直至$1没有值,循环结束。