第12章 学习shell脚本

风格不统一 提交于 2020-02-24 12:52:40

12.1什么是shell脚本

就字面上的意思,即针对shell所写的剧本,shell脚本是利用shell的功能所写的一个程序,这个程序使用纯文本文件,将一些shell的语法和命令(也可以是外部命令)写在里面,还能搭配正则表达式、管道命令和数据流重定向等功能,以达到我们需要的处理目的,执行一个“shell脚本”文件,就能一次执行多个命令,shell脚本还提供数组、循环、条件与逻辑判断等功能,用户可以直接用shell来编写程序,而不必使用类似C语言等传统程序语言来编写,shell脚本可以简单地被看成是批处理文件,也可以被说成是程序语言,且由于这个程序语言都是利用shell与相关工具命令,所以不需要编译即可执行

  1学习shell脚本的作用

自动化管理:管理一台主机每天要进行的任务有查询日志文件、跟踪流量、监控用户使用主机状态、主机各项硬件设备状态、主机软件更新查询等,你可以每天手动处理这些事情,也可以写个简单的shell脚本,让它来帮你处理每天的任务

帮助管理系统:CentOS6.x以前的版本中系统服务启动的接口是在/etc/init.d/这个目录下,这个目录下所有文件都是脚本文件,另外,包括启动过程也要利用shell脚本来帮忙查询系统的相关设置参数,然后再代入各个服务中,从CentOS7开始,/etc/init.d/这个脚本启动的方式由systemd所替代,仍有很多服务在管理服务启动方面,还是使用的shell脚本

简单入侵检测:当系统发生异常时会将异常记录在系统日志文件中,我们可以利用shell脚本在固定的几分钟内去主动地分析日志文件,若出现异常则报告给系统管理员让他加强防火墙规则

连续命令单一化:把一大串命令集合在一个文件里面,执行该文件即执行这一大串命令

简易的数据处理:可以用shell脚本直接处理数据的比对、文字数据的处理等

跨平台支持:几乎所有的UNIX-like上面都可以运行shell脚本

虽然shell脚本号称是程序,但实际上处理数据的速度不太快,因为它调用的是外部命令和shell的一些默认工具,常常需要调用外部的函数库,因此运行速度比不上传统的程序语言。shell脚本用在系统管理上面是一项很好的工具,但用在处理大量数值运算上就不够好,因为在于shell脚本的速度较慢,且使用的CPU资源较多,会造成主机资源的分配不良

  2基本规则

shell脚本中,命令是从上到下、从左到右地执行,命令、选项与参数间的空格都会被忽略掉,空白行会被忽略,tab键产生的空白视为空格键,如果脚本中存在并读取到了一个Enter符号(CR),就尝试开始执行该行命令,如果一行命令太多,可以使用\Enter来扩展到下一行,在一行中任何加在#后面的数据被视为注释

执行shell脚本的几种方式,前提是shell.sh文件必须具备rx的权限,使用绝对路径,如/home/dmtsai/shell.sh来执行,使用相对路径,假设工作目录在/home/dmtsai/,./shell.sh来执行,使用变量PATH,将shell.sh放进PATH指定的目录内如~/bin(用户家目录下的bin,自行设置并添加),shell.sh来执行,使用bash程序,通过bash shell.sh或sh shell.sh来执行(/bin/sh就是/bin/bash的链接文件)

脚本中的第一行为#!/bin/bash(以#!开头的行被称为shebang行),它声明这个脚本使用的shell名称,当这个脚本被执行时,它就能够加载bash的相关配置文件,一般就是非登录shell的~/.bashrc,并执行bash来使下面的命令被执行,如果被设置这一行,脚本可以无法执行,因为系统可能无法判断使用哪个shell执行,写脚本时要养成好的习惯,注释下该脚本的内容与功能、版本信息、作者和联系方式、脚本建立日期、历史记录等,可以用exit这个命令来让程序中断并返回一个数值给系统,如exit 0会结束脚本执行并将0返回给系统,接着用$?就能查看到这个“0”,同理,用exit n可以自定义错误信息

12.2简单练习

  1范例

变量内容由用户决定的交互式脚本,使用read命令,如read -p "Please input your first name: " firstname,会先显示一段提示信息,然后将用户的输入保存在变量firstname中

利用date建立文件名随日期变化的脚本,变量fileuser为用户输入的文件名,filename=${fileuser:-"filename"}不是当用户直接按下Enter并为输入时filename的值就设为"filename",若用户有输入,则变量filename的值为fileuser的值,获取前两天的日期date1=$(date --date='2 days ago' +%Y%m%d),今天的日期date3=$(date +%Y%m%d),前两天的文件名file1=${filename}${date1},建立文件touch "${date1}"

用$((计算式))或declare -i total=${num1}*${num2}进行简单的数值运算,bash默认仅支持整数数据,当要计算含小数点的数据时,就要用到bc命令

用bc命令计算Pi,echo "scale=num; 4*a(1)"|bc -lq会输出Pi的值,其中scale指定的num为Pi精确到多少位,4*a(1)为bc提供的计算Pi的函数,num=${checking:-"10"}用来判断用户有没有通过read输入要计算多少位Pi的值,没有输入则默认10位,

  2脚本执行方式的差异

脚本除了用上面提到的几种方式执行外,还可以使用source或小数点.来执行,前一类执行方式中,不论是用绝对路径、相对路径或是${PATH}内,或是用bash或sh来执行脚本,该脚本都会在一个新的bash环境中执行脚本中的命令,也就是脚本的执行是在当前bash的子进程中进行的,这种方式的特点在于,子进程内的各项变量或操作将会随着子进程的完成而消失,不会出现在父进程中。而用source或.来执行脚本时,是在当前bash中进行的,所有操作都会在当前bash中生效,这就是为什么当要不注销系统而要让某些写入~/.bashrc的设置生效时,要使用source ~/.bashrc(. ~/.bashrc)而不能用bash ~/.bashrc

12.3判断式

  1test命令的测试功能

当要检测系统上某些文件或相关属性时,使用test命令,如检查/dmtsai是否存在,test -e /dmtsai,执行结果不会显示任何信息,但可以利用$?或&&及||来展现结果,如test -e /dmtsai && echo "exist" || echo "not exist",test -e /dmtsai的执行结果可以用$?查看,0表示成功,其他数字表示失败,所以这个命令在/dmtsai存在的情况下输出exist(&&前面执行成功就执行后面命令),不存在的情况下输出not exist(||前面命令执行失败就执行后面),test使用见(Linux常用命令2)

  2判断符号[]

判断${HOME}这个变量是否为空,[ -z "${HOME}" ]; echo $?,要注意中括号用在很多地方,如通配符和正则表达式,在bash的语法中使用中括号用作shell的判断式时,中括号内部的两端必须有空格符来分隔(起到区分的作用),以o表示空格的话,使用判断符号时在这些地方需要加上空格,[o"${HOME}"o==o"${MALL}"o],否则bash显示错误信息,在bash中使用一个=和两个=效果是一样的,都是判断是否相等,在中括号内的每个组件都需要有空格来分隔,中括号内的变量最好都以双引号括起来,常数则最好都以单或双引号括起来,如果设置name="VBird Tsai",接着判断[ ${name} == "VBird" ],会发生太多参数的错误,因为实际判断变成了VBird Tsai == "VBird",左边表示有两个数据,而右边只有一个。另外中括号的使用与test基本相同

  3shell基本的默认变量

命令可以带选项与参数,把shell脚本看作是一个可执行文件,它当然也应该有参数,脚本带参数的形式为脚本名 参数1 参数2 参数3..,脚本针对这些参数设置了一些变量名称,其中$0、$1、$2、$3这几个变量分别代表脚本名、参数1、参数2、参数3,还有一些较为特殊的变量,$#代表后面接的参数的个数,这里即为3个,$@代表"$1""$2""$3",每个变量都是独立的,$*代表"$1c$2c$3",其中c为分隔符,默认为空格,所以这里代表"$1 $2 $3",$@和$*还是有所不同,我们可以通过在脚本内使用这些变量来调用对应参数,如执行了一个带参数的脚本,它的结果显示了程序的文件名、参数个数、第一个参数是什么等,则脚本内一定有$0、$#、$1这几个变量来输出这些数据,在使用时,$0、$1、$2这些变量要加上{},即${0},$@、$#则不需要

shift会造成参数变量号码偏移,在脚本中出现一次shift,排在最前面的那个参数就会被拿掉,shift后面可以接数字,表示拿掉前面几个参数的意思,如执行sh shift_paras.sh one two three four five six共传入六个参数,当在某一行执行了shift 3,再往下执行输出所有参数时,就只会出现four、five、six

12.4条件判断式

  1当符合某个条件判断的时候就予以执行某些任务的if...then,单层的if...then为

if [ 条件判断式 ]; then

    当条件成立时执行的任务

fi   <==结束if的意思

其中的条件判断式即为上述的判断符号[],当有多个条件要判断时,可以将多个条件写进一个[]中如[ "${yn}" == "Y" -o "${yn}" == "y" ],还可以将多个条件分别写入[]中并用&&和||符号分隔,这时的&&和||与在[]中的意义是不同的,用在不同的判断式之间分别代表"与"和"或"的关系,所以上式可替换为[ "${yn}" == "Y" ] || [ "${yn}" == "y" ]

对同一数据进行多种不同的判断的多重的if...then为

if [ 条件判断式1 ]; then

    当条件1成立时执行的任务

elif [ 条件判断式2 ]; then

    当条件2成立时执行的任务

else

    当条件1、2都不成立时执行的任务

fi

编写一个输入退役日期即可知道还有多久退役的脚本:

#! /bin/bash

read -p "please input your demobilization date: (YYYYMMDD) ex>20150617: " date

date2=$(echo ${date} | grep '[0-9]\{8\}')  #判断是否是8个数字

if [ "${date2}" == "" ]; then

    echo "you input the wrong date format."

    exit 1

fi

declare -i date_dem=$(date --date="${date}"+%s)  #转换为自1970-01-01累计的秒数

declare -i date_now=$(date +%s)

declare -i date_total_s=$((${date_dem}-${date_now}))  #内层的()是必需的

declare -i date_d=$((${date_total_s}/60/60/24))

if [ "${date_total_s}" -lt "0" ]; then

    echo "you had been demobilization before $((-1*${date_d})) ago"

else

    declare -i date_h=$(($((${date_total_s}-${date_d}*60*60*24))/60/60))

    echo "you will demobilize after ${date_d} days and ${data_h} hours."

fi

  2根据变量内容选择对应执行的一个程序段的case...in...esac

case $变量名称 in

    "第一个变量内容")   <==如果变量内容是这个就执行下面的程序段

        程序段

        ;;

    "第二个变量内容")

        程序段

        ;;

    *)   <==变量的内容既不是第一个也不是第二个的其他情况就是*

        程序段

        ;;

esac

早期系统的很多服务的启动脚本都是使用case...in...esac的写法,虽然现在CentOS7已使用systemd,但仍有数个服务放在/etc/init.d/目录下的服务

  3函数

function 函数名() {

    程序段

}

shell脚本中的函数定义一定要在程序的最前面,这样才能在执行时被找到(与传统程序语言不同),echo加上-n选项可以不用换行地继续在同一行显示

function也拥有内置变量,且与shell脚本的很类似,$0表示函数名,调用函数时后续接的参数对应$1、$2...,注意函数和脚本的这些变量的区别,在脚本内部使用$0时就代表脚本名,在函数内部使用$0时就代表函数名

12.5循环

  1while do done、until do done的不定循环

while [条件判断]

do

    程序段

done  <==当条件成立时就进行循环,直到条件不成立才停止

until [条件判断]

do

    程序段

done  <==当条件不成立时就进行循环,直到条件成立才停止

编写一个计算1-100数字和的脚本:

#! /bin/bash

i=0

s=0

until [ "${i}" == "100" ]

do

    i=$((${i}+1))

    s=$((${s}+i))

done

  2for...do...done的固定循环

for 变量名var in con1 con2 con3...  <==将变量内容con1赋值给var,执行一次程序段,再将con2赋值给var执行一次程序段...

do

    程序段

done

编写一个脚本用id查看所有用户的信息:

#! /bin/bash

users=$(cut -d ':' -f1 /etc/passwd)

for username in ${users}

do

    id ${username}

done

  3for...do...done的另一种写法

for ((变量初始值; 限制条件; 变量赋值))  <==for中的语句类似i=1; i<=100; i++

do

    程序段

done

12.6shell脚本的跟踪与调试

通过使用bash的相关选项,可以不需要直接执行脚本就知道脚本是否有问题,使用见(Linux常用命令2)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!