一、Shell脚本
Linux系统中的shell是一个特殊的应用程序,它介于操作系统内核与用户之间、充当一个“解释器”的角色负责接收用户输入的操作指令并进行解释将需要执行的操作传递给内核执行并输出执行结果。
编译器与解释器
基本编程语言分为这三类:机器语言、汇编语言、高级语言。高级语言按照类型分,又分为静态语言和动态语言。但是不管静态语言还是动态语言根据编程方式又分为面向过程编程和面向对象编程。
静态语言
编译型语言;程序执行前需要使用编译器事先转换成可执行格式;属于强类型变量,就是指变量在使用前必须提前声明甚至还需要初始化(初始化值为0或1)
编译型语言如C、C++、JAVA、C#等等。
动态语言
解释型语言;程序利用解释器边解释边执行,不需要提前编译;属于弱类型变量,就是指变量用时声明甚至不需要定义类型(Shell中默认都是字符串)
解释型语言如asp、asp.net、shell、php、python、perl。
面向过程
面向过程:就是把编程立足点主要用于问题解决过程本身;也就是以指令为中心,由指令处理数据,如C、shell。
面相对象
面相对象:就是把我们要实现的项目抽象为一个个对象来完成,以数据为中心,所有代码都围绕数据展开,如java、python(Python既是面向过程又是面向对象)。
二、Shell编程基本格式
1 2 3 |
1 #!/bin/bash 2 #This is a shell-script 3 echo "Hello world!" |
第1行很重要,它给shell一个很重要的线索,告诉它用什么程序来解释这个脚本,在这个列子中用的是/bin/bash。其他脚本语言例如perl、awk、python等都是采用这种机制。
第2行是注释,在#符号后面的东西,bash都视而不见给忽略掉。如果你的脚本很复杂的话,注释还是很重要的。
第3行就是shell脚本要执行要解释的指令了。
现在有个问题,第一行的#很重要,第二行的#号是注释。都是“#”号开头的东西,区别在于位置和“!”号,第一行的#号后面接着一个“!”号,这个就是脚本解释程序的声明指令,由调用这个脚本的shell来检测。它仅在脚本程序的第一行有效。
三、执行脚本的方式
1 2 3 4 5 6 7 8 9 |
$ bash [OPTIONS] firsh.sh -n:测试脚本是否有语法错误 -x:显示脚本执行的详细过程 # 直接执行脚本文件,脚本需要有执行权限; $ /etc/rc.d/init.d/network #在当前路径下执行脚本,脚本需要有执行权限; $ ./firsh.sh |
四、读取配置文件
在红帽系列的发行版中,一些服务脚本都存放在/etc/rc.d/init.d/目录下,其次脚本也可以有配置文件,都在/etc/sysconif/目录下存放。在Linux中环境变量也有相对应的配置文件。有时候经常会遇到修改这些配置文件或在脚本中读取这些配置文件,那么可以使用指令“source”或“.”进行读取这些配置文件,如下:
1 2 3 |
$ source /etc/sysconfig/* 或 $ . /etc/sysconfig/* |
另外在一个脚本中除了读取配置文件外,还会经常需要调用其他的脚本,这个时候只需要在脚本中直接执行需要调用过的脚本文件即可,如:
1 |
. /etc/rc.d/init.d/functions |
在Linux中对于“.”的含义有很多,比如有的表示隐藏文件;有的表示当前目录;有的用来执行当前脚本程序或文件等等。
五、变量
变量:就是一个容器用来存储数据也是一段内存空间(内存是编制的存储单元);通过变量赋值在变量中存储数据,然后可以通过变量名访问到的存储信息的内存地址。
数据类型:用来事先定义数据的存储格式和存储长度如10用字符表示来存储占2B而用数值来表示1010二进制占4bit
数据类型的重要性:有一种系统攻击叫缓冲区溢出,如声明一个数据类型为整型并申请1字节空间。当在这个变量中存储256时,这个整型数据时就会产生溢出,因为1字节存储单元的存储范围是0-255。而256就存储不了也就会产生溢出占用其他进程空间,解决缓冲溢出最简单的方法就是当用户存储一个数据时先检查数据是否可以存储下。
对于解释型语言来说,它的数据类型都是弱类型就是不管变量中的数据有没有类型都可以因为解释器能理解;由解释器在另外一个层次给予避免。
对于编译型语言来说,一旦数据类型出了问题只能靠程序自身来解决,它没有额外的一层保护机制,编译器也可以在编译时检查明显错误,但是对于后期用户输入进来的数据就无法检查了,所以但凡编译型语言都是强类型编译必须事先严格定义变量中的数据类型。
常用的数据类型有字符型,数值型,时间日期型,布尔型等等。
六、Bash变量分类
本地变量
本地变量作用域只对当前shell进程生效,Bash默认变量都是本地变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 定义本地变量,其中set可以省略; $ set VAR_NAME=value # 引用变量,其中{}一般可省略,直接echo $VAR_NAME; $ echo ${VAR_NAME} # 引用变量,引用变量后可跟字符串; $ echo $VAR_NAME:/PATH # 撤销变量; $ unset VAR_NAME # 查看所有变量; $ set |
局部变量
局部变量作用域对当前代码段有效,如在一个脚本函数中的变量跟函数外的变量名同名,就可以把函数内的变量定义为局部变量,这样就不会跟其他变量冲突。
1 2 |
# 定义局部变量 $ local VAR_NAME=value |
环境变量
环境变量用来定义每一个用户的操作环境,变量作用域只对当前用户shell进程及其子shell生效,并且机器重启变量失效。如我们常用的PATH变量就是一个环境变量,但是我们发现PATH变量重来没有失效过啊,不管机器有没有重启。那是因为针对环境变量有特定的环境变量配置文件,每一次用户登录就会加载此配置文件,同理在此文件中的变量就会生效。(后面会详细介绍环境变量配置文件)
1 2 3 4 5 6 7 8 9 10 |
# 定义环境变量,定义不存在的变量; $ export VAR_NAME=value # 定义环境变量,定义已存在的变量; $ export VAR_NAME # 查看当前系统环境变量命令: $ printenv $ env $ export |
位置变量
1 2 3 4 5 |
$0 #用于引用执行脚本的名称; $1 #用于引用执行脚本后跟的第一个位置参数; $2 #用于引用执行脚本后跟的第二个位置参数; $N #用于引用执行脚本后跟的第N个位置参数; shift 1 #轮换,用于替换一个或多个位置变量; |
特殊变量
1 2 3 4 5 |
$? #此变量用于保存上一个命令执行状态的返回值(0表示正确;1-255表示错误); $# #此变量用于引用命令行中位置个数; $* #此变量用于引用所有位置参数的列表; $@ #引用位置参数的列表; $$ #此变量保存当前bash的进程号; |
实例
1 2 3 4 5 6 7 8 |
$ echo $$ 8964 $ tty /dev/pts/2 $ ps aux | grep pts/2 | awk '/bash$/ {print $0}' root 8964 0.0 0.2 108468 1936 pts/2 Ss Oct23 0:00 –bash $ echo $? 0 |
另外,这里提供一个阶乘计算脚本,如果你能明白这个脚本的思想和其中各语法的含义,那么你就可以很容易理解上面说到的几种分类变量了。当然你是Shell初学者,那么对你来说应该有些难度,可以等Shell都学完再来看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ cat jiecheng.sh #!/bin/bash # JC=1 POS=$1 function test() { local LOCAL=$1 local LOCAL_N=$(($LOCAL - 1)) if [ $LOCAL_N -lt 1 ]; then JC=1 else test $LOCAL_N JC=`expr $LOCAL \* $JC` fi } test $POS echo $JC [root@localhost ~]# bash jiecheng.sh 5 |
七、声明变量
1 2 3 4 5 6 |
$ declare [OPTIONS] VARIABLES -i #整形 -a #数值 -x #环境变量 -r #只读变量,不能撤销,不能修改,相当于readonly -f #声明函数 |
案例
1 |
$ declare -i A=20 |
八、变量命令规则
1、首个字符必须为字母。
2、中间不能有空格,可以使用下划线。
3、不能使用标点符号。
4、不能使用bash里的关键字(可用help命令来查看保留关键字)。
PS:命名规则是针对自定义变量。