前言:
问题一:什么是shell?
答:从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁。用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操作。在Linux GUI日益完善的今天,在系统管理等领域,Shell编程仍然起着不可忽视的作用。深入地了解和熟练地掌握Shell编程,是每一个Linux用户的必修功课之一。
目前最流行的Shell称为bash Shell,bash Shell脚本编程以其简洁、高效而著称,多年来成为Linux程序员和系统管理员解决实际问题的利器。
问题二、为什么要使用shell脚本?
答:使用脚本编程语言的好处是,它们多半运行在比编译型语言还高的层级,能够轻易处理文件与目录之类的对象。之所以要使用shell脚本是基于:
简单性:shell是一个高级语言,通过它,你可以简洁地表达复杂的操作。
可移植性:使用POSIX所定义的功能,可以做到脚本无须修改就可以在不同的系统上执行。
开发容易:可以在短时间内完成一个功能强大有好用的脚本。
一、初识shell脚本编程
利用vim文本编辑器编写Shell脚本的格式是固定的,如下:
#!/bin/bash
#This is a shell file
echo "Hello World!"
第一行一定要使用#!指明系统需要哪种shell解释用户的shell程序,如:#!/bin/sh,#!/bin/bash,#!/bin/csh,#!/bin/tcsh和#!/bin/ksh等。如果首行没有这句话,在执行脚本文件的时候,将会出现错误。
除首行外,以#开头的行就是注释行。
后续的部分就是主程序,Shell脚本像高级语言一样,也有变量赋值,也有控制语句。
通常shell脚本多以.sh为后缀。
执行shell的方式有两种:第一种是为shell脚本加上可执行权限并执行,第二种是通过bash命令执行shell脚本。
二、shell的基本语法知识
1、变量类别
(1)本地变量:只对当前shell进程有效的变量;对其它shell进程无效,包当前shell进程的子进程;
VAR_NAME=VALUE
变量赋值:向变量的存储空间保存数据
变量引用:${VAR_NAME}
"":弱引用,里面的变量会被替换;
'':强引用,里面的所有字符都是字面量,直接输出;
(2)环境变量:对当前shell进程及其子shell有效,对其它的shell进程无效;
定义:export VAR_NAME=VALUE
导出:export VAR_NAME
(当定义了一个本地变量,要在当前shell的子shell进程中使用,可用导出: export VAR_NAME)
撤消变量:unset VAR_NAME(可撤销本地变量和环境变量)
设置只读变量:readonly VAR_NAME(标志变量为不可改变的,即只读变量)
(3)局部变量:对shell脚本中某代码片断有效;通常用于函数本地;
local VAR_NAME=VALUE(当函数调用结束,局部变量会随之消失。也能用unset 撤销局部变量。)
(4)位置变量:
$1, $2, ..., ${10}(当两位数或超过两位数时,最好用{}括起来)
(5)特殊变量:
$#: 传递到脚本的参数个数
$*: 以一个单字符串显示所有向脚本传递的参数
$$: 脚本运行的当前进程ID号
$!: 后台运行的最后一个进程的进程ID号
$@: 与$*相同,但是使用时加引号,并在引号中返回每个参数
$-: 显示shell使用的当前选项,与set命令功能相同
$?: 显示最后命令的退出状态,0表示成功,其他任何值都表示有错误
2、变量赋值
变量=值
任何变量无需事先声明,可直接使用
值默认都是字符型
例如:a=abc, b=3
a=3(不能写成 a = 3 ,即中间不能有空格。因在bash中是基于命令行的,中间有空格,bash会把其后的 = 和 3 看做选项或参数)
赋值:
a=4
增强型赋值:
+=, -=, *=, /=, %=
a=$[$a+1] 相当于 let a+=1 (let a+=1相当于let a=$a+1)
自加:var++, var--, ++var, --var
3、算术运算
(1)如何定义整型变量:
let VAR_NAME=INTEGER_VALUE
例如:let a=3
declare -i VAR_NAME=INTEGER_VALUE
例如:declare -i a=3
注意:即使没有定义为整型变量,字符型的数字依然可以参与算术运算;bash会执行变量类型的隐式类型转换;
(2)实现算术运算的方式:
let VAR_NAME=ARITHMATIC_EXPRESSION
( 使用let进行算术运算,要使用结果,必须把运算结果存储在变量中,再去引用变量(不可用` `引用该命令的执行结果) )
VAR_NAME=$[ARITHMATIC_EXRESSION]
(不是命令,而是表达式)
VAR_NAME=$((EXPRESSION))
(不是命令,而是表达式)
VAR_NAME=$(expr $num1 + $num2)
(使用expr进行算术运算,可用` `引用该命令的执行结果)
(3)算术运算符:
+ :加
- :减
* :乘
/ :除
%:取模,取余数
**: 次方,如: 2**2
4、条件测试
命令执行成功与否即为条件测试
test EXPR
[ EXPR ]
[[ EXPR ]]
比较运算:
>, <, >=, <=, ==, !=
(1)测试类型:根据比较时的操作数的类型
整型测试:整数比较
字符测试:字符串比较
文件测试:判断文件的存在性及属性等
注意:比较运算通常只在同一种类型间进行
(2)整型测试:
-gt: 大于,例如 [ $num1 -gt $num2 ]
-lt: 小于
-ge: 大于等于
-le: 小于等于
-eq: 等于
-nq: 不等于
(3)字符串测试:
双目
> 大于,例如[[ "$str1" > "$str2" ]]
< 小于
>= 大于等于
<= 小于等于
== 等于
!= 不等于
单目:
-n String: 是否不空,不空则为真,空则为假
-z String: 是否为空,空则为真,不空则假
字串测试中的模式匹配:
[[ "$var" =~ PATTERN ]]
(4)文件测试:
-a FILE :存在为真,否则为假;
-e FILE: 存在则为真;否则为假;
-f FILE: 存在并且为普通文件,则为真;否则为假;
-d FILE: 存在并且为目录文件,则为真;否则为假;
-L/-h FILE: 存在并且为符号链接文件,则为真;否则为假;
-b: 块设备
-c: 字符设备
-S: 套接字文件
-p: 命名管道
-s FILE: 存在并且为非空文件则为值,否则为假;
-r FILE :是否可读
-w FILE :是否可写
-x FILE :是否可执行
file1 -nt file2: file1的mtime新于file2则为真,否则为假;
file1 -ot file2:file1的mtime旧于file2则为真,否则为假;
5、if语句
(1)单分支的if语句: if 测试条件; then 选择分支 fi 表示条件测试状态返回值为0时,则执行选择分支 或者另外一种格式: if 测试条件 then 选择分支 fi (2)双分支的if语句: if 测试条件; then 选择分支1 else 选择分支2 fi 两个分支仅执行其中之一。 (3)多分支的if语句: if 条件1; then 分支1 elif 条件2; then 分支2 elif 条件3; then 分支3 ... else 分支n fi
5、case语句
case 语句:有多个测试条件时,case语句会使得语法结构更明晰 格式: case 变量引用 in PATTERN1) 分支1 ;; PATTERN2) 分支2 ;; ... *) 分支n ;; esac PATTERN:类同于文件名通配机制,但支持使用|表示或者; a|b: a或者b *:匹配任意长度的任意字符 ?: 匹配任意单个字符 []: 指定范围内的任意单个字符 (区分字母大小写) t-size:14px; font-family:'宋体'; " >**: 2**2 (分支后的“;;”表示分支结束,不再往下执行,相当于Java中的switch 中的break。如果进入分支1,不加“;;”,则会继续往下执行。) (注意:在bash编程中,case的分支中必须加上“;;”,如果不加“;;”或仅加了“;”,会有语法错误!!!)
6、for语句
格式: for VAR_NAME in LIST do 循环体 done 或者可以写成: for VAR_NAME in LIST ;do 循环体 done LIST:列表,中间包括一个或多个元素 退出条件:遍历结束 for的第二种使用格式 : for ((初始条件;测试条件;修改表达式)); do 循环体 done
7、while语句
while 测试条件; do 循环体 done 如测试结果为“真”,则进入循环;退出条件为,测试条件为假;
8、until语句
until 测试条件; do 循环体 done 如果测试结果为“假”,则进入循环;退出条件为,测试条件为真;
9、函数
(函数要先定义,然后才能使用)
语法: 两种格式: function F_NAME { 函数体 } F_NAME() { 函数体 }
可调用:使用函数名
函数名出现的地方,会被自动替换为函数;
函数中的变量:
(1)本地变量:作用域为整个bash进程
varname=value
(2)局部变量:作用域只对当前代码段有效(只在函数中有效)
local varname=value (使用local关键字定义)
(3)环境变量:作用域为当前shell进程及其子进程
export varname=value
函数的返回值:
函数的执行结果返回值:代码的输出
函数中的打印语句:echo, print
函数中调用的系统命令执行后返回的结果
执行状态返回值:
函数体中最后一次执行的命令状态结果
自定函数执行状态的返回值:return #
函数可以接受参数:
在函数体中调用函数参数的方式同脚本中调用脚本参数的方式:位置参数
$1, $2, ...
$# :参数个数
$*, $@ :取所有参数