shell脚本基础

一、shell脚本简介。

1.1    什么是shell,
        shell是一个命令解释器,他在操作系统的最外层,负责直接与用户对话,把用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果,输出到屏幕返回给用户,这种对话方式可以是交互的方式(从键盘输入命令,可以立即得到shell的回应),或者非交互式(执行脚本程序的方式)shell是命令解释器,是使用者和内核操作系统沟通桥梁。
1.2    什么是shell脚本
        当Linux命令或者语句不再命令行下执行(严格的说,命令行执行的语句也是shell脚本),而是通过一个程序文件执行时,该程序就称为shell脚本或shell程序,shell程序很类似DOS系统下的批处理程序(扩展名.bat),用户可以在shell脚本中敲入一系列的命令及命令语句组合,这些命令、变量和流程控制语句组合起来就形成了一个功能强大的shell脚本。
1.3    shell脚本在运维工作中的作用
        shell脚本擅长处理纯文本的数据,而Linux系统中几乎所有的配置文件,日志文件,如(nfs rsync httpd nginx lvs等)、多数启动文件都是纯文本类型的文件,因此,学号shell脚本,可以利用他在运维发挥巨大的作用。
        shell的调试:
        1、学习好脚本开发规范
        2、好的编码习惯,
                在掌握脚本调试方法之前,我们应可能的先学些脚本开发规范,从而减少陷入脚本调试的问题,达到未雨绸缪的效果,也就是老师常说的平时多学习好的习惯,注意规范只读,也体现了我们未雨绸缪的思想,常见的错误有如下几个:
                 1、if 条件语句缺失 fi  结尾关键字。
                 2、双引号没有对应上
                 3、中括号中 [] 用 <> 来比较
                shell脚本语法小结:
                1、重视书写习惯,开发规范和开发制度,尽量减少脚本调试的难度和次数,提高效率
                2、shell基本语法要熟悉,才能利用好脚本调试
                3、写脚本思路要清晰,
                    思考开发的框架,尽量模块化开发,复杂的脚本分段实现,
函数1
函数2
函数3
main()函数
main $*

  

 4、如果脚本不是你写的,或者是在windows下开发的脚本,你检查脚本明明没有问题,但就是执行出现错误,要想到执行dos2unix格式化一下。有一个好习惯是写脚本都执行dos2unix格式化一下。
                5、使用echo调试,可以在变量操作前后echo开输出调试变量结果,echo 结果然后退出 exit1 这样来调试逻辑错误。
                6、bash命令参数调试:
                        -n        不会执行该脚本,仅查询脚本语法是否有问题,并给出错误提示
                        -v        在执行脚本时,先将脚本的内容输出到屏幕上然后执行脚本,如果有错误,也会给出错误提示
                        -x        将执行的脚本内容及输出显示在屏幕上,这个是对调试很有用的参数。
                7、shell调试技巧小结:要记得dos2unix来格式化脚本,直接执行脚本根据报错来调试,有时报错不准确, sh -x调试整个脚本,显示执行过程,比较困难,set -x  set +x 调试部分脚本(要在脚本中设置),能缩小范围,echo 输出变量及相关内容,然后紧跟着exit退出,不执行后面程序的方法来跟踪脚本,对于逻辑错误比较好用,最关键的是要熟练语法,编码习惯,编程思想,将错误扼杀在萌芽志宏,降低调试负担,提高效率。
1.4    文件测试表达式
        我们在编程时需要处理一个对象时,需要先进行判断,只有符合要求,采取操作处理,这样做的好处就是避免程序出错以及无谓的消耗系统资源,这个要测试的对象可以是文件,字符串,数字等。
        在书写文件测试表达式时,常用如下:
常用文件测试操作符号 说明
-f     文件 英文file 文件存在且为普通文件则真,即测试表达式成立
-d    文件 英文 directory 文件存在且为目录文件则真,即测试表达式成立
-s    文件 英文size 文件存在且文件大小不为0则真,即测试表达式成立
-e    文件  英文 exist 文件存在则真,即测试表达式成立,只要有文件就行,
-r     文件 英文read 文件存在且可读则真,即测试表达式成立
-w   文件 英文write
文件存在且可写则真,即测试表达式成立
-x    文件 英文 executable 文件存在且可执行则真,即测试表达式成立
-L    文件 英文link 文件存在且为链接文件则真,即测试表达式成立
f1 -nt  f2  英文 newer than  文件f1比f2新则真,即测试表达式成立,根据文件修改时间计算
f1 -ot f2 英文 older than 文件f1比f2旧则真,即测试表达式成立,根据文件修改时间计算。
        普通文件测试表达式举例:
[root@localhost pandj]# touch pandj                             //创建一个测试文件
[root@localhost pandj]# [ -f pandj ]&&echo 1||echo 0            //需要注意[]中前后都有空格,意思是 pandj这个文件存在就输出1 不存在就输出0
1                                                               //返回1表示文件存在
[root@localhost pandj]# mkdir pandj.d                            //创建一个测试目录 
[root@localhost pandj]# ll
total 0
-rw-r--r--. 1 root root 0 Aug  8 10:28 pandj
drwxr-xr-x. 2 root root 6 Aug  8 10:33 pandj.d
[root@localhost pandj]# [ -d pandj.d ]&&echo 1 || echo 0          
1
//测试文件属性
[root@localhost pandj]# ll pandj
-rw-r--r--. 1 root root 0 Aug  8 10:28 pandj                    //查看文件具有可读可写的权限
[root@localhost pandj]# [ -r pandj ]&&echo 1||echo 0
1
[root@localhost pandj]# [ -w pandj ]&&echo 1||echo 0
1
[root@localhost pandj]# [ -x pandj ]&&echo 1||echo 0
0
[root@localhost pandj]# 
[root@localhost pandj]# file1=/etc/services                          //定义一个变量来举例、
[root@localhost pandj]# [ -f $file1 ]&&echo 1 || echo 0              //这里看的是file1这个变量对应的文件是否存在
1

  

 1.5    字符串表达式
        需要注意的是,字符串测试操作符号需要用""引起来,比较符号两端都有空格
常用字符串表达式操作符 说明
-z "字符串" 若字符串长度为0 则为真
-n "字符串" 若字符串长度不为0 则为真
"串1"="串2" 若字符串1等于字符串2,为真,可使用==代替 =
"串1"!="串2" 若字符串1不等于字符串2,为真
           字符串表达式举例:
[root@localhost pandj]# [ -n "pandj" ]&&echo 1 || echo 0
1
[root@localhost pandj]# [ -z "pandj" ]&&echo 1 || echo 0
0
[root@localhost pandj]# [ "pandj" = "pandj" ]&&echo 1||echo 0
1
[root@localhost pandj]# [ "pandj" != "pandj" ]&&echo 1||echo 0
0
[root@localhost pandj]# test=pandj
[root@localhost pandj]# echo $test
pandj
[root@localhost pandj]# [ $test = "pandj" ]&&echo 1 ||echo 0
1

  

 1.5    整数二元比较操作符
        在书写测试表达式时,可以用下表中的整数二元比较操作符:
在[]以及test中使用的比较符 在(())和[[]]中使用的比较符 说明
-eq == 相当
-ne != 不相等
-gt > 大于
-ge >= 大于等于
-lt < 小于
-le <= 小于等于
        整数二元比较符举例:
[root@localhost systemd]# [ 2 -eq 1 ]&&echo 1 ||echo 0
0
[root@localhost systemd]# [ 2 -gt 1 ]&&echo 1 ||echo 0
1
[root@localhost systemd]# [ 2 -lt 1 ]&&echo 1 ||echo 0
0
//提示: “=” “!=” 在[]中使用不需要转义,包含“>” “<”的符号在[]中使用需要转义,对于数字不转义的结果未必报错,但是结果可能不会对。推荐在使用过程中用 [] 及 -eq 这样的写法。 
//整数比较不要加双引号

  

1.5    逻辑操作符
        逻辑操作如下所示:
在[]和test中使用的逻辑操作符 在[[]]中使用的逻辑操作符, 说明
-a && and 与 两端都为真,则真
-o || or 或两端有一个为真,则真
! ! not 非,相反则为真。
        逻辑操作符举例:
[root@localhost systemd]# f1=/etc/rc.local;f2=/etc/services 
[root@localhost systemd]# [ -f "$f1" ]&&echo 1
1
[root@localhost systemd]# [ -f "$f2" ]&&echo 1
1
[root@localhost systemd]# [ -f "$f1" -a -f "$f2" ]&&echo 1||echo 0
1
[root@localhost systemd]# f2=/pandj/nofile
[root@localhost systemd]# [ -f "$f1" -o -f "$f2" ]&&echo 1||echo 0
1
[root@localhost systemd]# [ -f "$f1" -a -f "$f2" ]&&echo 1||echo 0
0

  

   逻辑操作符小结: [] 中就用 -a -o !  [[]] 中就用 && || ! test中的用法和[]相同,多个[]之间以及多个[[]]之间,或者任意混合中间逻辑操作符都是用 && ||
 
    1.6   分支和循环结构 //重要
        if语句是实际生产工作中最重要且最常用的语句,所以,if语句必须要掌握牢固
        if 语句的语法如下:
        1.6.1 if 语句的单分支结构:
if [ 条件 ]
    then
        指令
fi

//或者

if [ 条件 ];then        //比较常用
    指令
fi

  1.6.2  if 语句的双分支结构

if [ 条件 ];then
    指令1   
else
    指令2
fi

   1.6.3  if 语句的多分支结构

if [ 条件 ];then
    指令
elif [ 条件 ];then
    指令
else    
    指令
fi

  

  1.7   分支和循环结构 
            函数也就是具有和别名类似的功能:
  简单的讲,函数的作用就是把程序里面多次调用的相同的代码部分定义成一份,然后为这这一份代码起个名字,其他所有的重复调用这部分代码就只要调用这个名字就可以了,当需要修改这部分重复代码时,只需要改变函数体内的一部分代码即可实现所有调用修改,
           函数的优势:
            1、把相同的程序段定义成函数,可以减少整个代码的代码量
            2、增加程序的可读,易读性、以及可管理性。
            3、可以实现程序功能模块化,不同的程序使用函数模块化
            4、可以让程序代码结构更清晰
            强调:对于shell来说,Linux系统的200个命令都可以说是shell的函数。
            shell函数的语法如下:
                            简单语法格式:
函数名(){
    指令
    return n            //提示:shell中用exit来输出返回值,函数里用return来输出返回值。
}

            规范语法格式:

function 函数名(){
    指令
    return n               //提示:shell中用exit来输出返回值,函数里用return来输出返回值。
}

  

     shell函数的执行:
        1、直接执行函数名即可(不带括号)
                但是需要注意:
                a、执行函数时,函数后的小括号不要带了,
                b、函数定义及函数体必须在要执行的函数名前面定义,shell的执行是从上到下的按行执行的。
        2、带参数的函数执行方法:
                    函数名 参数1 参数2 
                说明:
                a、shell的位置参数($1  $2  $3 $# $* $? $@)都可以是函数的参数
                b、此时父脚本的参数临时地被函数参数所掩盖或隐藏
                c、$0 比较特殊,它仍然是父脚本的名称
                d、当函数完成时,原来的命令行脚本的参数即恢复
                e、在shell函数里面,return命令功能和shell里面的exit类似,作用是跳出函数。
                f、在shell函数体里面使用exit会推出整个shell脚本,而不是退出shell函数
                g、return 语句会返回一个退出值给调用函数的程序
                h、函数的参数变量是在函数体里面定义,如果是普通变量一般会使用 local i 定义。
    shell脚本中的自带参数:
                $# 是传给脚本的参数个数
                $0 是脚本本身的名字
                $1是传递给该shell脚本的第一个参数
                $2是传递给该shell脚本的第二个参数
                $@ 是传给脚本的所有参数的列表
              $*  是所有参数列表
        1.7   分支和循环结构之 case 语句详解
                case语句实际上就是规范的多分支If语句,
case "字符串变量"in
    值1)指令1
;;
    值2)指令2
;;
    *)指令3
esac

  

  case小结:
            1、case语句就相当于多分支if语句,case语句优势更规范,易读
            2、case语句适合变量的值少,且为固定的数字或者字符串集合,(1、2、3)或者(start、stop、restart)
            3、系统服务启动脚本传参的判断多用case语句,参考rpcbind/nfs/crond脚本
            4、所有的case语句都可以用if实现,但是case更规范清晰一些
            5、case 一般适合与服务的脚本启动。
            6、case 的变量的值如果已知固定的 /start/stop/restart元素的时候比较适合一些,
                语句小结:运维层面
            1、case主要是写启动脚本,范围较窄
            2、if 取值判断,比较, 应用更广。
 1.7   分支和循环结构之 while语句详解
            while循环在工作中使用的不多,一般是守护进程程序会用或者始终循环执行场景,其他循环计算,都会用for替换。
            while条件语句格式如下:
while 条件
    do
    指令
    done
            while是当型循环,需要了解一下sleep,sleep 1 休息一秒, 如果长达一分钟,可以使用定时任务管理,
            举例解释,没三秒输出当前系统的负载情况。
#!/bin/sh

while true
do
        uptime
        sleep 3
done
~      

  

     在脚本后面加 & 符的意思是后台执行脚本,在后台永久执行,我们称之为守护进程模式。
        同时,在脚本后面加&符也是方式客户端执行脚本中断的方法,防止客户端执行脚本中断的方法一共有三种:
        1、在脚本后面加 & 符,后台执行脚本
        2、nohup  /server/scripts/uptime.sh &       nohup 用户退出系统之后继续工作。
        3、screen 保持会话,有时间总结这个命令,
        下面举个脚本后台执行的例子。
[root@localhost scripts]# sh while-1.sh &
[1] 4776
[root@localhost scripts]# jobs                    //jobs可以显示后台程序
[1]+  Running                 sh while-1.sh &
[root@localhost scripts]# ps -ef | grep while-1.sh 
root       4776   2317  0 11:17 pts/0    00:00:00 sh while-1.sh
root       4815   2317  0 11:18 pts/0    00:00:00 grep --color=auto while-1.sh
[root@localhost scripts]# kill 4776

  命令补充:    

bg                    后台执行
fg                    挂起执行
jobs                  显示后台程序
kill killall pkill    杀掉进程
crontab               设置定时
ps                    查看进程
pstree                显示进程状态树
nice                  改变优先权,
nohup                 用户推出系统之后继续工作
pgrep                 查找匹配条件的进程
strace                跟踪一个进程的系统调用情况
ltrace                跟踪进程调用库函数的情况
vmstat                报告虚拟内存他哦那估计信息。

  

  while循环小结:   
           1、 while循环的特长是执行守护进程,以及我们希望循环不退出持续执行的场景,用于频率小于一分钟的循环处理,其他的while循环几乎都可以被我们即将要讲的for循环代替,
            2、case语句可以if语句代替,一般在系统启动脚本传入少量固定规则字符串,用case语句,其他普通判断多用if
            3、一句话,if for 语句最常用,其次是while(守护进程),case(服务启动脚本)
        各个语句使用场景:
            条件表达式,简单的判断,(文件是否存在,字符串是否为空)
            if 取值判断,不同值数量较少的情况
            for 正常的循环处理,最常用
            while 守护进程,无限循环sleep
            case服务启动脚本,菜单,
            函数,逻辑清晰,减少重复语句。
 
    1.7   分支和循环结构之  for  语句详解 (重要)
            for循环语法结构:
for 变量名 in 变量列表
do
    指令
done
提示。在此结构中in 变量取值列表可省略,省略时相当于 in $@ 使用for i 就相当于使用for i in $@

  

 提示:
                1、程序持续运行多用while,包括守护进程,还有配合read读入循环处理
                2、有限次循环多用for,工作中使用for更多。
 
        break continue exit return的区别。
            break n        n 表示跳出循环的层数,如果省略n表示跳出整个循环
            continue      n  n表示退出到第n层继续循环,如果省略n表示跳过本次循环,忽略本次循环的剩余代码,进入循环的下一次循环
            exit   n         退出当前shell程序, n 为返回值,n也可以省略,再下一个shell中通过$?来接收这个n的值
            retuen n         用于再函数里面,作为函数的返回值,用于判断函数执行是否正确。
 
1.8 shell数组,
            平时的定义 a=1 b=2 c=3,变量如果变多了,再一个个定义很费劲,并且取变量也很费劲,
            简单的说,数组就是各种数据类型的元素按一定顺序排序排列的集合,
            数组就是把有限个元素变量或数据用一个名字命名,然后用编号区分他们的变量的集合,这个名字称为数组名,编号称为数组下标,组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量
            由于有了数组,可以用相同名字引用一系列变量,并用数字(索引)来识别他们,在很多场合,使用数组可以缩短和简化程序开发,因为可以利用索引值设计一个循环,高效处理很多情况。
[root@localhost ~]# array=(1 2 3)                //定义一个简单的数组
[root@localhost ~]# echo ${#array[*]}            //取这个数组的值的个数。
3
[root@localhost ~]# echo ${array[0]}            //取数组的第一个值,需要注意数组的下标从0开始。
1
[root@localhost ~]# echo ${array[*]}             //查看数组中所有的值
1 2 3
[root@localhost file3]# array[3]=4                //给数组添加值
[root@localhost file3]# echo ${array[*]}
1 2 3 4
[root@localhost file3]# unset array[0]               //删除数组中的值
[root@localhost file3]# echo ${array[*]}
2 3 4
[root@localhost scripts]# array1=($(ls))             //可以将一个命令的返回结果放进数组里面
[root@localhost scripts]# echo ${array1[*]}
apple.sh case.sh color.sh file3 for1.sh for2.sh for3.sh fun01.8.9.sh httpd_check.8.8.sh lamp.8.8.sh lnmp.8.8.sh menu.sh nginx_admin.sh nginx_config.sh num100.sh num.8.8.sh url_check.8.9.sh while-1.sh youxiu.8.8.sh

  

            shell数组相关的知识小结。
            1、定义:
            静态数据:array=(1 2 3)
            动态数据:array=($(ls))
            给数组赋值:array[3]=4
            2、打印:
            打印所有元素: ${array[@]}  ${array[*]}
            打印数组长度 ${#array[@]} ${#array[*]}
            打印单个元素 ${array[i]}        i是数组的下标。
 
原文地址:https://www.cnblogs.com/pandj/p/9491786.html