本节我们一起来看一下shell的编程,可能讲的不是非常全,希望大家可以一起讨论补充。
我将根据以下几个步骤对shell的编程语法进行讲解:
u 脚本简单介绍
u 变量
u 算术运算
u 交互式编程
u 选择判断
u 条件测试
u 循环
u 函数
脚本简单介绍
在讲这些语法格式之前我们先对脚本进行简单的了解。
1、书写格式
第一行,顶格:以下为例
#!/bin/bash #!/usr/bin/python
上边2个是两种不同编程语言的开头,一个是bash,一个是python,使用哪种语言开头这里就用哪种语言。
其它的以#开头的行均为注释,会被解释器忽略。
2、执行与检测
Bash [option] file
-n: 语法测试
#bash -n ping.sh 即对ping.sh进行语法检测,如不报错,则语法真确
-x: 模拟单步执行
#bash -x ping.sh 可以显示该脚本执行中的每个步骤
变量
变量定义规则:
1、不能使用程序中的关键字(保留字)
2、变量名只能包含字母、数字、下划线,且不能以数字开始
3、最好做到见名知意
bash变量类别:
1、本地变量:对整个脚本进程或整个bash进程有效。
2、环境变量:当前shell进程及其子进程。
3、局部变量:只对当前的代码段有效。
4、位置变量:$n即为第n个位置变量 $1, ..., $n, ${10}
5、特殊变量:bash内置的变量
$?:上一个命令执行的返回值
$#: 传递给脚本参数的个数
$*
$@:引用传递给脚本的所有参数
变量的赋值
格式:变量名=值
VARNAME=VALUE
变量引用:${VAR_NAME}
双引号"": 弱引用,里面的变量会被替换;
单引号'' : 强引用,里面的所有字符都是字面量,直接输出;
变量的撤销:unset VAR_NAME
算术运算
算术运算:bash会对数字执行隐式的类型转换
let VAR_NAME=Integer_Value
declare -i Var_Name=Integer_Value
注意:即使没有定义为整型变量,字符型的数字依然可以参与算术运算;bash会执行变量类型的隐式类型转换;
运算操作符:+, -, *, /, %(取模), **(次方)
bash的算术运算的方式:
let Var_Name=EXPRESSION
例如
$[EXPRESSION]
例如
$((EXPRESSION))
例如
命令:expr ARG1 OP ARG2
例如
交互式编程
格式:read option “交互时的提示信息” 参数(交互时输入的值)
option:
-p :直接指定一个变量接受参数
-t :timeout ,指定等待接受参数的时间
下面我们来一个简单示例:创建一个脚本
#!/bin/bash # read -p "Please input a user :" us echo $us
执行该脚本:会出现交互,我输入了一个hello.
[root@mwj87v5 scripts]# bash test5.sh Please input a user :hello hello
选择判断
if语句
(1)单分支的if语句:
if 测试条件; then
选择分支
fi
表示条件测试状态返回值为值,则执行选择分支;
(2)双分支的if语句:
if 测试条件; then
选择分支1
else
选择分支2
fi
两个分支仅执行其中之一。
练习:通过命令行给定一个文件路径,而后判断:
如果此文件中存在空白行,则显示其空白行的总数;
否则,则显示无空白行;
我们对/etc/fstab文件进行测试
注意:如果把命令执行成功与否当作条件,则if语句后必须只跟命令本身,而不能引用。
(3)分支的if语句:
if 条件1; then
分支1
elif 条件2; then
分支2
elif 条件3; then
分支3
...
else
分支n
fi
练习:传:递一个用户名给脚本
如果此用户的id号为0,则显示说这是管理员
如果此用户的id号大于等于500,则显示说这是普通用户
否则,则说这是系统用户;
case语句
当有多个测试条件时,case语句会使得语法结构更明晰
用法格式:
case 变量引用 in
PATTERN1)
分支1
;;
PATTERN2)
分支2
;;
...
*)
分支n
;;
esac
PATTERN:类同于文件名通配机制,但支持使用|表示或者;
a|b: a或者b
*:匹配任意长度的任意字符
?: 匹配任意单个字符
[]: 指定范围内的任意单个字符
例如:用户键入字符后判断其所属的类别;
条件测试
条件测试根据比较时的操作数的类型可以分为以下3类:
整型测试:整数比较
字符测试:字符串比较
文件测试:判断文件的存在性及属性
整型测试
格式:[ $num1 比较符 $num2 ]
比较符
-gt | 大于 |
-lt | 小于 |
-ge | 大于等于 |
-le | 小于等于 |
-eq | 等于 |
-ne | 不等于 |
字符串测试
格式:[[ "$str1" 比较符 "$str2" ]]
比较符:
双目
>
<
>=
<=
==
!=
单目:
-n String: 是否不空,不空则为真,空则为假
-z String: 是否为空,空则为真,不空则假
字串测试中的模式匹配 [[ "$var" =~ PATTERN ]]
文件测试
格式:[选项 文件名]
选项:
-a FILE
-e FILE: 存在则为真;否则则为假;
-f FILE: 存在并且为普通文件,则为真;否则为假;
-d FILE: 存在并且为目录文件,则为真;否则为假;
-L/-h FILE: 存在并且为符号链接文件,则为真;否则为假;
-b FILE: 存在并且为块设备文件,则为真;否则为假;
-c:FILE:存在并且为字符设备文件,则为真;否则为假
-SFILE:存在并且为套接字文件,则为真;否则为假
-p:FILE:存在并且为命名管道文件,则为真;否则为假
-s FILE: 存在并且为非空文件则为值,否则为假;
-r FILE:存在并且有读的权限则为值,否则为假;
-w FILE:存在并且有写的权限则为值,否则为假;
-x FILE:存在并且有执行的权限则为值,否则为假;
file1 -nt file2: file1的mtime新于file2则为真,否则为假;
file1 -ot file2:file1的mtime旧于file2则为真,否则为假;
组合条件测试
条件测试还可以组合在一起进行测试
在多个条件间实现逻辑运算
与:[ condition1 -a condition2 ]
condition1 && condition2
或:[ condition1 -o condition2 ]
condition1 || condition2
非:[ -not condition ]
! Condition
讲完了这么多条件测试,下面我们进行一些练习进行巩固。
练习1
如果wget命令对应的可执行文件存在且可执行,则使用它下载http://172.16.0.1/centos6.5.repo至当前目录中;
#!/bin/bash # downURL='http://172.16.0.1/centos6.5.repo' downloader=`which wget` if [ -x $downloader ]; then $downloader $downURL fi
练习2
给定一个文件路径
判断此文件是否存在;不存在,则说明文件不存,并直接结束脚本;
如果文件是否普通文件,则显示为“regular file”;
如果文件是目录,则显示为“directory”;
如果文件是链接文件,则显示为“Symbolic file";
否则,则显示为“unknown type.”
#!/bin/bash # if [ ! -e $1 ]; then echo "file not exist." exit 8 fi if [ -L $1 ]; then echo "Symbolic file" elif [ -d $1 ]; then echo "Directory" elif [ -f $1 ]; then echo "regular file." else echo "unknown." fi
循环
For:
For循环只是遍历LIST元素,遍历结束,循环退出。
格式:
第一种格式
for Var in LIST; do
循环体
Done
生成列表的方式:
1、手动给个列表:
for i in 1 2 3 4 5;
2、数值列表:
{start..end}
`seq [start [increment]] end` 例如: for i in `seq 1 100`;do
3、$*, $@
4、命令生成列表
例题:创建10个用户user
#!/bin/bash # for i in {1..10};do useradd user$i done
第二种格式
for ((初始条件;测试条件;修改表达式)); do
循环体
Done
例题:
求100以内所有正整数之和:
#!/bin/bash # declare -i sum=0 for ((counter=1;$counter <= 100; counter++)); do let sum+=$counter done
While:
while适用于循环次数未知,或不便用for直接生成较大的列表时,需要注意是退出循环必须要有退出条件。
格式:
while 测试条件; do
循环体
done
如测试结果为“真”,则进入循环;退出条件为,测试条件为假;
求100以内所有正整数之和
#!/bin/bash # declare -i count=1 declare -i sum=0 while [ $count -le 100 ]; do let sum+=$count let count++ done echo $sum
Until:
Until和while循环使用方法差不多,但是条件刚刚好相反
until 测试条件; do
循环体
done
如果测试结果为“假”,则进入循环;退出条件为,测试条件为真;
练习:求100以内所有正整数之和
#!/bin/bash # declare -i count=1 declare -i sum=0 until [ $count -gt 100 ]; do let sum+=$count let count++ done echo $sum
循环控制及信号捕捉
信号捕捉:trap 'COMMAND;COMMAND' signal
例如:
trap
'quitScript; exit 5'
SIGINT
将此段代码放在函数中,只要按下ctrl+c,即使脚步在运行循环语句中也能直接退出脚本,而不是退出其中的一次循环。
循环控制:
continue: 提前进入下一轮循环
用于条件语句中,仅在某些个特殊场景提前进入;
break [n]:跳出当前循环
用于条件语句中
例如:查看用户登录
#!/bin/bash # read -p "Plz enter a username: " userName while true; do if who | grep "\<$userName\>" &> /dev/null; then break fi echo "not here." sleep 5 done echo "$userName is logged on."
函数
函数可以使脚本编程变的模块化,只要是函数名出现的地方就相当于调用了整个函数。
1、语法:
[function] 函数名()
{
语句
[return]
}
其中这里的function和return都是可省的,function表示定义一个函数,return则自定义函数的返回值
2、函数的返回值:
函数的返回值可以自己定义,也可以省略,一旦省略函数的返回值将变成函数中最后调用的一个系统命令执行后返回的结果。想要自定函数执行状态的返回值可以使用return #,#表示返回值。
3、函数可以接受参数:
在函数体中调用函数参数的方式同脚本中调用脚本参数的方式:位置参数
$1, $2, ...
$#, $*, $@
练习:
使用函数写一个脚本。
使用格式:
script.sh {start|stop|restart|status}
1) start: 创建/var/lock/subsys/script.sh
2) stop: 删除此文件
3) restart: 先删除文件,再创建文件
4) status: 如文件存在,显示running,否则,显示stopped
#!/bin/bash # # chkconfig: 2345 67 34 # srvName=$(basename $0) lockFile=/var/lock/subsys/$srvName start() { if [ -f $lockFile ];then echo "$srvName is already running." return 1 else touch $lockFile [ $? -eq 0 ] && echo "Starting $srvName OK." return 0 fi } stop() { if [ -f $lockFile ];then rm -f $lockFile &> /dev/null [ $? -eq 0 ] && echo "Stop $srvName OK" && return 0 else echo "$srvName is not started." return 1 fi } status() { if [ -f $lockFile ]; then echo "$srvName is running." else echo "$srvName is stopped." fi return 0 } usage() { echo "Usage: $srvName {start|stop|restart|status}" return 0 } case $1 in start) start ;; stop) stop ;; restart) stop start ;; status) status ;; *) usage exit 1 ;; esac
以上就是本节所讲的内容,欢迎大家的补充及批评指正,谢谢!