开发者学堂课程【Linux Shell 编程:Linux Shell 训练营 Day3】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/856/detail/14110
Linux Shell 训练营 Day3
目录
一、Shell 脚本编码规范
二、Shell 脚本调试与排错
三、了解系统公共函数库
四、了解第三方公共函数库
一、Shell 脚本编码规范
如果是在公司或者是工作中正式的写 shell ,应该遵循编码规范,如果公司比较大,可能会有一个内部的编码规范,如果是小公司或者是自己做项目,可能没有一个完整规范说明。
1、脚本的命名和权限:
(1)文件后缀:一般 shell 脚本的文件的后缀都是.sh或者不用后缀。比如, linux 中很多系统里面的 shell 脚本,没有.sh 而是直接一个脚本名,然后去执行。
(2)文件名可读性:不能起乱七八糟的文件名,尽量简单,用小写的字母来取一个文件名,也可以用下划线或者驼峰命名法。
脚本的可执行权限:一般建议所有脚本都会给加上可执行权限,以避免在一些奇怪的情况下,要执行的时候,却发现没有权限执行。
脚本的结构:
(3)Shebang:一般第一行都是一个 Shebang,下面是脚本的正文。
函数:有两种命名规范。如果加 function 然后加个函数名是可以的,或者直接用函数名加一个语言括号来来定义一个函数也是可以的。一般来说,不用 function 这个关键字,如果想用也可以,在一个脚本中要尽量保持一致。
(4)注释: 对于一些复杂的功能,建议添加一些注释,在昨天作业中,有加一些注释,因为 shell 脚本语法很奇怪,如果是自己写过的脚本,过几天看,可能并不能马上看出来,如果加上一些注释会更容易的去读懂脚本。
2、脚本中的变量和函数的命名与引用:
(1)脚本中的变量和函数的命名格式:和脚本本身的命名是类似的,一般的命名规范是驼峰命名法或者下划线命名法。只要自己定一个规范,在整个脚本或整个项目中用一个规范就可以。
(2)引用方式:指变量的引用方式,一般引用变量,如果是简单的变量,可以直接在前面加$,如果是复杂的,特别是如果想输出一个字符串,而这个字符串中又包含了一个变量,这时建议用$加上()去把这个变量包围起来。
3、脚本中的相对路径和绝对路径:
(1)获取脚本执行路径的方法:一般执行脚本的时候,如果在脚本中用到了路径,建议都使用绝对路径,因为相对路径可能会有问题。比如已经准备好一个叫 getdir 的脚本。
执行这个脚本的时候输出脚本当前的执行目录。
当前目录是 root 的 day3下面,执行脚本是没问题的,它输出是正确的。但是如果在别的目录,用脚本的绝对路径去执行。
你会发现它其实输出的 pwd 是工作目录,这个和脚本的目录不是同一个目录。对于这种情况,要用绝对路径去把脚本的工作目录定义一下。经常使用的一个方法是用一个 dirname 命令获取到脚本的绝对路径。
它需要跟一个$0(脚本的名称),就是bash后面的第一个参数是$0,后面的参数是从$1、$2开始。dirname 是一个命令,所用$加圆括号括起来,作为一个命令执行,然后把它复制给一个变量 dir,一般 cd 到目录,之后再用 pwd 去获取这个脚本的路径。
这样不管是在当前目录去执行,或者是在别的目录通过绝对路径的方式去执行,输出的目录都是脚本所在的目录。
当前目录:
其他目录:
(2)脚本中日志的输出规范:在别的编程语言中,输出日志一般都有相应的库,可以输出日志的级别,日志的各种信息,然后统一输出到一个文件里面。在需要脚本中,可以自己定义一个日志输出规范,比如用 java 的格式先输出日期,然后日志级别,然后再输出日志信息,可以通过函数封装的方式来对日志输出进行规范。
下面来看一下 google 的 shell 的风格指南。在 google 除了有 C++,Python, Objective-C,还有 shell 的风格指南。
里面写了什么时候使用shell脚本,一般情况下,shell仅仅被用于小功能或者是简单的包装脚本,一般情况下它的功能不会特别复杂,一般都是实现某一个特定的功能,用shell脚本会特别简单,因为 shell 脚本可以包含很多 shell 命令。
如果对于一个小的功能,用 Python 或者 C 或者 java,都会特别复杂,用 shell 可能两行或者几行就可以实现。在 google 中也会有非常多的 shell 脚本,公布了 shell 的语法规范和风格规范,一般写完最上面的第一行是 bin,后面会对这个脚本的功能进行描述,一般是用#作为注释,然后后面去写脚本的功能,有时候会在后面加上它的作者,是谁写,哪一天更新。对于函数来说,最好把函数的变量、参数以及它的返回值也通过注释的方式描述清楚。还包括它的格式,就是一行写多少字符,循环是放一行还是放两行,这个都有一个固定的规范,可以不遵循它的规范,如果有自己的想法,整个脚本的格式保持一致就可以。
4、常见问题:
(1)特殊字符问题:
特别是复文本编辑框里面,从复文本编辑框复制出代码的时候,一定要注意它可能包含的特殊字符,特殊字符在 shell中就是在这个 vim 中,可能是不可见字符,执行的过程中,shell 警示器会报错。这时,可以去用 cat-a 或者用 od 这样的一个16进制的命令去看字符是否有问题。
如果发现脚本语法没有问题,但它执行有问题,可能就是特殊字符的问题。
有两种问题,一个是从复文本编辑框复制的时候,比如网页、邮件,特别是邮件,很容易有这种问题,复制出来的时候,它不是 windows 和 linux 换行符的问题,就是不可见的特殊字符。
当然,换行符是另外一个问题,就是 windows 和 linux 的格式不一样,一个是-r-n,一个是-n,这是另外一个问题,这种一般不影响脚本运行。
(2)环境变量:有些脚本在命令行执行的时候是没有问题的,但是如果要让它在crontab中定时执行,发现它执行有问题,这可能跟环境变量有关系,因为在命令行的时候,其实有一些环境变量,然后会默认使用这些环境变量去执行,但是 crontab 可能不会加这些环境变量。
那么如果要在 crontab 中正确运行的话,需要在脚本中把这些环境变量加上。有几种加法,一种是直接把 pass 就是 ph 这个环境变量写在脚本的最开始。还有一种是直接去引用 etc 下面的 profile 文件,一般一些环境变量的定义在这个脚本里面。
二、调试与排查
(1)bash-n:可以用来检查语法问题。比如刚才的脚本,如果有语法问题,比如在 while 后面加一个方括号。
一般来说,这个是不正常的。用 bash-n 试一下,没有检查出这个错误。这个是不好的一点,有一些语法错误它检查不出来,有一些可以。
比如把 do 改成 da,它发现 while 必须和 do 一起用,如果不对会报一个错误。
(2)bash-x:可以运行的过程输出出来,比如昨天的作业,把转至一个文件的脚本来运行一下,第一行是arr是一个定义空数组,然后显示了它开始定义一个空数组,下面它会依次的把脚本的每一行的执行过程列出来,这样就可以看到这个脚本的实际工作情况。
一般会在脚本报错的的时候,用这个来跟踪,看一下在哪个地方报错,然后可以对这个脚本进行修改。现在把刚才的脚本恢复成原来的样子,把错误的语法删掉,然后再去执行一下,可以发现它把每一个循环的执行过程全部列了出来。
三、系统的公共函数库
在 etc 的 init.d 的这个目录下,有一个 function 的文件
目录下面有一 个 functions 文件,还有一个 aegis,netconsole 和 network 三个程序。
然后看一下 network,它是 linux 内部管理网络的一个脚本,可以学习一下它的注释。
首先注释注明了它的功能,然后注明了脚本的描述。它把 info 就是脚本的信息都用注释写了出来。
再下面有一行命令是./etc/init.d/functions,指这个文件里面的内容引用到脚本中,其实它就是调用了 functions 系统公共库。系统公库里面定义了很多公共的函数,供其他的系统或程序去调用。
比如 action 函数。如果上面这个脚本,这一行命令上面执行的程序所执行的命令是正确的,它可以输出一个 ok,如果执行错误,它可以输出一个 failed。通常可以在系统特别是网络进行重启的时候,看到这样的输出。
演示:
用 source 或者是点都可以调用系统公共的函数库。然后加上.etc/init.d/functions,后面用 action 函数,action 函数后面跟两个参数,第一个参数是它输出的内容,第二个参数可以认为是一个返回值,true or false 或者是0或1,那么如果是 false,它在左边输出一个字符串 do something,右边输出一个 failed。
如果把它改成 true,再去执行,可以看到右边输出一个 ok。
一般重启网络或重启网卡的时候,它会这样输出,其实就是用了系统公共函数库里的一个函数来实现这样的功能。
当然,还有第三方的公共函数库,举两个例子,一个是 bash shell function library,还有一个 bash lib 两个公共函数库。
(1)bash shell function library:提供了如数组操作、命令执行、文件管理、日志记录、信息提醒、网络检测、字符操作、时间操作、变量操作等功能,可帮助运维工程师快速完成自己的脚本编写工作。
项目地址:https://github.com/SkypLabs/bsfl
例子:log.sh 用 dir 定义了一个脚本的路径,declare-r 表示变量是只读的,后面不可以修改。Source 了以 dir 为 base目录的上一层再上一层的 lib 下的 bsfl.sh,其实是把实际的脚本引入进来。后面的 msg 是公共函数库里的函数。
执行过后,观察格式,前面是时间戳。如果需要更详细更完整的格式,可以自己去修改封装的函数。
(2)Bash lib:一个原子化的公共库,可以根据自己的需要,引入所需的公共库分组,使用相应的内容,降低整个项目的大小。Bash lib 提供了诸多原子库,包括参数处理、日历处理、Hash 处理、帮助处理、列表处理、交互处理、文字处理、测试处理、时间处理等功能。
项目地址:https://aks.github.io/bash-lib/
四、第三方函数库
和上面的函数库不一样的地方,这是一个原子库,它把所有内容它分了类。
比如 utils 的脚本其实就是函数库,grep 一下。
这些都是按照功能去划分的,比如要处理参数,就用这个 arg-utils.sh,要处理数组,用 list-utils.sh。用的时候就直接在脚本前面用 source 包含库文件的绝对路径就可以使用其中的函数。
介绍的两个函数库都是有一定复杂度的,建议如果使用的话可以先了解一下它里面的函数是如何封装,如何定义的,然后,把它这个函数直接挪过来,自己去定义一个自己的函数库,从而可以对里面的代码非常熟悉,如果自己写,会更方便自己维护,但如果用它这个函数护理的东西,还要去看它的手册,它的用法,如果有问题可能不好办,因为需要去排查它是函数库的问题还是自己使用上的问题。
用于 shell 脚本的测试,其实专门有一个测试的库,但一般默认情况下系统上不安装这个。