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

bash脚本编程基础

文件测试:

         (文件测试的功能无非就是判断文件的存在性,权限等测试;字符测试和前面的讲到的数值测试,字符测试还是不一样的,数值测试和字符测试他们主要是使用双目测试,即需要两个测试数,只有使用到“-z”或“-n”时他们才是单目运算符,对于文件测试而言,大部分使用的是单目的,说白了就是需要一个操作数,)

 

         文件存在性测试:

                   -a  FILE  //FILE就是表示需要自己替换成文件路径

                   -e  FILE

                            文件的存在性测试,存在则为真,否则则为假;

 

举例:

[root@centos6 ~]# [ -e/etc/rc.d/init.d/functions ]

[root@centos6 ~]# echo $?

0

[root@centos6 ~]# [ -e /etc/rc.d/rc.sysinit]

[root@centos6 ~]# echo $?

0

[root@centos6 ~]#

 

        

         文件存在性及类型测试:(存在且为块设备)

                   -b  FILE:是否存在并且为块设备文件;

                   -c  FILE:是否存在并且为字符设备文件;

                   -d  FILE:是否存在并且为目录文件;

                   -f  FILE:是否存在并且为普通文件;

                   -h  FILE -L  FILE:是否存在并且为符号链接文件;

                   -p  FILE:是否存在且为命名管道文件;

                   -S  FILE:(大写的S)是否存在并且为套接字文件;

 

举例:

[root@centos6 ~]# test -b /dev/sda

[root@centos6 ~]# echo $?

0

[root@centos6 ~]# test -b /dev/sdb

[root@centos6 ~]# echo $?

1

[root@centos6 ~]# [ -e /etc/inittab ]                //文件存在

[root@centos6 ~]# echo $?

0

[root@centos6 ~]# [ -d /etc/inittab ]                //但她不是块设备

[root@centos6 ~]# echo $?

1

[root@centos6 ~]#

         文件权限测试:

                   -r  FILE:是否存在并且对当前用户可读;

                   -w  FILE:是否存在并且对当前用户可写;

                   -x  FILE:是否存在并且对当前用户可执行;

         特殊权限测试:

                   -g  FILE:是否存在并且拥有sgid权限;

                   -u  FILE:是否存在并且拥有suid权限;

                   -k  FILE:是否存在并且拥有sticky权限;

举例:

[root@centos6 ~]# ls -l /usr/bin/passwd

-rwsr-xr-x. 1 root root 30768 Nov 24  2015 /usr/bin/passwd

[root@centos6 ~]# [ -u /usr/bin/passwd ]

[root@centos6 ~]# echo $?

0

[root@centos6 ~]#

 

 

         文件是否有内容测试:

                   -s  FILE:(小写s)是否有内容也可以说成是否不为为空,有内容为真;

举例:

[root@centos6 ~]# touch yes

[root@centos6 ~]# [ -s yes ]

[root@centos6 ~]# echo $?

1

[root@centos6 ~]# [ -s /etc/fstab ]

[root@centos6 ~]# echo $?

0

[root@centos6 ~]#

 

 

         文件侧时间戳做测试:

                   -N  FILE:文件自从上一次读取操作后,是否被修改过;

        

         从属关系测试:

                   -O  FILE(大写O):当前用户是否为文件的属主;

                   -G  FILE:当前用户是否属于文件的属组;

 

         双目测试:

                   FILE1  -ef FILE2FILE1FILE2是否指向同一个文件系统的相同inode的硬链接;

                   FILE1  -nt FILE2FILE1最近的一次更新时间是否比FILE2的更新时间距离这一刻                                                           更近,所以简写为:FILE1是否新于FILE2

                   FILE1  -ot FILE2:是否旧于FILE2

 

 

         组合测试条件:

                   逻辑运算:

                            第一种方式:

                                     COMMAND1  && COMMAND2

                                     COMMAND1  || COMMAND2

                                     !COMMAND

举例:

                   当前用户为文件的属主,并且拥有读权限;

                   解:

                            [  -O FILE  ]  && [  -r  FILE  ]

 

                            第二种方式:

                                               (就是我们经常编写成测试表达式的时候,而不是命令)

                                     EXPRESSION1  -a EXPRESSION2  (与运算)

                                     EXPRESSION1  -o EXPRESSION2   (或运算)

                                     !EXPRESSION    (取反运算)

                                               上面的这个测试表达式有三种形式:

                                                        test  EXPRESSION1 -a  EXPRESSION2

                                                        [  EXPRESSION1 -a  EXPRESSION2  ]                                   

                                                        [[  EXPRESSION1 -a  EXPRESSION2  ]]

 

举例:

                   判断当前用户是否属于这个文件的属主,并且有执行权限

                   解:

                            [  -O FILE  -a  -x FILE  ]

 

 

练习:

                   将当前主机名称保存至hostName变量中;

                   主机名如果为空,或者为localhost.localdomain,则将其设置为www.mageedu.com

         解:

                   我们可以编写一个脚本

                            #!/bin/bash

                            hostName=$(hostname )

                            [  -z "$hostName"  -o  "$hostName"  == "localhost.localdomain" -o                            "$hostName"  == "localhost.localdomain" ]  &&  hostname                                                  www.mageedu.com

 

 

 

 

 

 

 

向脚本传递参数:

         常用的变量有五种类型:

                   局部变量

                   本地变量

                   环境变量

                  

                   位置参数变量

                   特殊变量

 

位置参数:

接下来就讲解一下位置参数变量和特殊变量,其实向脚本传递参数就是用位置参数变量来实现,位置参数变量是什么?简单的来讲,当我们去运行ls命令的时候,我们一般后面跟上一个目录,例如:“ls  /etc/var”那么这个“/etc/var”就是参数,那么我们在程序中我们如何知道对方给我们的参数,我们如何将我们的代码作用在我们的参数之上呢,就像ls这个命令的作者,再写ls时他必须能够从命令行获取用户传递过来的参数,并且能让自己内部的某些代码作用在这些参数上,而像这些传递给命令的参数,我们就称之为位置参数

         对于shell脚本来讲他们的确是通过位置来引用的。

 

举例:

         假如我们现在写一个脚本,脚本的名字就叫myscript.sh,并且向这个脚本中传递的第一个参数就是argu1,第二个参数是argu2......等等。那么我们就可以在脚本中直接引用,只要是用户传递的参数,我们就可以在脚本中直接引用他

         引用方式:

                   使用$1$2来实现的,当我我们给脚本的参数很多的时候,当达到两位数的时候,我们,需要给数字加上花括号比如:${10}${11}......

 

$1是整个脚本中位置参数中的第一个,我们传递的第一个参数他会自动的保存在$1中,这就跟我们之前讲到的反向引用一样,当我们需要向脚本中传递参数,一回车运行时,argu1就会自动的保存在$1中,argu2会自动的被保存在$2中,依次类推,那么此时的$1$2....这些就被称为位置参数变量,他们的确是可变的但是每一次运行脚本时,通过传递参数的变化,来改变这些变量的值。

 

举例:

         编写一个脚本,我们通过命令行随便给两个整数,我们能够求出这两个整数之和。

                   解:

                            我们编写的脚本的名字为“sum.sh

                            脚本的内容为:

                            #!/bin/bash

                            echo $[$1+$2]

                            保存这个脚本,下面我们运行一下,我们可以给这个脚本一个执行权限,我们也可以使用bash直接运行这个脚本后面跟上我们的参数

                            [root@centos6scripts]# bash sum.sh 2 6

                            sum=8

                            [root@centos6scripts]#

         轮替:

除此之外位置参数还可以做轮替

         shift [n]:位置参数轮替;(解释后面的字母n表示依次轮替多少个参数,n1shift就能够踢掉此前的对应位的第一个参数,n2表示剔除此前的前两个参数,但是shift不能指定是哪一位,n3就代表剔除前三个。)

                                    

 

         什么叫轮替嗯?

                   本来我们传递的第一个参数,就像刚才我们给定的第一个参数是2但是我们可以将这个2剔除,那么后面的6就变成了第一个了。

 

举例:

         仍然写一个脚本,这个脚本的名字就叫 shift.sh

                   脚本的内容是:

                   #!/bin/bash

                   #

                   echo  "First pos  argu:  $1"

                   shift

                   echo  "Second pos  argu:  $1"

我们运行上面的脚本并给这个脚本的参数为:one  two

                   [root@centos6scripts]# bash shift.sh onetwo

                   Firstpos argu:one

                   SecondPOS argu:

                   [root@centos6scripts]#

         分析:

                   其实我们执行上面的脚本时,我们两次其实都是运行的 $1”,也就是脚本的第一个位置参数,我们在脚本中执行“shift”命令时,其实我们往脚本中传送的第一个参数“one”其实就已经被踢掉了,那么我们再传递two时,其实还是保存在第一个位置参数$1中。那么以此类推我们想剔除几个就剔除几个。

                   我们也可以剔除两个,比如说我们先传递两个位置参数,然后我们将这两个都剔除,然后我们再传递一个参数,然后我们输出这个脚本的第一个位置参数。

                            #!/bin/bash

                   #

                   echo  "First and  Second  pos argu:  $1,$2"

                   shift  2             //表示我们这次剔除2个位置参数,如果我们不指定剔除几个,那么                                       shift默认是剔除1个。

                   echo  "Third pos  argu:  $1"

         执行脚本的结果显示我们位置1参数为3

                   [root@centos6scripts]# bash shift.sh 1 2 3

                   Firstand Second pos argu:1, 2

                   ThirdPOS argu:3

                   [root@centos6scripts]#

 

 

练习:

                   写一个脚本,通过命令行传递两个文本文件路径给脚本,计算空白行数之和;

                            解:

                   分析:我们看到“向脚本中传递参数”那么我们就应该想到引用位置参数。那么文件保存在$1$2后,接下来我们就使用这个参数,然后利用命令讲这个参数中的文件中的空白行取出,首先我们先读取这个文件我们才能知道有没有空行,然后我们使用过滤文本和正则表达式,将空行取出,然后计算

                   脚本的内容如下:

                   #!/bin/bash

                   file1=$(cat  $1 |  grep  "^$"  | wc  -l)

                   file2=$(cat  $1 |  grep  "^$"  | wc  -l)

                   echo  "sum_line=$[ $file1+$file2]"

 

                   执行这个脚本:

                   [root@centos6scripts]# bash file_line.sh/etc/fstab /etc/gdm/custom.conf

                   sum_line=8

                   [root@centos6scripts]#

 

                   老师的写法:

                   在求每个文件的空白行时是这样写的:file_lines=$(grep  "^$"  $1 |  wc  -l)

 

 

 

特殊变量:

         $0:脚本文件路径本身;

         $#:脚本参数的个数

         $*:所有参数;

         $@:所有参数;

                            "hello"   "hi"  "toyou"

                            "hello  hi toyou"

         注意:$*$@的不同之处在于,向脚本传递了n个字符串,那么有一个是将这n个字符串当成了一个整体,就像"hello  hi  toyou";另外一个是将这个n个参数分别当成不同的参数进行传递仅脚本中就像"hello"  "hi"  "toyou",但是对于我们现在的用法来讲这两种没什么不同,我们可以先不用注意。

举例:

         1$0表示是脚本名称,或者说脚本文件路径本身,假如我们给定一个脚本路径,假设这个脚本执行给的是全路径或者是“./”开始的,那么我们应该如何获取脚本文件自己的实际的文件名。

         我们编写一个脚本,脚本的名字为“filename.sh”然后该脚本的内容为:

                   #!/bin/bash

                   echo $0

                   保存退出后,然后我们给文件加上执行权限

             chmod  +x  filename.sh  

                   ./filename.sh                                                                        

                   [root@centos6scripts]# ./filename.sh

                   ./filename.sh       //这里显示的脚本文件路径为“./

                   [root@centos6scripts]#

         我们可以将上面的脚本内容改为

                   #!/bin/bash

                   basename  $0

再次执行这个脚本:

                   [root@centos6scripts]# ./filename.sh

                   filename.sh

                   [root@centos6scripts]#

 

 

         2)假设我们想知道我们一共向脚本中一共传递了多少个参数。我们可以使用“$#

编写一个脚本,文件名为“pos.sh”脚本文件内容:

                   #!/bin/bash

                   echo $1,$2

                   echo $#

执行上面的脚本:

                   [root@centos6scripts]# ./pos.sh 5 6

                   5,6

                   2

                   [root@centos6scripts]#

                   [root@centos6scripts]# ./pos.sh hello hi

                   hello,hi

                   2

                   [root@centos6scripts]#

 

 

有了上面的特殊变量,那么我们马上就能做一个判断了,前面我们写过一个脚本,要求用户输入两个文件的路径,然后求空白行之和,那么如果当用户只是输入一个路径怎么办?

                            技术分享

当我们只给一个路径然后执行这个脚本时,发现我们的脚本卡主了,因为在脚本中第二个命令grep无法运行了,那么这时候我们就可以要去用户至少给两个以上的参数,在运算之前就做判断了。

#!/bin/bash

#

#

[  $# -lt 2  ] && echo "At lease twofiles" && exit 1

file1=$(cat$1 | grep "^$" | wc -l)

file2=$(cat$2 | grep "^$" | wc -l)

echo"sum_line=$[$file1+$file2]"

执行脚本:

[root@centos6 scripts]# ./file_line.sh /etc/issue

At lease two files

[root@centos6 scripts]# echo $?

1                          //这里的1不是命令执行失败的那个1,这是我们自定义的1,表示脚本命令                                在这个地方结束了。

[root@centos6 scripts]#

 

 

过程式编程语言的代码执行顺序:

         1)顺序执行:逐条运行;

         2)选择执行:代码存在一个或多个分支,只执行一个分支;

                   代码有一个分支:条件满足时才会执行;

                   两个或两个以上的分支:只会执行其中一个满足条件的分支;

         3)循环执行:

                   代码片段(循环体)要执行01或多个来回;

                  

 

         选择执行:

 

         单分支的if语句:

         if  测试条件;then           //两个注意点:测试条件为真代码才能往下执行;要有分号,没                                                  有分号表示语法错误

                   代码分支

         fi

 

         上面的单分支语句还可以写成这样的格式:

         if  测试条件

         then

                   代码分支

         fi

 

         双分支的if语句:

                   if  测试条件;then           //测试条件为布尔型测试语句,要么为真要么为假,没有其                                                           他情况

                            条件为真时执行的分支

                   else

                            条件为假时执行的分支

                   fi

 

示例:

         通过参数传递一个用户名给脚本,此用户不存在时,则添加用户;

分析:

         我们写一个名字叫“useradd.sh”的脚本,通过脚本传递一个参数过来,不管传递的是什么,我们首先要判断用户是否存在,判断用户是否存在,我们现在有两种方法:一种是一个命令,另一种是一个表达式。如果是一个表达式,那我们就将其放在一个中括号中或者前面加test表示,怎样判断一个用户存在?无非就是有两种方法,一种是id  用户名,要是成功则说明这个用户存在,第二种就是grep这个用户名到/etc/passwd文件中

那么现在我们就使用在文件/etc/passwdgrep这个用户名,如果有结果表示存在。

         则我们的测试条件表达式就是这样写,用户名传递给脚本则保存在位置参数$1中,那么我们就过滤这个变量$1,而且这个$1是在行首,是一个单词,那么我们就单词尾部锚定grep "^$1\>"  /etc/passwd  这就是表示在文件中某一行行首的单词中有我们给定的这个用户名,则就表示这个用户存在,存在就不创建,但是我们现在要求是不存在为真。那么现在是存在为真,那么我们取反,就表示存在为假,那么也就是说不存在为真,所以测试条件因该这样写:!grep "^$1\>"  /etc/passwd 这个过滤的 结果对我们不重要,那么我们=就将这个结果输出到文件/dev/null中。

 

 

         综上所述:测试条件表达式为:!  grep "^$1\>" /etc/passwd  &>  /dev/null

所以我们的脚本内容为:

                   if !  grep  "^$1\>"  /etc/passwd &>  /dev/null;then  //特别注意叹号前后都                                                                                                                                         有空格

                            useradd   $1

                            echo "123456"  |  passwd --stdin  $1 > /dev/null

                            echo "Add  user  $1 finished."                         //添加用户完成后的提示信息

                   fi

 

上面的脚本还可以更加的完善,就是当用户运行上面的脚本时,压根就忘了传参数,怎么办?那我们就可以在if语句的前面再加上一个判断参数的测试语句;

                   [ $#  -lt  1 ]  &&  echo "plese  input  a username"  &&  exit  2

上面的这语句又可以写成一个条件判断语句:

         if [  $#  -lt 1  ];then

                   echo  "Please input  a  username!"

                   exit  2

         fi

 

执行上述脚本:

         [root@centos6scripts]# ./useradd.sh

         plese  input a  username

         [root@centos6scripts]# echo $?   //命令执行状态结果返回值是自己定义的2

         2

         [root@centos6scripts]#

 

 

 

 

 

 

 

上面的命令我们还可以使用双分支语法来写:

                   #!/bin/bash

                   #

                   if  [ $#  -lt  1 ];then

                            echo  "Please input  a  username!"

                            exit  2

                   fi

                   if  grep "^$1\>"  /etc/passwd  &> /dev/null;then

                            echo  "the user  $1  exists"

                   else

                            useradd  $1

                            echo  "123456"  | passwd  --stdin  $1 &>  /dev/null

                            echo  "Add user  $1  finished."

                   fi

                           

练习1:通过命令行参数给定两个数字,输出其中较大的数值;

         解:

                   #!/bin/bash

                   #

                   if  [ $#  -lt  2 ];then

                            echo  "Two integers."

                            exit  2

                   fi

                   if  [ $1  -ge  $2  ];then

                            echo  "Max number:$1"

                   else

                            echo  "Max number:$2"

                   fi

 

上面的代码echo语句重复太多,我们可以将其优化一下,代码前面先声明一个变量

                   #!/bin/bash

                   #

                   declare  -i  max

                   if  [ $#  -lt  2 ];then

                            echo  "Two integers."

                            exit  2

                   fi

                   if  [ $1  -ge  $2  ];then

                            max=$1

                   else

                            max=$2

                  fi

                   echo  "Max number:$max"

 

练习2:通过命令行参数给定一个用户名,判断其ID号是偶数还是奇数;

         解:

                  

练习3:通过命令行参数给定两个文本文件名,如果某文件不存在,则结束脚本执行;

                   都存在时返回每个文件的行数,并说明其中行数较多的文件;

 

 

 

 

 

 

 

 

 

 

 


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

bash脚本编程基础