首页 > 代码库 > bash脚本编程基础

bash脚本编程基础

回顾:

         文件系统管理

         管理工具:mkfsmke2fse2labeltune2fsdumpe2fse2fsckblkid

                            mkfs.xfsmkfs.vaftfsck

                            mmkswapswaponswapoff

                            mountumountfuerlsof

                            dfdu

         fstab文件:

                   设备         挂载点        文件系统类型     挂载选项     转储频率      自检次序

 

         文件系统:

                   目录也是文件

                            目录也有元数据inode,元数据存放在inode  table

                            数据:data  blocks

                                               下级文件或目录的文件名与其inode对应关系

                           

                                     dentry

                   文件名:上级目录;

 

 

删除文件:

 

         (我们从别处复制或者创建一个2G的文件非常慢,但是删除一个文件速度会非常快,这是什么原因导致的?

                   其实删除文件的方式非常容易,因为他无需填充数据,也无需移除数据,)

 

         删除文件:

 

                  第一步:将此文件指向的所有data  block标记为未使用状态(这就意味着其他数据在填充时,可以覆盖里面的原有数据了。可以使用这些磁盘块了);

                   第二步:此文件的inode标记为未使用;(这仅需要修改inode表中条目的状态,和各datablock,并修改inode位图,和datablock位图即可完成);

         之所以有些文件删除以后,能够通过一些文件恢复工具找回,原理是我们扫描这些数据块上是否依然有文件,只需要扫描某些inode,这些inode中是否有有效的数据,而不用管他是否有标记未使用;从而能够把误删除的文件找回来。但是我们如果在此分区上,删除了文件,又存储了文件,这时候数据很难找回。

 

         复制和移动文件:

 

                   复制:就是新建文件。

                            (其实就是新建一个文件,将原文件的数据流导出来,然后在流入cetons,无论是在本地文件系统,还是跨文件系统,复制就是新建文件。)

                   移动文件:

我们剪切文件时,我们发现速度非常快,尤其是在同一个分区上的时候,但是跨分区时就不一定了。原因:在同一个文件系统上移动文件,仅仅是将其路径映射修改了,inode还是那个原来的inode,连这个文件的inode某没有变,那么inode指向的data base数据也就不变。

[root@centos6 ~]# ls -i fstab.2

2373340 fstab.2

[root@centos6 ~]# mv fstab.2 /tmp

[root@centos6 ~]# ls -i /tmp/fstab.2    //移动前后,文件的inode不变

2373340 /tmp/fstab.2

[root@centos6 ~]#

 

         移除文件:

                   在同一个文件系统:改变的仅仅是其路径;

                   在不同文件系统;复制数据至目标文件,并删除源文件;

 

 

 

符号连接:

         一般情况下,一个文件都有一个元数据和数据,但是符号链接没有数据,即data  block,因为在符号链接的元数据中的数据块指针中,存放的是另一个目录或文件的路径。但是当指向的目标文件的路径非常长时,当数据块指针空间不够用时,也是需要使用数据块的。

         权限为:lrwxrwxrwx  777

 

 

硬链接:

         指向同一个inode,任何一个文件路径被删除,另一个文件路径还有效,

 

 

 

 

bash脚本编程:

 

                  脚本文件格式:

 

                            首行顶格写:#!/bin/bash

                            注释信息:用#表示

                            代码注释:

                            缩进,适度添加空白行;

 

语言:编程语法格式,库,算法和数据结构

编程思想:

         问题空间--------->解空间

 

 

 

 

脚本:

         在脚本中前面一般需要写一些说明信息:

                   description(描述)

                   #version:0.0.1

                   author:dong<qq.com>

                   date:2015-11-10

 

 

变量:

         shell脚本中没有复杂的数据结构,但是并不妨碍我们自己去组织出来复杂的数据结构,为了组织出来复杂的数据结构,我们就需要一些内建的数据存储机制,比如说像变量,还有像多个变量连续组织在一起的存储结构就叫数组。)      

 

shell脚本中的变量类型:

 

         局部变量(代码片段),

         本地变量(当前shell进程)

         环境变量:当前进程及其子进程

 

         位置参数变量

         特殊变量

 

变量其实就是有名字的内存空间。

 

 

变量中的数据类型:

 

         字符型

         数值型

                   精确数值型;

                            整型

                   近似数值型:

                            浮点数(单精度,双精度)

 

                            但是shell脚本内部是不支持浮点数的,只能借助其他   的工具来实现。

 

shell编程是典型的弱类型语言。

         因为shell将所有的数值类型都看作是字符型。所以他的算术运算,加“+”就看做是字符串的连接,其他得减,乘,除就没什么意义了。

 

[root@centos6 ~]# apple=iphone         //下面我们想输出iphones

[root@centos6 ~]# echo "$apples"

 

[root@centos6 ~]# echo "${apple}s"            //要加大括号括起来

iphones

[root@centos6 ~]# echo $PATH

/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin

[root@centos6 ~]# PATH="$PATH:/local/apache/bin"        //PATH后面再加一个路径,可以                                                                                                                      不用加双引号。

[root@centos6 ~]# echo $PATH

/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/local/apache/bin

[root@centos6 ~]#

 

 

算术运算:

         +  - *  /   %   **(次方运算)

         let  VAR=expression

         VAR=$[expression]

         VAR=$((expression))

         VAR=$(expr  参数运算符  参数2)           //$() 是命令引用。这个运算表达式运算                                                                                               符前后都有空格

        

注意:有些时候乘法符号*需要转义;

 

 

shell脚本中支持变量的增强型赋值:

 

 

[root@centos6 ~]# declare -i i=1

[root@centos6 ~]# echo $i

1

[root@centos6 ~]# i=$[$i+1]

[root@centos6 ~]# echo $i

2

[root@centos6 ~]# i=$[$i+1]

[root@centos6 ~]# echo $i

3

[root@centos6 ~]#

上面的这个例子就是变量I将自身每次加1然后存回i本身,像这样的运算就是变量的增强型赋值

 

 

增强型赋值:

         变量做某种算术运算后,回存至此变量中。

                   let i=$i+#

                   let i+=#

 

常用的增强型的表达式:

                   +=  -= *=   /=  %=

         这种增强型的赋值,在shell脚本中我们一般要使用let进行描述。

 

         自增:

                            VAR=$[$VAR+1]

                            let  VAR+=1

                            let  VAR++    //这个表示在自身上每次都加1

         自减:

                            VAR=$[$VAR-1]

                            let  VAR-=1

                            let  VAR--

 

演示:

         我们写一个脚本,创建两个用户,并求出这两个用户UID的和

编写的脚本内容是:

         #!/bin/bash

         id user1  &>  /dev/null ||  useradd  user1

         id user2  &>  /dev/null ||  useradd  user2

 

         user1_id=$( id  -u user1 )

         user2_id=$( id  -u user2 )

         id_sum=$[ $user1_id+$user2_id ]

         echo "T he  id  sum: $id_sum."

 

运行上面的脚本,我们可以直接执行,不给他赋执行权限的话:

         bash  idsum.sh

 

 

 

练习:

         1.写一个脚本

                   计算/etc/passwd文件中的第10个用户和第20个用户的id号之和

 

                   解:

                            分析:先用命令实现将两个用户的ID号取出:

                                     id1=$(head  -10 /etc/passwd  |  tail -1  |  cut -d:  -f3)                                                      id2=$(head  -20 /etc/passwd  |  tail -1  |  cut -d:  -f3)     

 

         问题拓展,如果要想取出UID最大的两个用户,然后求其UID之和,那就用sort进行一下排序,然后再取。

 

 

         2.写一个脚本

                   计算/etc/rc.d/init.d/functions/etc/inittab文件的空白行数之和;

 

                   解:

                            分析:要先怎样去界定空白行:

                            grep"^[[:space:]]*$"  /etc/rc.d/init.d/functions  |wc  -l

 

 

 

条件测试:

         (什么是条件测试?

                            如果某用户不存在我们就去添加用户,那怎样去判断用户是否存在呢?之前我们用的是id命令,id后跟用户名,如果执行失败就添加用户,那么这就是条件测试)

        

         条件测试:

                   判断某需求是否满足,需要由测试机制来实现;

(专用的测试表达式,需要由测试命令来辅助完成,那问题是我们如何实现测试机制,如何完成测试条件的描述)

 

                   举例:

         判断某用户是否存在?判断某文件中是否有空白行?

解:

         我们编写一个命令,只要这条命令执行正确,我们就认为这个文件是存在空白行,我们在查看上面的这条命令的执行状态结果为0即为正确执行:

 

 

         1)我们判断一个用户存在不存在,我们要先知道怎样用命令将这个用户名取出来,然后再利用命令执行状态结果的返回值来判断

[root@centos6 ~]# who | grep "^centos\>"

[root@centos6 ~]# echo $?

1                //说明命令不存在                                                        

[root@centos6 ~]#

 

         2       grep  "^$"  /etc/rc.d/init.d/functions

 

[root@centos6 ~]# grep "^$" /etc/rc.d/init.d/functions&> /dev/null

[root@centos6 ~]# echo $?

0

[root@centos6 ~]#

上面这条命令我们根据他的执行状态结果的返回值来完成测试。

 

 

但是也有一些条件测试我们无法给他编写成命令

         比如我们判断一下2是否大于3

                   这是我们需要使用一个比较表达式执行测试,像“2>3”这个表达式本身不是一个命令,不能在命令行中运行,这时我们就需要给借助一个专门用于比较的测试表达式来实现

[root@centos6 ~]# test 2 > 3

[root@centos6 ~]# echo $?

0

[root@centos6 ~]# test 2 < 3

[root@centos6 ~]# echo $?

0

[root@centos6 ~]# test 2<3

[root@centos6 ~]# echo $?

1

[root@centos6 ~]# test 2>3

[root@centos6 ~]# echo $?

1

[root@centos6 ~]#

注意:由上面的执行命令,我们需要说明的是,在shell脚本中进行数值的比较并不是使用大于号或者小于号进行的,而是使用特定的符号

 

 

 

         如何编写测试表达式,以实现所需的测试:

                            1)执行命令,并利用命令的状态返回值来判断;

                                               0:成功

                                               1-255:失败

                            2)测试表达式:

                                               test  EXPRESSION

                                               [  EXPRESSION ]    

                                               [[  EXPRESSION ]]

                            注意:在测试EXPRESSION两端必须有空白字符,否则为语法错误;

                           

         bash测试类型:

                   数值测试

                   字符串测试

                   文件测试

                  

 

         数值测试:数值比较

                   -eq:是否等于

                            举例:     [ $num1  -eq  $num2 ]

                   -ne:是否不等于

                   -gt:是否大于

                   -ge:是否大于等于

                   -lt:是否小于

                   -le:是否小于等于

 

举例:

         [root@centos6~]# test 5 -ge 6   //比较符前后要有空格

         [root@centos6~]# echo $?

         1

         [root@centos6~]# test 5 -lt 6

         [root@centos6~]# echo $?

         0

         [root@centos6~]#

 

         字符串测试:字符比较

                            ==:两个等于号,表示两个字符是否相同;(这使用一个等号也是可以的,因                                        为比较表达式卸载中括号中,我们的赋值是不会写在中括号中的。)

                            >  :是否大于;

                            <  :是否小于;

                            !=  :是否不等于;

                            =~  :左侧字符串是否能够被右侧的PATTERN所匹配

 

                            -z "STRING":判断指定的字串是否为空;空则为真,不空则为假;

                            -n"STRING":判断指定的字符串是否不空;不空则为真,空则为假;

 

 

         字符间的比较无非就是按照字符在ASCII表中对应的数值进行比较。

 

注意:

         1)字符串要加引号,表示引用;做变量替换就用双引号,不做替换可以用单引号

         2)做字符间比较时要使用[[]]

我们在做字符间比较时,尽量使用双中括号“[[]]”在单中括号中比较是不生效的。

 

                   举例1

[root@centos6 ~]# [ tom == Tom ]

[root@centos6 ~]# echo $?

1

[root@centos6 ~]# [ tom == tom ]

[root@centos6 ~]# echo $?

0

[root@centos6 ~]# [ tom == $name ]

-bash: [: tom: unary operator expected

[root@centos6 ~]# [ tom =="$name" ]    //由于name的值不存在,也就是null值,不加引                                                                                         号的话,我们的tom不知道跟谁比较,所以我们                                                                                        加上引号,表示至少这也是个空串

[root@centos6 ~]# echo $?

1

[root@centos6 ~]#

注意:在有些场景中我们必须要给他加引号,但凡使用变量,在执行变量之间的等值或者大小比较时,一定要记得加引号。平时我们用的时候不加引号没有问题。

 

 

                   举例2

                            [root@centos6~]# [[ a>b ]]

                            [root@centos6~]# echo $?

                            1

                            [root@centos6~]# [[ a<b ]]

                            [root@centos6 ~]# echo $?

                            0

                            [root@centos6~]# [ a>b ]

                            [root@centos6~]# echo $?

                            0

                            [root@centos6~]# [ a<b ]

                            [root@centos6~]# echo $?

                            0

                            [root@centos6~]#

 

 

演示字符比较中的“=~

 

[root@centos6 ~]# name=jerry

[root@centos6 ~]# [[ "$name" =~e.* ]]          //这也说明了这个也不是做全部匹配的,只要能匹                                                                             配模式中的一部分就行

[root@centos6 ~]# echo $?

0

[root@centos6 ~]# [[ "$name" =~ e]]

[root@centos6 ~]# echo $?

0

[root@centos6 ~]# [ "$name" =~ e]                 //这也看出来做字符比较时我们一定要使用[[]]

-bash: [: =~: binary operator expected

[root@centos6 ~]#

 

脚本的状态返回值:

                   默认是脚本中执行的最后一条命令的状态返回值;

(但是二者不能等同,比如说脚本前面30条命令都错了,就最后一个命令是对的,那最后echo一下发现脚本是正确的,因为最后的echo命令正常执行了,为了避免出现这种结果,我们自定义执行状态结果返回值)

 

                   自定义状态退出状态码;

                            exit  [n]:不加数字表示状态码为0,加数字n为自己指定的状态码;

                                     注意:在shell脚本中不能轻易使用exitshell进程遇到exit时,即会终止,因此,整个脚本执行即为结束;

         那么exit不能随便加,我们需要给他加一个判断条件以后再加exit:

举例:

         使用条件测试来判断我们要不要退出:

         不如说,先判断用户是否存在,存在我们就退出,不存在我们就添加用户,

[root@centos6 ~]# id user3 &> /dev/null && exit 0 ||useradd user3

技术分享这里连终端都退出了。但是在shell脚本中运行时是退出脚本。

         上面的这个命令就用到了短路与,前面的命令执行正确,那么就执行exit,前面的得错误就直接执行后面的创建。

 

 


本文出自 “11847750” 博客,请务必保留此出处http://11857750.blog.51cto.com/11847750/1875121

bash脚本编程基础