初识shell脚本编程

shell脚本基础

shell脚本是包含一些命令或声明,并符合一定格式的文本文件

一般格式要求:

1.首行shebang机制

#!/bin/bash

#!/bin/python

#!/usr/bin/perl

2.注释信息

以#开头,可写明脚本用途,作者,版本,时间等

3.脚本正文内容,

创建shell脚本

以bash为例

  1. 第一行定格#!/bin/bash

  2. 注释信息:

    1. #用途

    2. #作者 

    3. #版本

    4. #时间

  3. 脚本正文内容,保存退出

  4. 给予脚本文件执行权限,

    1. 在命令上执行脚本的绝对路径或相对路径

    2. 直接运行解释器,将脚本作为解释器程序的参数运行

脚本范例

blob.png

blob.png

脚本调试

当我们写完脚本时,可以使用指令检测脚本中的语法错误

   bash  -n  /path/to/some_script  ==>检测错误 

   bash  -x  /path/to/some_script  ==>调试执行

blob.png

blob.png

变量  :命令的内存空间

变量命名法则:

  1. 不能使用程序中的保留字:例如if,for ==>不能使用系统自带的变量名

  2. 只能使用数字,字母及下划线,且不能以数字开头

  3. 见名知意

  4. 统一命名规则:驼峰命名法

变量种类


本地变量:生效范围为当前shell进程;对当前shell之外的其他shell进程,包括当前shell的子shell进程均无效

blob.png

blob.png

blob.png

说明:脚本file1.sh定义变量num1,且脚本file2.sh是其子进程,脚本file2.sh中定义变量num2

运行file2.sh,变量num2有效显示,变量num1未显示,运行file1.sh,

变量num1有效显示,变量num2未显示,子进程file2.sh运行,变量num2有效显示,变量num1未显示

由以上可知,以上变量只在当前进程有效

环境变量:生效范围为当前shell进程及其子进程

blob.png

blob.png

blob.png

说明:在上面本地变量的基础上将变量类型改变成环境变量,执行效果也有差异

file2.sh是file.1sh的子进程,file1.sh中定义的变量file2.sh在作为其子进程运行时,

是有效果的,但是在file2.sh不作为file1.sh的子进程运行时,是没效果的,看单独运行

file2.sh中只有变量num2,而在运行file1.sh时,num2没有继承其子进程的变量

所以:生效范围为当前shell进程及其子进程,其父进程也是不生效的

局部变量:生效范围为当前shell进程中某代码片段(通常指函数)

位置变量:$1,$2,…..来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数

特殊变量$?,$0,$*,$@,$#

本地变量:

变量赋值:name=‘value’

  1. 可以直接是字符串;name=“root”

  2. 变量引用:name="$USER"

  3. 命令引用:name=`command` ,name=$(command)

变量引用:${name} ,$name

    " " :弱引用,其中的变量引用会被替换为变量值    

   ‘ ’:强引用,其中的变量引用不会被替换为变量值,而保持原字符

显示已定义的所有变量:set

删除变量: unset  name

环境变量

变量赋值:    

    export name=VALUE

    declare -x name=VALUE

变量引用:${name} ,$name

显示所有环境变量:

    export

    env    

    printenv

删除变量: unset name

bash内建的环境变量:PATH,SHELL,USER,UID,HISTSIZE,HOME,PWD,OLPWD,PS1,HISTFILE

只读变量:只能用,不能修改和删除

readonly  name

declare  -r  name

可用来定义一些固定的值,直接引用,例如π

位置变量:在脚本代码中调用通过命令行传递给脚本的参数

$1,$2,…:对应第1,2,….等参数

$0 :命令本身

$* :传递给脚本的所有参数,全部参数何为一个字符串

$@ :传递给脚本的所有参数,每个参数为独立字符串

$# :传递给脚本的参数的个数

备注:$@ $* 只在被双引号包起来的时候才会有差异(后面会专门验证)

算数运算:

bash的算术运算: +,-,*,/,%取余,**(乘方)

实现算术运算:

  1. let var=算术表达式

  2. var=$[算术运算]

  3. var=$((算术运算))

  4. var=$(expr arg1  arg2  arg3 ….)

  5. declare -i var  =  数值

  6. echo  `算术表达式` |bc

乘法符号游戏场景中需要转义:\*

bash 有内建的随机数生成器:$RANDOM (1-32767) )

    echo $[$RANDOM%50]  :0-49 之间随机数

blob.png

赋值

增强型赋值:

    变量做某种算数运算后会存至此变量中

    i=$[$i+1] ==>每运行一次此命令,变量i的值会增加1

    let i+=1 同上

    let i+=2每次增加2

    let(+=,-=,*=,/=,%=)

自增:

VAR=$[$VAR+1]

let VAR+=1

let VAR++

自减:

VAR=$[$VAR-1]

let VAR-=1

let VAR–

blob.png

逻辑运算

true :1   false:0

与:

    1与1=1

    1与0=0

    0与1=0

    0与0=0

或:    

    1或1=1

    1或0=1

    0或1=1

    0或0=0

非:!

    !1=0

    !0=1

短路运算:

    短路与:

        第一个为0,结果必定为0

        第一个为1,第二个必须要参与运算

    短路或:

        第一个为1,结果必定为1

        第一个为0,第二个必须要参与运算

异或:^

    异或的两个值相同为假,不同为真

聚集命令

复合式:date;who| wc -l

    命令会一个接一个的运行

子shell:(date;who|wc -l)>>/tmp/trace

    所有的输出都被发送给单个的STDOUT和STDERR

退出状态

进程使用退出状态来报告成功或失败

0:成功

1-255:失败

$? :变量保存最近的命令退出状态

 blob.png

退出状态码

bash自定义退出状态码

    exit [n] :自定义退出状态码

ps:脚本中一旦遇到exit命令,脚本会立即终止;终止状态取决于exit命令后面的数字

   如果未给脚本指定退出状态码,整个脚本的推出状态码取决于脚本中执行的最后一条命令的状态码

条件测试

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

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

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

            0:成功    

            1-255:失败

        2.测试表达式

            test expression  比较大小

            [ expression ]    

            [[ expression ]]

        注意:expression两端必须有空白字符,否则为语法错误

    

    根据退出状态而定,命令可以有条件的运行

        && 代表条件性的AND THEN

        || 代表条件性的OR ELSE

        

        短路与: command1&&command2

                如果command1成功,执行command2

                如果command1失败,不执行command2

  blob.png

        短路或:command1||command2

                如果command1成功,跳过command2

                如果command1失败,将执行command2

blob.png                

test

长格式

test "$A" == "$B"&& echo "xiangdeng"

test "$A" -eq "$B" && echo "budengde"

简写格式

[ "$A" ==  "$B" ]&& echo "xiangdeng"

[ "$A" -eq "$B  ]&& echo "budengde"

blob.png

bash 测试类型

数值测试:

-gt:是否大于

-ge:是否大于等于

-eq:是否等于

-ne:是否不等于

-lt:是否小于

-le:是否小于等于

blob.png

字符串测试

== :是否等于

>  :ascii码是否大于ascii码

<  :ascii码是否小于ascii码

!=:ascii码是否不等于ascii码

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

注意:此表达式一般用于[[ ]]中

-z “string”:字符串是否为空,空为真,不空为假

-n “string”:字符串是否不空,不空为真,空为假

注意:用于字符串比较时的用到的操作数都应该使用引号

[root@localhost ~]# A=bbb
[root@localhost ~]# B=aaa
[root@localhost ~]# C=aaa
[root@localhost ~]# [[ "$C" == "$B" ]]&& echo right
right
[root@localhost ~]# [[ "$C" == "$A" ]]&& echo right
[root@localhost ~]# [[ "$B" == "$A" ]]&& echo right
[root@localhost ~]# [[ "$B" == "$A" ]]&& echo right||echo wrong
wrong
[root@localhost ~]# [[ "$B" == "$c" ]]&& echo right||echo wrong
wrong
[root@localhost ~]# [[ "$B" == "$C" ]]&& echo right||echo wrong
right
[root@localhost ~]# [[ "$A" == "$C" ]]&& echo right||echo wrong
wrong

文件测试

存在性测试

-e file:文件存在性测试,存在为真,否则为假

-a file:同-e

存在性及类别测试

-b file :是否存在且为块设备文件

-c file :是否存在且为字符设备文件

-d file :是否存在且为目录文件

-f file :是否存在且为普通文件

-h file 或者-L file:存在且为符号链接文件

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

-S file : 是否存在且为套接字文件

blob.png

文件权限测试:

-r file:是否存在且为可读

-w file: 是否存在且为可写

-x file: 是否存在且为可执行

文件特殊权限测试

-g file :是否存在且拥有sgid权限

-u file :是否存在且拥有suid权限

-k file :是否存在且拥有sticky权限

blob.png

文件大小测试

-s file :是否存在且为非空

blob.png

文件是否打开

-t fd :fd表示文件描述是否已经打开且与某终端相关

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

-O file:当前有效用户是否为文件属组

-G file: 当前有效用户是否为文件属组

双目测试

file1 -ef file2 :file1与file2是否指向同一个设备上的相同inode

file1 -nt file2: file1 是否新于file;

file1 -ot file2: file1 是否旧于file; ;

blob.png

组合测试条件

第一种:

    command1$$command2 并且

    command1||command2 或者

    !command 非

    例如[[ -e file ]] &&[[ -r file  ]]

第二种:

    expression1  -a  expression2 并且

    expression1  -o  expression2 或者

    !expression

     必须使用测试命令进行

关于位置变量总结 

1.位置变量中$@和$*的区别

当$@和$*不使用“”包起来时效果一样

blob.png

blob.png

当$@和$*用“”包起来时,执行$@正常运行

但是执行$*时报错显示的是没有“a b”这个文件后目录,

说明使用“”时,$*是把所有参数当作一个字符串的

$@则是每个参数都是分开的

blob.png

blob.png

$@和$*换个顺序看看有影响没

blob.png

blob.png

结果一样,所以上面结论成立

上课的时候老师好像不是这样验证的,我尝试下老师的方法

首先我创建了三个脚本,内容如下分别是t2.sh,t3.sh,t1.sh

其中t1.sh是t2.sh,t3.sh子脚本(后面简称脚本1.2.3)

blob.pngblob.pngblob.png

我们先看下运行三个脚本结果如何

运行脚本1时,1.2.4命令正常执行,说明命令和参数都没有问题,从其结果看也是正确的

命令3执行时显示的的是“a b c”这个文件,这是把a b c当作的是一个参数,和上面的结论一样的

这样说明了$*是把多个参数当作一个字符串的

blob.png

运行脚本3时,命令1.2正常执行,且结果正确,然后执行子脚本t1.sh参数是$*

但是t1第一行显示的a b c,t2第二行命令没显示,说明第二行没参数,所以没显示

t1第三四行显示正常结果,再次证明t1.sh运行时只有一个参数“a b c”

blob.png

在运行脚本2

所有参数都是正常执行,

blob.png

在强调一下结论:

     在被双引号包起来时 $*传递给脚本的所有参数,全部参数合为一个字符串,变成一个参数    

     $@不受影响,每个参数依然是独立的

$#:传递到脚本的参数个数

$0:命令本身

$$:脚本运行的当前进程ID号

$!:后台运行的最后一个进程的进程ID号

$?:显示最后命令的退出状态结果

    0:成功    

    1-255:失败

练习


1、编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小。

脚本内容如下:

1 #!/bin/bash
  2 #The current host system information
  3 echo -e "hello,everyone, \nThe current host system information is as follows"
  4 echo -e " "
  5 echo "hostname :`hostname`"
  6 echo ""
  7 echo "ip:`ifconfig |head -n2|tail -n1|tr " " ":"|tr -s ":"|cut -d: -f3`"
  8 echo ""
  9 echo -e  "system version:`cat /proc/version`"
 10 echo ""
 11 echo "kenel version:`uname -r`"
 12 echo ""
 13 echo -e "CPU Model:`lscpu |grep "Model name"|cut -d: -f2|sed 's@^[[:space:]]\+@@'`"
 14 echo ""
 15 echo -e "the memory capacity :`free -h|tr " " ":"|tr -s ":"|head -2|tail -1|cut -d: -f2`"
 16 echo ""
 17 echo -e "hard drive size:`fdisk -l |head -2|tail -1|cut -d: -f2|cut -d, -f1`"

运行结果如下:

blob.png

2、编写脚本/root/bin/backup.sh,可实现每日将/etc/目录备份到/root/etcYYYY-mm-dd中

脚本内容如下

 1 #!/bin/bash
 2 #discription:wen jian bei fen
 3 cp -a  /etc/  /root/etc`date +%Y-%m-%d`

因为暂时还没学到每日实现,只能做到手动备份,结果如下

blob.png

3、编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利用率最大的值

指令如下

 1 #!/bin/bash
  2 #ci pan li yong lv
  3 df|grep "sda"|tr " " ":"|tr -s ":"|cut -d: -f5|sort -rn|head -n1|tr -d "%"

结果:

blob.png

4、编写脚本/root/bin/links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序

命令如下

1 #!/bin/bash
  2 netstat -nt|grep tcp|tr " " ":"|tr -s ":"|cut -d: -f6|sort |uniq -c|sort -rn

blob.png

5、写一个脚本/root/bin/sumid.sh,计算/etc/passwd文件中的第10个用户和第20用户的ID之和

命令如下:

 1 #!/bin/bash
  2 user1_id=`cat /etc/passwd |head -10|tail -1|cut -d: -f3`
  3 echo "the 10 user_id: $user1_id"
  4 user2_id=`cat /etc/passwd |head -20|tail -1|cut -d: -f3`
  5 echo "the 20 user_id: $user2_id"
  6 sumid=$[$user1_id+$user2_id]
  7 echo "the sumid:$sumid"

结果:

blob.png

6、写一个脚本/root/bin/sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和

空行和空白行表示的应该是不一样的

空行:^$

空白行:^[[:space:]]*$space

指令1如下

 1 #!/bin/bash
  2 num1=`cat /etc/rc.d/init.d/functions |grep "^[[:space:]]*$"|wc -l`
  3 echo "the 1st is: $num1"
  4 num2=`cat /etc/fstab |grep "^[[:space:]]*$"|wc -l`
  5 echo "the 2st is: $num2"
  6 sumspace=$[$num1+$num2]
  7 echo "the sum is: $sumspace"

blob.png

指令2如下

  1 #!/bin/bash
  2 num1=`cat /etc/rc.d/init.d/functions |grep "^$"|wc -l`
  3 echo "the 1st is: $num1"
  4 num2=`cat /etc/fstab |grep "^$"|wc -l`
  5 echo "the 2st is: $num2"
  6 sumspace=$[$num1+$num2]
  7 echo "the sum is: $sumspace"

blob.png

7、写一个脚本/root/bin/sumfile.sh,统计/etc, /var, /usr目录中共有多少个一级子目录和文件

命令:

  1 #!/bin/bash
  2 num1=`ls -A /etc|wc -l`
  3 echo "etc: $num1"
  4 num2=`ls -A /var|wc -l`
  5 echo  "var: $num2"
  6 num3=`ls -A /usr|wc -l`
  7 echo  "usr :$num3"
  8 sumfile=$[$num1+$num2+$num3]
  9 echo "the sumfile: $sumfile"

blob.png

8、写一个脚本/root/bin/argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数

命令:

1 #!/bin/bash
2 num1=1
3 [[ $# -lt  $num1  ]]  &&( echo "zhishaoyinggai geiyige canshu" ;exit)|| grep "^$" $1|wc -l

blob.png

9、chmod -rw /tmp/file1,编写脚本/root/bin/per.sh,判断当前用户对/tmp/fiile1文件是否不可读且不可写

命令

 1 #!/bin/bash
  2 [ -r /tmp/file1  -o  -w /tmp/file1 ]&&echo 读写至少一个 ||echo 不可读且不可写

blob.png

10、编写脚本/root/bin/nologin.sh和login.sh,实现禁止和充许普通用户登录系统。

说明下,当/etc/nologin文件存在时,用户是禁止登录的,知道了这个上面的就简单了

禁止登录

 1 #!/bin/bash
 2 [ -f /etc/nologin ] && echo "user cannot login" || (touch /etc/nologin; echo "user cannot login")

blob.png

允许登录

 1 #!/bin/bash
 2 [ -f /etc/nologin ]&& (rm -rf /etc/nologin ; echo "user can login") ||echo  "user already login"

blob.png

11、写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,先判断是否合格IP,否,提示IP格式不合法并退出,是,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”

命令:

1 #!/bin/bash
2 ping -c1 -w1 $1 &>/dev/null &&echo "the ip can access" ||echo "the ip cannot access"

blob.png

12、计算1+2+3+…+100的值

1 #!/bin/bash
  2 echo the sumnumis: `echo {1..100}|tr " " "+"|bc`

blob.png

13、计算从脚本第一参数A开始,到第二个参数B的所有数字的总和,判断B是否大于A,否提示错误并退出,是则计算之

1 #!/bin/bash
 2 [[ $1 -lt $2  ]] && seq -s+ $1 $2|bc || (echo "error" ;exit)

blob.png

原创文章,作者:qiuwei,如若转载,请注明出处:http://www.178linux.com/33529

评论列表(1条)

  • 马哥教育
    马哥教育 2016-08-16 14:18

    总结的很好很详细,态度认真,内容充实,排版精美,再接再厉。

联系我们

400-080-6560

在线咨询

工作时间:周一至周五,9:30-18:30,节假日同时也值班

QR code