循环loops,bash中三大循环【for-do-done】,【until-do-done】,【while-do-done】
1.【for-do-done】
for i in 1 2 3;do echo $i;done
> 1
> 2
> 3
for i in "1 2 3";do echo $i;done
> 1 2 3
a="1 2 3";echo $a
> 1 2 3
for i in $a;do echo $i;done ##带分隔符的变量和数组是for循环元素形式的一大类
> 1
> 2
> 3
for i in "$a";do echo $i;done
> 1 2 3
##空格是for循环的元素间隔符,要想把有空格的字符串当作元素,需要加【""】
##脚本中当list列表元素不设置的时候,会遍列【$@】【$*】即全部脚本参数
vim for_test.sh
#!/bin/bash
for i;do echo -n "$i ";done
./for_test.sh
>
./for_test.sh x y z 1 2 3
> x y z 1 2 3
##函数生成list
vim generate_list.sh
#!/bin/bash
generate_list() {
echo "elementA elementB elementC" ##函数中可以设置一些复杂的list元素输出过程
}
for i in $(generate_list)
do
echo $i
done
##搜索指定目录下的软连接
vim symlinks.sh
#!/bin/bash
if [ -z "$1" ];then
echo "you do not appoint the directory for searching symlinks,
the ${0#*\/} usage: ${0#*\/} serach_directory.s
now, the outout will show you the symlinks in current directory."
fi
for i in $(find ${1-`pwd`} -type 1 -name "*")
do
echo $i
done | sort | head -n5 ##for循环的输出可以作为stdin输入管道进行处理
> you do not appoint the directory for searching symlinks,
> the root/symlinks.sh usage: root/symlinks.sh serach_directory.
> now, the outout will show you the symlinks in current directory.
> /usr/lib/cpp
> /usr/lib/debug/bin
> /usr/lib/debug/lib
> /usr/lib/debug/lib64
> /usr/lib/debug/sbin
除了上述常用的结构,还有一些特殊的for循环结构可以省略in、do和done关键字,这里不作介绍了。
2.【while-do-done】
while循环可以和for循环一样通过结合【read】进行遍历,但是还是有很大的区别,从英文的字面看while应该是条件语句,是一个条件循环,在满足一定条件时候进行循环,不满足的时候退出,因此当设置的条件永远为true(【:】可以代替true出现在语句中),会出现无限循环。
var=0;while [ $var -le 10 ];do let var+=1;done;echo $var > 11 while [ "${var-start}" = "end" ];do read -p "please input: " var;done please input: hello please input: end ##while循环也支持函数,while condition,这个condition其实是一个布尔值,while true或者是while 0 vim while_function.sh #!/bin/bash condition() { let i+=1 if [ ${i-0} -lt 3 ];then return 0 else return 1 fi } while condition do echo "value = $i" done > value = 1 > value = 2
下面介绍一下几种常见给while read传输数据进行遍历的形式
vim file.log 08:30:00 open 09:10:55 close 09:20:30 vim log.sh #!/bin/bash cat file.log | while read Time Operation do echo $Time ${Operation:-NULL} done OLDIFS=$IFS IFS=$'\n' ##以行结尾作为分隔符 while read Time Operation do echo $Time ${Operation:-NULL} done < file.log IFS=$OLDIFS > 08:30:00 open > 09:10:55 close > 09:20:30 NULL > 08:30:00 open NULL > 09:10:55 close NULL > 09:20:30 NULL
3.【until-do-done】
until这边就不做介绍了,没有类似while read的遍历操作,只有条件判断循环,while是不满足条件时退出循环,until是满足条件时退出循环。
4.嵌套循环
两种元素的组合遍历操作,循环中嵌套循环,没有什么太过高深的内容,举个例子,在数据库中提取过去一个月每天0点,8点和16点的数据,输出报表,sql语句和输出报表的工具比如说gnuplot这些我就不说了,应该写两个for循环,一个是for Date in 2021-03-01 ......,一个是for Time in 00:00:00 08:00:00 ....
vim multiplication_table.sh #!/bin/bash for i in `seq 1 9` do for j in `seq 1 9` do echo "$i * $j = $((i*j))" done done > 1 * 1 = 1 > 1 * 2 = 2 > 1 * 3 = 3
5.循环控制
break和continue,break跳出循环,continue略过本次循环继续下一次循环,这里的描述和while循环的功能相当,是这样的,如果只有一个循环条件的时候while可以直接实现,可是当有多个循环条件且可能互相之间没有逻辑关系的时候,就需要用到循环+判断+控制【for-do-done】【if-then-else-fi】【break】【continue】。
##输出一组数据中的偶数 vim continue.sh #/bin/bash for i in 1 3 19 22 33 46 do if [ `expr $i % 2` -eq 0 ];then echo $i else continue fi done > 22 > 46 ##筛选字符串遇到eixt退出并输出行号 vim string.txt done do exit fail vim break.sh #!/bin/bash while read string do let num+=1 if [ ${string:-done} != 'exit' ];then continue else echo "string \"exit\" line num is $num" fi done < string.txt
上述的案列都是单重循环,当有多层循环的时候可以用continue N和break N来继续活着跳出上N-1层的循环
for x in 1 2 3 do for y in 1 2 3 do for z in 1 2 3 do case $x in 1|2) break 2 ;; 3) case $y in 1|2) continue ;; 3) echo "x:$x y:$y z:$z" esac esac done done done > x:3 y:3 z:1 > x:3 y:3 z:2 > x:3 y:3 z:3
6.测试与分支
上面的案例出现过了case的语句,case语句不属于循环结构,case并没有循环重复执行代码,但是在根据代码顶部或者尾部的条件控制程序的功能上和循环结构一样。
vim case_test.sh #!/bin/bash read -p "please input a character: " keyword case $keyword in [[:lower:]]) echo "Lowercase letter" ;; [[:lower:]]) echo "Uppercase letter" ;; [0-9]) echo "Digit" ;; *) echo "Punctution, whitespace, or other" ;; esac > please input a character: o > Lowercase letter
case在一些软件的bash执行文件中比较常见,可以用来检测命令参数
#!/bin/bash while [ $# -gt 0 ] do case $1 in -d|--debug) DEBUG=1 echo "DEBUG value is $DEBUG" ;; -C|--conf) CONFFILE="$2" shift if [ !-f $CONFFILE ];then echo "ERROR: The conf file does not exist!" exit 1 fi ;; esac shift ##现在发现了shift的好处了,在涉及多个命令参数的时候,shift可以让每个参数都是$1 done ./shift.sh -d -c case > DEBUG value is 1 > ERROR: The conf file does not exist!
测试分支中有个比较重要的语句select,我们经常在执行软件安装脚本的时候遇到屏幕输出选项,让选择,这种选项的输出就可以用select完成。
vim select_vegetable.sh #!/bin/bash echo "which vegetable is your favorite?" select vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas" do case $vegetable in "beans"|"carrots"|"potatoes"|"onions"|"rutabagas") echo "your favorite vegetable is \"$vegetable\"" break ;; *) echo "please select the vegetable in the list!" ;; esac done ./select_vegetable.sh > which vegetable is your favorite? > 1) beans > 2) carrots > 3) potatoes > 4) onions > 5) rutabagas > #? 2 > your favorite vegetable is "carrots" ./select_vegetable.sh > which vegetable is your favorite? > 1) beans > 2) carrots > 3) potatoes > 4) onions > 5) rutabagas > #? orange > please select the vegetable in the list! > #? 2 > your favorite vegetable is "carrots"
##select可以和函数结合使用,方法和之前的很多结合函数的类似,结合$@和$*
vim choice_vegetable.sh
#!/bin/bash
choice_vegetable() {
select vegetable
do
if [[ "${@/$vegetable/}" != "${@}" ]];then ##这里是判断某个元素是否属于数组的巧妙方法!!!
echo "your favorite vegetable is \"$vegetable\""
break
else
echo "please select the vegetable in the list!"
fi
done
}
choice_vegetable beans rice carrorts radishes rutabaga spinach
./choice_vegetable.sh
> 1) beans
> 2) rice
> 3) carrorts
> 4) radishes
> 5) rutabaga
> 6) spinach
> #? ric
> please select the vegetable in the list!
> #? 2
> your favorite vegetable is "rice"
最后一个脚本算是半原创吧,感觉用起来还算巧妙!
到这里关于循环loops基本也差不多了。
这篇文章个人觉得还不错!ヾ(≧∇≦*)ゝ