十二 Shell脚本-结构化命令
对脚本中的命令施以逻辑流程控制,这类命令会根据条件是脚本跳过某些命令,这样的命令就叫做结构化命令;
1、if-then语句
语法:
if 命令
then
命令
fi
执行逻辑为:执行if后的命令,该命令退出状态码为0(成功执行),然后执行then后的命令,否则不执行执行then后的命令,继续执行脚本中的下一条命令;
then部分可以使用不止一条命令,可以有多条命令构成一个像java中的语句块;
2、 if-then-else
语法:
if 命令
then
命令
else
命令
fi
当if命令退出状态码为0,执行then命令,否则执行else命令;
3、嵌套if
。。。
if-then只能测试命令退出码状态这个条件;
test命令
语法格式:
test 条件
test中条件成立,test命令就会退出并且返回退出状态码0;条件不成立,test命令就会退出并且返回非0的退出状态码;如果省略条件,test命令就会退出,并且返回非0的退出状态码;
bash shell还提供了另外一种条件测试方法,无需在if-then中使用test命令;
if [条件]
then
命令
fi
注意:第一个方括号之后和第二个方括号之前必须加上一个空格;
test命令测试三类条件:
-
数值比较
-
字符串比较
当在做字符串比较时,大于、小于功能时,会出现两个问题:
-
大于、小于号需要转义,否则shell会把他们当做重定向符号,把字符串当做文件名
-
大于、小于顺序和sort命令采用的不同(区分大小写,大写字母被认为是小于小写字母,因为使用的是标砖ASII码顺序排序,而sort命令使用的是系统本地化语言设置中定义的排序顺序,本地化设置指定了排序顺序中小写字母出现大写字母之前)
!/bin/bash test=haha test2=leslie if [ $test < $test2 ] then echo "$test1 is greater than $test2" else echo "$test1 is less than $test2" fi
会提示:
[root@Linleslie 桌面]# ./test4
./test4: line 6: leslie: 没有那个文件或目录
这儿会把<当做当做输入重定向符号,将leslie这个文件的内容如果到左边命令,所有会提示leslie这个文件不存在;
所以这儿正确的写法是对<符号转义,在其前面添加一个\转义;
- 文件比较
-e:可用于比较文件和目录。要确定指定对象为文件,必须使用-f
-nt、-ot比较:不会检查File是否存在,所以在用是必须确定file存在,否则将会得出一个错误的答案;
如:
#!/bin/bash
directory=/home/arthur
if [ -d $directory ]
then
echo "the $directory directory exists"
cd $directory
ls
else
echo "the $directory does not exist "
fi
符号条件测试
使用布尔逻辑来组合测试;有两种布尔运算符可用:
[条件1]&&[条件2]
[条件1]||[条件2]
if-then高级特性
1. 用于数学表达式的双括号
允许你在比较过程中使用高级数学表达式;
注意:不需要将双括号中的大于号转义;
如:
var1=10
if(($var1**2>90))
.....
2、双方括号[[ ]]
提供了字符串比较的高级的特性;较test命令提供了***匹配模式***这个新特新
case命令
类似于java中的switch语句
语法格式:
case 变量 in
pattern1 |pattern2) 命令1;
pattern3) 命令2;
....
*) 命令N;
esac
for命令
语法格式:
for 变量 in list
do
任意条bash shell命令
done
list:为迭代提供一些列值;
指定列表中的值的几种方式:
方式一: 直接在in后列出多有列表值,以空格隔开
#!/bin/bash
for name in lin leslie lu lucas
do
echo "the name is $name"
done
echo "the last name is $name"
name=cherry
echo "the name is $name"
运行结果:
[root@Linleslie codetest]# ./test2
the name is lin
the name is leslie
the name is lu
the name is lucas
the last name is lucas
the name is cherry
[root@Linleslie codetest]#
这里name变量在迭代结束后会一直在shell中保持有效,值为最后一次迭代的值;当然我们也可以对他进行重新赋值;
注意:
- *** 当列表值中有单引号,必须转义,否者出现错误(比如通过\或者 通过双引号将其列表值括起来)***;
- 我们说了for命令默认是用空格分隔列表值的,但是如果某个列表值本来就存在空格,显然也会出现错误,这时就必须使用双引号将这个值括起来如:“ha lin”
只要你愿意,也可以将for do放在有一行(使用分号将其同列表中的值隔开):
for 变量 in list;do
***方式二:***将所有列表值存储在一个变量中,然后直接遍历这个变量;
列表值变量="值1 值2 值3..."
for 变量 in $列表值变量
除此之外,当将所有列表值存储在变量后,如果后面我们还想继续添加值,可以通过下面方式添加:
list=$列表值变量" 值"
***方式三:***从命令中读取,通过命令替换符来执行任何能产生输出的命令
file="test"
for 变量 in $(cat $file)
***注意:***这里的文件名没有加入路径,默认该文件和脚本文件在相同目录中,否者需要使用全路径;
更改字段分隔符
造成上面我们说的值中存在空格时,必须使用双引号将这个值括起来的的罪魁祸首就是IFS(internal field separactor)这个环境变量,叫做内部字段分隔符;
默认情况下,bash shell会将下列字符当做分隔符:
- 空格
- 制表符
- 换行符
当然在shell脚本中我们可以,临时更改IFS环境变量的值来作为字段分隔符;
如:使用换行符作为字段分隔符
IFS=$'\n'
如果要定义多个分隔符(直接挨着写):
IFS=$'\n':;
假设在处理代码量较大的脚本是,可能只需要某一处需要修改IFS值,然后其他地方还是使用之前的IFS值,可以这样实现:
IFS.oLD=$IFS;
ISF=新值
《使用新值的代码》
ISF=$IFS.OLD
while命令
基本语法格式:
while test 命令
do
命令
done
只要定义的测试命令退出状态码为0,就会循环;测试命令和if-then一样,可以是是普通的bash shell命令,或者用test命令进行条件测试;
除此之外,测试命令还可以是多个测试命令一起,只要最后一个测试命令的退出状态码不为0,循环结束;每个测试命令都出现在单独的一行;
如:
#!/bin/bash
var1=10
while echo $var1
[ $var1 -gt 0 ]
do
echo "this is inside loop"
var1=$[$var1-1]
done
嵌套循环
和java中for嵌for,while嵌for一样;
退出循环
- break:退出离break最近单个循环结构;
如有多层嵌套循环要跳出指定外层循环,break接受单个命令行参数值:
break n
默认情况n为1:表示跳出最近的循环,n为2表示跳出离break最近的下一外层循环;依次类推…
- continue:中止某次循环的命令,继续执行下一次循环;
1、对循环输出使用重定向、管道
通过在done命令后添加一个处理命令如:>、|来实现:
for value in Linux Android IOS
do
echo "$value"
done >leslie.txt
2、创建多个用户
前面我们说了通过指令创建用户,显然那种方式太过于笨拙;我们可以通过一个脚本文件,瞬间创建N个用户;
在写脚本之前我们需要了解一种文件格式(.csv):
userid,user_name
第一个条目是新用户账号所选的用户id,第二个条目是用户全名,两个值之间用逗号隔开,这样就形成了一种名为逗号分隔值的文件格式(或者是.csv).以备shell脚本读取;
#!/bin/bash
input="namepasswd.cvs"
while IFS=','
read -r userid username
do
useradd -c "$username" -m $userid
done < $input
种方式太过于笨拙;我们可以通过一个脚本文件,瞬间创建N个用户;
在写脚本之前我们需要了解一种文件格式(.csv):
userid,user_name
第一个条目是新用户账号所选的用户id,第二个条目是用户全名,两个值之间用逗号隔开,这样就形成了一种名为逗号分隔值的文件格式(或者是.csv).以备shell脚本读取;
#!/bin/bash
input="namepasswd.cvs"
while IFS=','
read -r userid username
do
useradd -c "$username" -m $userid
done < $input