设为首页 收藏本站
查看: 1015|回复: 0

[经验分享] bash脚本编程详细剖析

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2014-12-4 10:27:00 | 显示全部楼层 |阅读模式
背景:bash脚本编程是Linux学习一个至关重要的部分,想完成一个脚本可能很简单;但是想让自己的脚本写的让人觉得心旷神怡实为不简单。bash是所有Linux发行版的几乎都有的,因此我们这里以bash脚本为例,讨论bash脚本的编写方法。对于bash脚本编程中一些比较重要的知识点,我这里也会给予案例演示。
一.脚本编程中前话:

    我们都知道,bash脚本编程说白了就是命令的堆积。只不过这种堆积的方式不是杂乱无章的堆积,而是按照一定要求和格式的链接。这说明在学好编程的同时,一定要有大量的的命令基础。这就像我们以前写作文一样,一定要有大量的辞藻基础才能把文章写的饱满。脚本编程其实就是这样,这就需要我们自己多多记忆啦。另外要养成多看好的编程脚本的习惯,好的地方可以记忆下来。久而久之,自己的编程素材就多起来了。
二.bash脚本的的基础知识和流程梗概:

    首先,脚本的文本命名为了便于区别,我们都是以.sh作为后缀。脚本的第一行要写明解释器入口例如bash:#!/bin/bash 。要注意除了#!/bin/bash行之外,所有以#号开头的行都是注释行。注释行的文本不作为脚本执行过程。脚本在编写时要注意脚本内容的层次性,各层次之间实现缩进。这样才能让脚本调整的时候一目了然。这也是一个很重要的素质。另外脚本中经常会涉及一些关于计算的表达式,我这里简单提供四种方法,之后会在具体的习题中体现具体如何运用:
    1.$[EXPRESSION] $符中括号中加算数表达式的形式。
    2.$((EXPRESION))$符加两个小括号加算数表达式的形式。
    3.let VAR_NAME=EXPRESSION let 让一个变量等于一个表达式的形式。
    4.$(expr argu1 argu2 argu3) $符小括号格式
脚本在编写完之后的调用方式有多种,比如我们调用一个叫files.sh的脚本。我们可以在脚本的相应目录,先给files.sh附加一个执行权限:chmod +x files.sh ,然后在直接这种方式调用 ./files.sh 。或者,我们还可以在命令行中直接:bash -n stusum.sh 先来查看一下有没有语法错误,然后bash files.sh即为调用了。例如:
wKiom1R-_r3BC-N9AADy6e-tWDM848.jpg
三.bash脚本编程流程控制语句:
    这样了解了脚本编写调用的基本流程之后,可以来了解具体的一些语法了。bash的脚本编程的流程控制语句大致分为:顺序执行;循环执行;选择执行;。变量大致有:本地变量;环境变量;特殊变量;位置参数变量;局部变量。
    流程控制语句之—顺序执行:顺序执行顾名思义,就是按照从前向后执行的方式,依次完成脚本的逐个语句。例如:

wKioL1R_CO2AX7RJAADNddlXnf8168.jpg    
流程控制语句之—条件执行:条件执行即为满足一种条件就去执行下一步,不满足就跳过该步不执行。条件判断主要是if型和case型。先来看下if型:
    if格式:if CONDITION; then  CMD ...; fi这是单分支的if语句,即条件满足就执行if中的语句,否则跳过。我们来个例子看一下:
wKioL1R_VgLRAf9NAAClH8ADqBk258.jpg
分析一下:for 循环{1..9}。我们用一个单分支语句来条件判断,-le是一个整数测试条件,表示小于等于。我们这里是小于等于5的求和自然是15。对于条件执行语句的条件条件可分三类:(1) 整数测试;(2) 字符串测试;(3) 文件测试
    1.整数测试:
     整数测试:A, B
                                A -eq B: 等于
                                A -ne B: 不等于
                                A -ge B: 大于等于
                                A -gt B: 大于
                                A -le B: 小于等于
                                A -le B: 不等于
     比如想表示条件A < B ,可以写成 if [ $A -lt $B ];then
    2.字符测试条件:
      符串测试:A, B
                                A > B
                                A < B
                                A >= B
                                A <= B
                                A == B或A = B:等值比较
                                A != B: 不等于
                                -z A: 判断A是否为空;空则为真,不空则假;
                                -n A:判断A是否不空;不空则为值,空则为假;
    例如:我们判断一段未知字符是否为know ;可以表示为if [ "$A" == "keow" ];then

注意:在字符判断是=号与字符之间保留一个空格,避免报错。
    以上是单分支的if语句当我们想表示如果不是A,就B的情况是。单分支语句此时就没办法很好的实现了,因此我们还有双分支以及多分支if语句。if CONDITION-TRUE; then CMD1 ;else CMD2 ;fi 。如果多分支格式为:
                                if TRUE; then
                                        分支1
                                elif CONDITION2-TRUE; then
                                        分支2
                                elif CONDITION3-TRUE; then
                                        分支3
                                ...
                                else
                                        分支n
                                fi
注意:这里else也可以没有,为了保持格式的统一建议保留。下面给个例子来熟悉下这个单分支和多分支用法。例如:我们要实现以下功能,给脚本传递一个用户名,如果用户存在则判断其id 是否小于500,如果小于500输出,该用户为系统用户。如果id 大于500,输出用户为普通用户。如果给的用户名不存在,我们给予添加该用户。对于这种题目我们可以先用自己的语言分析一下,先把框架理清楚再下手。
   wKiom1R_NGqTk1_oAAEax5Mnuk4946.jpg
注意:这里使用了脚本传递参数,就涉及的到特殊变量了。那再来找找脚本中船用的特殊变量有那些。$*表示传递参数的列表,比如外面给了三个参数a,b,c 那么$*就表示{a b c}三个参数的集合。他可以直接用在循环中作为LIST,通常用于定义我们不知道的传递参数列表。$#  表示传递参数的个数,比如给脚本传递10个参数,$#就为10。$1...$n用于表示传递的第几个参数。例如$1表示传递的第一个参数。
    其实对于这种多分支的if语句,还可以有另外一种简洁方式case语句。case的语法结构:他表示是如果变量引用属于那个pattern,就相应的执行那一个分支。
                                  case 变量引用  in
                                  PATTERN1)
                                  分支1
                                    ;;
                                  PATTERN2)
                                 分支2
                                    ;;
                                     ...
                                  *)
                                 分支n
                                    ;;
                                  esac
     下面我们就用case来实现以下功能。例如:我们使用tar工具把/etc目录备份至/backup目录中,名字为/backup/etc-日期时间.tar.{xz|bz2|gz};
        (1) 显示如下菜单
                        #######################################
                        #       xz) xz compress tool          #
                        #        gzip) gzip compress tool      #
                        #        bzip2) bzip2 compress tool    #
                        #        *) wrong choice and quit      #
                        #######################################

        (2) 根据用户选择的工具,执行相应操作;如果用户没有键入任何数据,则默认使用xz;

wKiom1R_WROy66SeAAJwJvdYDIc648.jpg


注意:[ -z $command ] && command="xz" 其实是另一中逻辑判断给条件添加逻辑操作符;而-z则表示判断文件是否为空。具体含义:如果命令为空,执行command="xz" 除此还有[ -z $command ] || command="xz" 它则表示如果command空为真,不执行command="xz" 。怎么样像不像一个双分支if语句。其实在脚本中这种用法代替if非常常见。除此种表示之外,还可以表示为:
                或, -o: [ -z "$hostname" -o "$hostname" == 'localhost' ]
                与, -a: [ $uid -gt 0 -a $uid -lt 500 ]
                非:[ ! EXPRESSION ]
    除以上条件判断的种种用法,在条件判断中,有时我们还会用到文件判断。文件判断就是诸如判断一个文件是否存在,判断一个文件是否为目录,判断一个文件是否有写权限等等。

                -e $file: 是否存在;存在则为真;
                -a $file: 同上;弃用;
                -f $file: 文件是否存在,且为普通文件;
                -d $file: 是否存在且为目录;
                -h $file: 是否存在且为符号链接文件;
                -L $file:同上
                -b $file: 是否存在且为块设备文件;
                -c $file: 是否存在且为字符设备文件;
                -S $file: 是否存在且为套接字文件:
                -p $file: 是否存在且为管道文件;

                -r $file: 当前用户对此文件是否拥有读权限;
                -w $file: 当前用户对此文件是否拥有写权限
                -x $file: 当前用户对此文件是否拥有执行权限;

                -u $file: 文件是否拥有suid权限;
                -g $file:文件是否拥有sgid权限;
                -k $file: 文件是否拥有sticky权限;

                -O $file: 当前用户是否为文件的属主;
                -G $file: 当前用户是否属于文件的属组;

                -N $file: 文件自从上一次被读取之后,是否被修改过;

                $f1 -nt $f2: 文件f1是否比文件f2新;
                $f1 -ot $f2: 文件f1是否比文件f2旧;
                $f1 -ef $f2: f1和f2是否为同一个文件的硬链接


    流程控制语句之一循环执行:循环执行这里有for 型;while 型;until 型;每一种有各自的语法,但是都有和很大的相似之处。先来看一下for 型循环:

    for型循环格式:for VAR in LIST ; do  STATEMENT1 ...;  done。VAR是一个变量,表示LIST中的每一个。LIST是一个列表,LIST的个数决定循环的次数。do ...done 分表用来表示循环体的开始和结束。do ...done之间的部分表示循环体,就是每次循环都会执行的部分。我们来一简单的for循环语句来看一下具体的用法比如:我们给系统添加10个用户分别为:student1...student10.
wKioL1R_DVTBfeQpAACCM3lh52o180.jpg     分析:i变量代表1..10中的每一个数,LIST这里是用花括号{first..last}表示。中间两个点点不可丢掉呀。do表示满足条件开始进入循环,done是循环体的结束位置。那么我们这里来说一下循环的LIST列表的几种生成形式:
    1.{first..last} 花括号这种表达方式。
    2.seq first step last。这里seq是命令这里使用时要命令引用如:`seq 1 2 100` 这就表示,从1到一百中的奇数部分。
    3.globbing通配的方式  例如: for i in /etc/*  表示/etc目录下的所有子文件
    4.变量引用的方式:$* ,$# 等
    5.直接列出——这是一种最简单粗暴的方式,但是仅限于极少参数的情况。
注意:for 循环如果省略会自动生成列表,但是不提倡这种做法。
    for循环的嵌套:有时在循环时我们为了让问题简单的实现,需要使用使用双重或者多重循环。这样就必然会用到循环的嵌套。嵌套其实就是外循环循环一次,内循环循环一个周期的方式。比如我们来实现一个9*9乘法表看下效果。

wKiom1R_FHjxZYFjAACX_6BxaTI016.jpg     分析一下:外循环i{1..9}。按照我们说的思路,外循环变化一次,内循环循环一个周期。当i为1时 内循环循环一个周期。循环{1..$i}此时i为1, 实际就是1X1。当i=2时,内循环还是一个周期{1..$i}。此时就是1X1 1X2依次类推。然后只需要在每次i=j的时候换行就可以了,循环中的echo就是实现这一功能的。        表示退出一个制表符,用-e 来转义。还要注意的是,i ,j都为变量,为了保证输出本身字符时不会报错,均用花括号括起来了。


    while型循环格式:while true ;do ... done 这种循环表示当条件为真时开始循环。但是直的注意的是,while循环语句结构中条件判断一旦为真,就进入循环了。而且结构中并没有给予循环跳出条件,因此在写循环时,我们必须给出循环的跳出条件。具体如何实现:比如我们想监控当前登陆系统的一个叫centos的人,每5秒刷新一次。如果登陆就显示centos is logged on. 如果没登陆就显示no login.
#!/bin/bash
#
while true; do
who | grep "centos" &> /dev/null
if [ $? -eq 0 ];then
        break
fi
        sleep 5
echo "no login"
done
echo "centos is logged."
~ wKiom1R_UUjh0JwlAADE-CID6AQ166.jpg     除此while还可以用于遍历文本的每一行格式如下:while read line; do 循环体 ;done < /path/from/somefile 。按行读取done后面重定向到的文本。例如:我们随便给定一个文本文件,让while统计有多少行。
wKiom1R_VKyBFpinAAB2PbnaJHY058.jpg      until作为循环语句用法和while只用条件判断是相反的,其余的用法相同。即当until条件为假时实现循环。用法简单,朋友可以自行尝试,此处不在举例。下面一篇将是bash的函数的和数组用法。   


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-35620-1-1.html 上篇帖子: Python实战-天气预报项目-获取天气信息 下篇帖子: centos6.3 安装python爬虫框架scrapy
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表