第十二章 学习Shell Scripts

1.了解shell scripts

1.1 为什么要学习shell script?

自动化管理

我们管理主机不是一件简单的事情:查询登录档,追踪流量,监控用户使用主机状态,主机各项硬设状态,主机软件更新查询等等。这些工作其实可以用shell scripts自动完成。

追踪与管理系统的重要工作

我们开机自启的接口是在/etc/init.d目录下,目录下所有文件都是scripts,开机过程、参数设定也是。

简单入侵检测

当系统有异状时,一般这些异状都记录在"系统注册表"里,我们可以在固定的时间段内主动去分析"系统注册表"。

连续指令一体化

script最简单的功能就是汇集一些下达的连续指令,将它写入script中。

1.5 简单的数据处理

就如前一章正规表示法的awk程序说明,可以发现awk用来处理数据。撰写方便,速度又快。

1.6 跨平台支持

几乎所有的Unix Like上面都可以跑shell scripts的。

注意:shell scripts用的是外部的指令与bash shell的一些默认工具,经常去调用外部的函数库,所以处理数据的速度上是不太够的。shell scripts用在系统管理上面是很好用的一项工具,但是在处理大量数值运算上就不好用了。

1.2 scripts的撰写

shell scripts的注意事项:

  • 1.指令的执行是从上而下、从左到右的分析与执行;

  • 2.指令、选项与参数间的多个空白都会被忽略掉;

  • 3.空白行也会被忽略掉,并且tab键的空白也同样视为空格键;

  • 4.如果读取到一个"Enter"符号(CR),就尝试开始执行该行或者该串命令;

  • 5.如果一行的内容太多,可以使用"\Enter"来延伸到下一行;

  • 6."#" 可以作为批注;

假设写的程序文件名是shell.sh,那么如何执行这个文件?

  • 直接下达指令:shell.sh文件必须要具备可读与可执行的权限;

  • 以bash程序来执行:通过"bash shell.sh"或"sh shell.sh"执行;

shell scripts 的编写

[~]$ vim hello.sh
#!/bin/bash
# Program:
# This program shows hello world in your screen
# History:
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e 'hello world! \a \n'
exit 0

解释:

第一行: #!/bin/bash在声明scripts使用的shll名称;
因为我们使用的是bash,所以必须要以"#!/bin/bash"来声明文件内的语法使用bash的语法。

程序内容说明:
建议一定要养成说明的习惯:1.标注内容与功能;2.版本信息;3.作者与联络方式;4.建档日期等等。

主要环境变量的声明。

主要程序部分

返回执行结果:
一个指令的执行成功与否,可以使用$?这个变量来观察。那么我们也可以利用"exit"指令让程序中断,并且回传一个数值给系统。"exit 0"表示离开script并回传一个0给系统,所以在执行完script后,可以使用"echo $?"来得到0的值。

2.简单的shell scripts练习

2.1 简单示例

对谈式脚步:变量内容由用户决定

[~]$ vim showname.sh
#!/bin/bash
# Program:
#   User inputs his first name and last name. Program shows his full name.

read -p "Please input your first name: " firstname
read -p "Please input your last name: " lastname
echo -e "\nYour full name is: " ${firstname} ${lastname}

随日期变化,利用date进行文件的建立

[~]$ vim create_3_file.sh
#!/bin/bash
# Program:
#       Program creates three files,which named by user's input and date command.

# 1.让使用者输入文件名,并取得fileuser这个变量
echo -e "I will use 'touch' command to create three files."
read -p "Please input your filename: " fileuser

# 2.为了避免使用者随意按enter,利用变量功能分析档名是否有设定
filename=${fileuser:-"filename"}

# 3.开始利用date指令来获取所需要的档名:
date1=${date --date='2 days ago' +%Y%m%d}
date2=${date --date='1 days ago' +%Y%m%d}
date3=${date +%Y%m%d}

file1=${filename}${date1}
file2=${filename}${date2}
file3=${filename}${date3}

# 4.建立档名
touch "${file1} ${file2} ${file3}"

数值运算:简单的加减乘除

[~]$ vim multiplying.sh
#!/bin/bash
# Program:
#    User inputs 2 integer numbers:program will cross these two numbers.

echo -e "You SHOULD input 2 numbers,i will multiplying them! \n"
read -p "first number:" firstnum
read -p "second numbser:" secondnum
total=$((${firstnum}*${secondnum}))
echo -e "\n The result of ${firstnum} x ${secondnum} is ==> ${total}"

在数值运算上,可以使用"declare -i total={secondnum}"实现,也可以使用上面的方式进行:var=$((运算内容))。

数值运算:通过bc计算pi

[~]$ vim cal_pi.sh
#!/bin/bash
# Program:
#       User input a scale number to calculate pi number
echo -e "This program will calculate pi value.\n"
echo -e "You should input a float number to calculate pi value.\n"
read -p "The scale number(10-10000)?" checking
num=${checking:-"10"}  # 开始判断是否有输入数值
echo -e "Starting calculate pi value.Be patient."
time echo "scale=${num};4*a(1)" | bc -lq

上面部分4*a(1)是bc提供的计算pi的函数,scale就是表示bc计算的pi有几位小数点的意思。

2.2script的执行方式差异(source,sh script,./script)

利用直接执行的方式来执行script

我们前面使用bash(sh)执行脚步时,script是在子程序的bash内执行的。重点是"当子程序完成后,在子程序内的各项变量或动作将会结束而不会传回到父程序中"。

例如上面的showname.sh,设定好的变量在退出showname.sh后,使用"echo ${firstname}"是无效的。因此你的firstname变量其实是在子程序的bash内执行的。当showname.sh执行完后,子程序bash内的所有数据会被移除。

第十二章 学习Shell Scripts_第1张图片
子程序运作示意图

利用source执行脚本:在父程序中执行

使用source执行的化,在退出showname.sh后,使用"echo ${firstname}"还是会得到数据的!

3.判断式的使用

3.1 利用test指令的测试功能

看一个示例:

[~]$ test -e showname.sh
## 什么都没显示,但是可以通过$?、&&、||来展现效果!
[~]$ test -e showname.sh && echo "exist" || echo "not exist"

功能简介:

1.关于某个档名的"文件类型"判断:

-e  :该"档名"是否存在?(常用)
-f   :该"档名"是否存在切为文件(file)?(常用)
-d  :该"文件名"是否存在且为目录(directory)?(常用)
-b  :该"档名"是否存在且为一个block device装置?
-c  :该"档名"是否存在且为一个character device装置?
-S  :该"档名"是否存在且为一个socket文件?
-p  :该"档名"是否存在且为一个FIFO文件?
-L  :该"档名"是否存在且为一个连结档?

2.关于文件的权限检测,如"test -r filename"表示是否可读。

-r  :检测该"档名"是否存在且具有可读权限?
-w  :检测该"档名"是否存在且具有可写权限?
-x  :检测该"档名"是否存在且具有可执行权限?
-u  :检测该"档名"是否存在且具有SUID权限?
-g  :检测该"档名"是否存在且具有SGID权限?
-k  :检测该"档名"是否存在且具有Sticky Bit权限?
-s  :检测该"档名"是否存在且为非空白文件?

3.两个文件之间的比较,如test file1 -nt file2

-nt  :(newer than)判断file1是否比file2新;
-ot  :(older than)判断file1是否比file2旧;
-ef  :判断file1和file2是否为同一个文件。主要是判断是否均指向同一个inode;

4.关于两个整数之间的判定,例如test n1 -eq n2

-eq  :两数值是否相等;
-ne  :两数值是否不想等;
-gt   :n1大于n2?
-lt    :n1小于n2?
-ge  :n1大于等于n2?
-le   :n1小于等于n2?

5.判断字符串的数据

test -z str  :判断字符串是否为空字符串;
test -n str  :判断字符串是否为非空字符串;
test str1 == str2  :判断str1是否等于str2;
test str1  != str2  :判断str1是否不等于str2;

6.多重条件判定,例如:test -r filename -a -x filename

-a  :相当于&&,file同时具有r和x权限时为true;
-o  :相当于||,file具有r或x权限时为true;
!    :反向,如test ! -x file,表示当file不具有x权限是为true;

3.2 判断符号[]的使用

假设我们想判断一个${HOME}是否为空可以这样做:

[~]$ [ -z "${HOME}" ] ; echo $?

需要注意的地方;

  • 1.在中括号[]内的每个部分都需要有空格键来分隔;

  • 2.在中括号内的变量,最好都以双引号括起来;

  • 3.在中括号内的常量,最好都以单引号括起来;

3.3 Shell script的默认变量(1.....)

在我们执行"/etc/inti.d/network restart"时,就可以重启网络服务了。那么script是怎样获取我们指定的命令呢?

其实,script针对参数已经有设定好的一些变量名称了,对应如下:

/path        opt1        opt2        opt3    
  $0           $1            $2           $3
  • $# :表示后面接参数的"个数",以上面为例值为"3"。

  • $@ :表示"$1" "$2" "$3",每个变量是独立的;

  • $* :表示"$1[c]$2[c]$3",其中[c]为分隔符,默认为空格键;

shift:参数变量号码偏移

#!/bin/bash
...
shift n ##n为数字
...

执行脚本中写入shift n后,表示拿掉最前面的n个数。

4.条件判断

4.1 利用if...then

单层、简单条件判断

if [ 条件判断 ] ; then
        ...................
fi

多重、复杂条件判断

if [ 条件判断 ] ; then
        .......................
else
        .......................
fi

或者

if [ 条件一 ] ; then
        .......
elif [ 条件二 ] ;then
        .......
else
        .......
fi 

下面练习一个示例:假设我们要检测端口号为:22、21、25、80的是否开启。

[~]$ vim netstat.sh
#!/bin/bash
#Program:
#       Using netstat and grep to detect WWW,SSH,FTP,MAIL services.

testfile=netstat_check.txt
netstat -tuln > ${testfile}

str=$(grep ':80' ${testfile})
if [ "${str}"!="" ] ; then
    echo "WWW is running"
fi

str=$(grep ':21' ${testfile})
if [ "${str}"!="" ] ; then
    echo "FTP is running"
fi

str=$(grep ':22' ${testfile})
if [ "${str}"!="" ] ; then
    echo "SSH is running"
fi

str=$(grep ':25' ${testfile})
if [ "${str}"!="" ] ; then
    echo "Mail is running"
fi

4.2 case...esac的使用

case    $变量名称    in 
    "第一个变量内容")
            程序段
            ;;
    "第二个变量内容")
            程序段
            ;;
   *)
            不包含前面条件所执行的程序段
            exit   1
            ;;
esac

接下来看一个示例:

[~]$ vim hello-2.sh
#!/bin/bash
# Program:
#   Show "hello" from $1

case ${1} in
    "hello")
        echo '你好!'
        ;;
    "")
        echo '请输入字符!'
        ;;
    *)
        echo  ${0} '{的参数必须是hello}'
        exit 1
        ;;
esac

4.3利用function功能

语法:

fuction fname(){
        代码段.....
}

需要注意的是:Shell Script的执行方式是由上而下,从左到右,所以,function的设定一定要在程序的最前面。

看下面一个示例:

[~]$ vim show123.sh
#!/bin/bash
# Program:
#   Use function to repeat information.

function printit(){
    echo -n "Your choice is" # 加上 -n 可以不断行继续在同一行显示
}

echo 'This program will print your selecton.'

case ${1} in
    "one")
        printit; echo ${1} | tr 'a-z' 'A-Z' # 将参数做大小写转换
        ;;
    "two")
        printit; echo ${1} | tr 'a-z' 'A-Z'
        ;;
    "three")
        printit; echo ${1} | tr 'a-z' 'A-Z'
        ;;
*)
        echo "Usage" ${0} "{one|two|three}"
        ;;
esac

另外,function也拥有内建的变量,和shell script类似。函数名称代表1,$2.....,看看下面一个例子:

[~]$  vim show123-2.sh
#/bin/bash
# Program:
#   Use funciton to repeat information.

function printit(){
    echo "Your choice is ${1}" #这个${1}必须参考下面指令的
}

echo "This program will print your selection."

case ${1} in
    "one")
        printit 1   #printit后面接参数!
        ;;
    "two")
        printit 2
        ;;
    "three")
        printit 3
        ;;
*)
    echo "Usage "${0}"one|two|three"
    ;;
esac

5.循环(loop)

5.1 while do done,until do done(不定循环)

一般来说,不定循环就是以下两种语法:

while [ condition ]  ## 括号内是条件,达到条件继续循环
do 
        程序段落
done

或者

until [ condition ] ## 括号内是条件,达到条件时终止循环
do 
        程序段落
done

假设我们要让使用者输入yes/YES才结束执行,否则就一致进行告知用户输入字符串。

[~]$ vim yes_to_stop.sh
#!/bin/bash
# Program:
#   Repeat question until user input correct answer.

while [ "${str}" != "yes" ] && [ "${str}" != "YES" ]
do
    read -p 'Please input yes to stop looping! ' str
done
echo 'program is shutdown!'

5.2 for...do...don(固定循环)

语法:

for    var    in    con1    con2    con3    ...
do 
        程序段
done

假设有三个动物:狗、猫、猪,我们想每一行都输出她们怎么做?

[~]$ vim show_animals.sh
#!/bin/bash
# Program:
#   Using for ... loop to print 3 animals.

for animal in dog cat pig
do
    echo "this is a" ${animal}
done

再假设局域网有192.168.1.1~192.168.1.100,100台主机,我们想要进行网络检测是否可以连接:

[~]$ vim pingip.sh
#!/bin/bash
# Program:
#   Use ping command to check the network's PC state

network=192.168.4
for site in $(seq 1 100)
do
    ping -c 1 -W 1 ${network}.${site} &> /dev/null && result=0 || result=1

    if [ ${result} == 0 ] ; then
        echo ${network}.${site} 'UP'
    else
        echo ${network}.${site} 'DOWN'
    fi
done

5.3 for...do...done的数值处理

for (( 初始值; 限制值; 执行步骤 ))
do
        程序段
done

5.4 搭配随机数与数组

假设我们要写个脚本来决定你们团队今天中午吃什么菜怎么做?

[~]$ vim lunch.sh
eat[1]="汉堡"
eat[2]="鸡翅"
eat[3]="可乐"
eat[4]="冰糕"
eat[5]="烤肉"
eat[6]="火锅"
eat[7]="便当"
eat[8]="爆米花"
eat[9]="薯条"
eat[10]="雪花"
eatnum=10

eated=0
while [ "${eated}" -lt 3 ]
do
    check=$(( ${RANDOM} * ${eatnum} / 32767 + 1 ))
    mycheck=0
    if [ ${eated} -ge 1 ] ; then
        for i in $(seq 1 ${eated})
        do
            if [ ${eatedcon[$i]} == ${check} ] ; then
                mycheck=1
            fi
        done
    fi
    if [ ${mycheck} == 0 ] ; then
        echo 'you may eat' ${eat[${check}]}
        eated=$((${eated}+1))
        eatedcon[${eated}]=${check}
    fi
done

6.shell script 的追踪与debug

scripts执行前,如何debug呢?我们就以bash相关参数入手:

[~]$ sh [ -nvx ] scripts.sh
选项与参数:
-n  :不要执行script,仅查询语法问题;
-v  :在执行script前,先将script的内容输出到屏幕上;
-x  :将使用到的script内容显示到屏幕上;

7.重点

  • shell script是利用shell的功能所写的程序,这个程序使用纯文本文件,将一些shell的语法和指令写在里面,搭配正规表示法、管线命令与数据流重导向等功能;

  • shell script用在系统管理上面是很好的工具,但用在处理大量数值运算上,就不好用了!因为shell script 速度较慢,且使用CPU资源多;

  • shell script的执行至少要有r的权限,若下达指令,需要有r和x权限;

  • 良好的程序编写习惯,第一行要有声明shell(#!/bin/bash),第二行则声明程序用途等等;

  • 要建立每次执行脚本都有不同结果的数据,可使用date指令利用日期达成;

  • script的执行若以source来执行时,代表在父程序的bash内执行之意;

  • 若要进行判断式,可用test或中括号 [] 处理;

你可能感兴趣的:(第十二章 学习Shell Scripts)