引言
通过 Shell 编程的学习,铺平架构师道路上的一块大砖。
Shell 在Linux 系统中的定位如下所示:
一、第一个 Shell 脚本
我们通过一个简单的 Shell 脚本来感受一下。
在 Shell 中不需要加 “;” 结尾,通过 vim 可以进行 shell 的编程工作,并且一般建议为 shell 文件添加可执行权限。
更改权限之后,执行脚本:
另外如果没有可执行权限,还可以通过 sh 来执行:
一般在企业中,都是需要加上可执行权限的。
注意,本片文章后面的内容全部忽略 chmod 操作。
二、Shell 变量
Shell 变量分为系统变量和用户变量。
2.1 系统变量
系统变量指的是预先在 Linux 系统中定义好的变量(常量),如PATH 等。
另外,可以通过 set 命令来查看所有系统变量:
2.2 自定义变量
在 Shell 编程中,多数情况下会使用自定义变量。
基本语法:
1、赋值:变量名=值。注意,等号两边不能有空格,这是与其他编程语言有所区别的。
2、撤销变量:unset 变量名
3、声明静态变量:readonly 变量名=值 。 注意:静态变量不能 unset。
4、引用已定义的变量:$变量名 或 ${变量名} 。后者可以在连续的字符串中帮助 Shell 解释器区分变量名的边界。
5、变量名可以是字母、数字、下划线 的组合,且不能以数字开头。这与Java 的变量命名规则相同。
6、变量名一般习惯大写。
7、将命令返回值赋给变量:第一种:反引号:A=`ls -l` ; 第二种 :$(ls -l) 。两种方式等价。
案例:定义一个 变量 A ,输出,然后 unset ,再输出:
案例:定义一个静态变量 B,查看 unset 的效果:
可以看到,readonly 类型的变量是不能 unset 的。
2.3 环境变量
可以自定义环境变量,供其他程序使用。
第一步:vim /etc/profile
第二步:export 变量名=值 功能:将 Shell 变量输出为环境变量(或先定义变量,再 export $变量名)
第三步:source /etc/profile 功能:让修改后的配置信息立即生效
第四步:echo $变量名 功能:查询环境变量的值。
vim 打开 profile 文件,在文件最末尾(输入G),添加我们需要定义的环境变量 SAY_HELLO,保存并退出:
然后刷新配置文件:source /etc/profile :
可以看到,变量可以被直接使用了,我们可以在其他的 Shell 文件中尝试使用刚刚定义的环境变量:
可以看到,可以使用。
2.4 多行注释
在 Shell 文件中使用 :<<! xxxx ! 进行多行注释。
2.5 位置参数变量
当我们执行一个Shell 脚本的时候,如果希望获得命令行参数信息,可以通过位置参数变量。比如,我们执行 ./app.sh 100 200 ,这就是一个 Shell 的执行命令行,可以在 app.sh 中动态获得参数信息,即100 和 200。
常用的位置参数获取方式有:
$n : n 为大于等0 的自然数,其中 $0 表示 shell 文件本身,$1 - $9 表示第一到第九个参数, 10 以上的参数需要写作 ${10}。
$* : 表示命令行中的所有参数,注意,$* 把所有参数看做是一个整体。
$@ : 表示命令行中的所有参数,注意,$@ 把所有参数都区分对待。$* 和 $@ 这两者主要在循环语句的时候可以看出区别。
$# : 表示命令行中所有参数的个数。
演示:在 HelloWorld.sh 中输出这些位置变量参数,然后观察结果:
2.6 预定义变量
预定义变量指的是 Shell 设计者事先已经定义好的变量,可以直接在 Shell 脚本中使用:
$$ 表示当前进程的进程号(PID)
$! 表示后台运行的最后一个进程的进程号(PID)
$? 表示最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令执行正确;如果这个变量的值非 0 (具体是哪个数,由命令自己决定),则证明上一个命令执行不正确。
演示:首先,我们定义一个新的 Shell 脚本:myShell.sh,然后通过其他的 Shell 来执行它,并输出响应的预定义变量:
myShell.sh:
HelloWorld.sh:
执行结果:
其中,在HelloWorld.sh 中的 & 符号,代表将Shell 脚本以后台的方式运行。
三、运算符
第一种:$((运算式))
第二种:$[运算式]
第三种:expr 运算式(注意,expr 运算符之间要有空格,因为乘号需要转义)
另外,还有一些常见的比较运算符:
= 字符串比较
-lt 小于
-le 小于等于
-eq 等于
-gt 大于
-ge 大于等于
-ne 不等于
四、Shell 条件判断
4.1 基本的 if 判断
基本语法:
if [ condition ]
注意,条件语句要与中括号之间有空格。
按照文件权限进行判断:
-r 有读的权限
-w 有写的权限
-x 有执行的权限
按照文件类型判断:
-f 文件存在 并且是一个常规文件
-e 文件存在
-d 文件存在,并且是一个目录
案例:实现一个Shell 程序,判断输入的参数,大于 60 ,则输出 “及格了”,如果小于 60,则输出“不及格”。
4.2 case 语句
基本语法:
case $变量 in
"值1")执行语句1;;
"值2")执行语句2;;
"值3")执行语句3;;
......
*)没有匹配项的执行语句;;
esac
case 语句的功能和所有其他开发语言一致,不过语法有些不同。
案例:当命令行参数是 1 时,输出 Mon,是 2 时,输出 Tues,其他数字,则输出 Other。
五、Shell 流程控制
5.1 for 循环
基本语法1:
for 变量 in 值1 值2 值3...
do执行语句
done
案例:打印命令行中的参数(使用 $* 和 $@并观察区别):
基本语法2:
for((初始值; 循环控制条件; 变量变化))
do执行语句
done
案例:从1 加到 100 ,然后输出显示:
5.2 while循环
基本语法:
while [ 条件判断 ]
do执行语句
done
案例:从命令行输入一个数字 n ,统计 从1 加到 n 的值是多少:
六、Shell 读取控制台输入
基本语法:
read [选项] 变量名
选项及参数:
-p 指定读取值时的提示符
-t 指定读取值时等待的时间(秒),如果没有在指定时间内输入,则不再等待。
变量名:指定读取值的变量名
七、Shell 函数
Shell 编程和其他编程一样,同样存在系统函数,和自定义函数。其中有两个比较重要的系统函数:
7.1 系统函数
1、basename 函数
功能: 返回完整的路径最后 / 的部分,即获取文件的文件名。
基本语法:
basename [pathname] [suffix]
suffix 是后缀,可以用于除去文件名的后缀名。
2、dirname 函数
功能:返回完整路径最后 / 的前面的部分,常用于返回路径部分。
基本语法和 basename 完全一致。
7.2 自定义函数
基本语法:
[function] 函数名[()]
{执行语句[return int]
}
说明:Shell 函数不需要写任何参数, return 返回,如果不加,将以最后一条命令运行结果作为返回值。return 后面跟0 ~ 255 的数值。调用函数直接书写函数名称即可,函数的返回值在调用函数后,通过 $? 来获得。
另外,虽然 Shell 函数定义不需要定义参数,但是也是可以传入参数的,不过语法比较奇怪,在调用的时候,我们可以这样写:
同样也是可以的,但是注意,变量名要保持一致。也就是说,在 Shell 脚本文件中,不论是在哪里定义的变量,只要还在这个脚本文件中,就可以直接访问。上图也看到,我们在 readNum 函数中通过控制台,读入了两个数字 n1 和 n2 ,直接在twoSum 函数体中使用也是可以的。另外, readNum 没有返回语句,默认也是最后一行语句执行的结果。
综上,就是关于Shell 编程的入门知识,后面还会增加相关的文章,欢迎大家文末留言。