循环命令主要有三种:for(for又分两种)、while、until,另外有两个控制循环提前结束的命令:continue、break。
一、 for循环
for循环有两种格式,一种bash shell基本格式,一种C语言风格的形式。
1. bash shell的基本格式
for variable [in words]
do
commands
done
for命令的强大之处在于可以通过多种方式来创建in后面的列表,例如:
- 直接由你列出所有要循环的值
#!/bin/bash
for i in f1 f2 f3 ;
do
echo $i is appoint ;
done
for str in 'This is a string'
do
echo $str
done
- 花括号创建 for i in {A..D}
#!/bin/bash
for i in {1..10}
do
echo $(expr $i \* 3 + 1);
done
- 路径名展开 for in in dir*.txt
#!/bin/bash
for file in /proc/*;
do
echo $file is file path \! ;
done
- 命令替换 for i in $(seq 1 5) 或者 for i in `ls`
#!/bin/bash
for i in $(seq 1 10)
do
echo $(expr $i \* 3 + 1);
done
#!/bin/bash
for i in `ls`;
do
echo $i is file name\! ;
done
- 从文件中读取(可更改IFS变量改变默认字段分隔符)
#!/bin/bash
file="states"
IFS=$'\n' #指定字段分隔符仅为\n,默认为空格、Tab与\n
for state in `cat $file`;
do
echo "visit beautiful $state"
done
2. C语言风格
# for (())中变量不以$开头
for (( 初始值; 循环条件; 迭代量 ))
do
commands
done
例子
for (( i=0; i<5; i++ ))
do
echo $i
done
也允许在初始值中使用多个变量,分别进行处理,但循环条件只能有一个
for (( i=0, j=10; i<5; i++, j-- ))
do
echo "$i - $j"
done
二、 while循环
和if一样,while也是利用命令的退出状态判断,只要退出状态为0,它就执行循环体内的内容
while commands
do
commands
done
例子
#!/bin/bash
count=1
while [ $count -lt 5 ]
do
echo $count
count=$((count + 1)) # 也可以写成 count=$(expr $count + 1)
done
创建多个用户
#!/bin/bash
input="users.csv"
while IFS=',' read -r userid name
do
echo "adding $userid"
useradd -c "$name" -m $userid
done < "$input"
来看一个复杂点的例子(here-document语法里的内容必须顶格写,否则会报错)
#!/bin/bash
DELAY=3 # 结果展示时长
while [[ $REPLY != 0 ]]; do # 当输入值不为0,清除屏幕内容,显示菜单并执行对应命令
clear
cat <<EOF
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
EOF
read -p "Enter selection [0-3] > "
if [[ $REPLY =~ ^[0-3]$ ]]; then # ~是对后面的正则表达式表示匹配的意思,匹配输出1,不匹配输出0
if [[ $REPLY == 1 ]]; then
echo "Hostname: $HOSTNAME"
uptime
sleep $DELAY
fi
if [[ $REPLY == 2 ]]; then
df -h
sleep $DELAY
fi
if [[ $REPLY == 3 ]]; then
if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
sleep $DELAY
fi
else
echo "Invalid entry."
sleep $DELAY
fi
done
echo "Program terminated."
菜单包含在 while 循环中,每次用户选择之后,能够让程序重复显示菜单。只要 REPLY 不为0,循环就会继续,菜单就能显示,用户可以重新选择。每次动作完成之后,会执行一个 sleep 命令,所以在清空屏幕和重新显示菜单之前,程序将会停顿几秒钟,为的是能够看到选项输出结果。 一旦 REPLY 等于0,则表示选择了“退出”选项,循环就会终止,程序继续执行 done 语句之后的代码。
三、 until循环
也是利用命令的退出状态判断,但与while相反,退出状态不为0时才执行循环体内的内容
until commands
do
commands
done
简单例子
#!/bin/bash
count=1
until [ $count -gt 5 ]
do
echo $count
count=$((count + 1))
done
四、 嵌套循环
上面几种循环间都是可以相互嵌套的,可根据需要选择,例如:
循环处理文件数据,结合前面提到的IFS环境变量,取出/etc/passwd各字段值
#! /bin/bash
IFS.OLD=$IFS
IFS=$'\n' #外层循环使用\n分段,解析出各行
for entry in $(cat /etc/passwd)
do
echo "Values in $entry -"
IFS=: #内层循环使用:分段,解析行中各字段
for value in $entry
do
echo " $value"
done
done
五、 控制循环
如果一旦启动循环就只能等到完成所有迭代,有时是非常低效的,在符合某些条件后有时可以跳出循环。跳出方法有两种:break和continue。
1. break
默认是立刻结束本层循环,执行外层内容(可能是外层循环或其他命令)。
改写下前面的例子,如果用户名为oracle,跳出内层value循环,不再输出本行值,开始下一次外层entry循环
#! /bin/bash
IFS=$'\n' #外层循环使用\n分段,解析出各行
for entry in $(cat passwd)
do
echo "Values in $entry -"
IFS=: #内层循环使用:分段,解析行中各字段
for value in $entry
do
if [ -z $value ] #由于:之间可能存在空字符串,需要额外加个判断,否则执行==比较会报错
then echo " $value"
elif [ $value == 'oracle' ]
then
break #跳出内层循环
else
echo " $value"
fi
done
done
可以看到oracle那一行没有输出具体字段值
其实break还可以指定要跳出哪层循环,默认为1,即当前循环。指定方式为
break n
稍微改写一下前面的脚本,改为跳出外层(即break 2)循环
#! /bin/bash
IFS=$'\n'
for entry in $(cat passwd)
do
echo "Values in $entry -"
IFS=:
for value in $entry
do
if [ -z $value ]
then echo " $value"
elif [ $value == 'oracle' ]
then
break 2 #修改为跳出外层循环
else
echo " $value"
fi
done
done
可以看到只执行到输出Values in oracle那行,下面的字段输出及外层的循环都没有再执行了(若执行外层循环还会输出grid用户相关信息)
2. continue
默认结束本层本次循环,开始执行本层下一次循环。还是用刚才的例子:
#! /bin/bash
IFS=$'\n'
for entry in $(cat passwd)
do
echo "Values in $entry -"
IFS=:
for value in $entry
do
if [ -z $value ]
then echo " $value"
elif [ $value == 'oracle' ]
then
continue
else
echo " $value"
fi
done
done
可以看到没有输出oracle这个字段值,其余都正常输出了
其实continue也可以指定要继续执行哪层循环,默认为1,即当前循环。指定方式为
continue n
稍微改写一下前面的脚本,改为继续执行外层循环,即跳出本层循环(continue 2)
#! /bin/bash
IFS=$'\n'
for entry in $(cat passwd)
do
echo "Values in $entry -"
IFS=:
for value in $entry
do
if [ -z $value ]
then echo " $value"
elif [ $value == 'oracle' ]
then
continue 2
else
echo " $value"
fi
done
done
可以看到跟默认的break一个效果。
六、 循环结果处理
如果要将循环输出的结果输入文件,或者通过管道传给其他命令,应该在done后面操作。
#!/bin/bash
count=1
while [ $count -lt 5 ]
do
echo $count
count=$((count + 1)) # 也可以写成 count=$(expr $count + 1)
done > output.txt
#!/bin/bash
for file in /proc/*;
do
echo $file is file path \! ;
done | sort
参考