本节我们一起来看一下shell的编程,可能讲的不是非常全,希望大家可以一起讨论补充。

我将根据以下几个步骤对shell的编程语法进行讲解:

脚本简单介绍

变量

算术运算

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

例如

shell编程的语法知识点_shell 

$[EXPRESSION]

例如

shell编程的语法知识点_shell_02 

$((EXPRESSION))

例如

shell编程的语法知识点_编程_03 

命令:expr ARG1 OP ARG2

例如

shell编程的语法知识点_语法_04 

 

 交互式编程

格式: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

 

两个分支仅执行其中之一。

 

练习:通过命令行给定一个文件路径,而后判断:

如果此文件中存在空白行,则显示其空白行的总数;

否则,则显示无空白行;

shell编程的语法知识点_编程_05 

我们对/etc/fstab文件进行测试

shell编程的语法知识点_编程_06 

 

注意:如果把命令执行成功与否当作条件,则if语句后必须只跟命令本身,而不能引用。

 

(3)分支的if语句:

if 条件1; then

分支1

elif 条件2; then

分支2

elif 条件3; then

分支3

...

else

分支n

fi

练习:传:递一个用户名给脚本

如果此用户的id号为0,则显示说这是管理员

如果此用户的id号大于等于500,则显示说这是普通用户

否则,则说这是系统用户;

shell编程的语法知识点_编程_07

 

case语句

当有多个测试条件时,case语句会使得语法结构更明晰

用法格式:

case 变量引用 in

PATTERN1)

分支1

;;

PATTERN2)

分支2

;;

...

*)

分支n

;;

esac

 

PATTERN:类同于文件名通配机制,但支持使用|表示或者;

a|b: a或者b

*:匹配任意长度的任意字符

?: 匹配任意单个字符

[]: 指定范围内的任意单个字符

 

例如:用户键入字符后判断其所属的类别;

shell编程的语法知识点_shell_08

 

 

条件测试

条件测试根据比较时的操作数的类型可以分为以下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: file1mtime新于file2则为真,否则为假;

file1 -ot file2file1mtime旧于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

Untilwhile循环使用方法差不多,但是条件刚刚好相反

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

 

 以上就是本节所讲的内容,欢迎大家的补充及批评指正,谢谢!