一文详解shell编程(shell编程笔记)
Posted 非晚非晚
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文详解shell编程(shell编程笔记)相关的知识,希望对你有一定的参考价值。
本文为快速入门教程,如果你所在的行业需要用到很深入的shell脚本知识,可参考书籍《Linux命令行与shell脚本编程大全》,豆瓣链接:https://book.douban.com/subject/26854226/
1. 简介
常见的编程语言分为两类:一个是编译型语言,它们运行前全部一起要经过编译器的编译。另一个解释型语言,执行时,需要使用解释器一行一行地转换为代码,相关的语言分类如下:
真正能够控制计算机硬件(CPU、内存、显示器等)的只有操作系统内核(Kernel),图形界面和命令行只是架设在用户和内核之间的一座桥梁。
在Linux下,这个命令行程序叫做Shell。由此可见,Shell 是将内核、程序和用户连接了起来。常见的 Shell 有 sh、bash、csh、tcsh、ash 等。
- sh:sh 的全称是 Bourne shell,由 AT&T 公司的 Steve Bourne开发,为了纪念他,就用他的名字命名了。sh 是 UNIX 上的标准 shell,很多 UNIX 版本都配有 sh。sh 是第一个流行的 Shell。
- bash:全称Bourne Again shell,bash shell 是 Linux 的默认 shell。
- csh:sh 之后另一个广为流传的 shell 是由柏克莱大学的 Bill Joy 设计的,这个 shell 的语法有点类似C语言,所以才得名为 C shell ,简称为 csh。
- tcsh:tcsh 是 csh 的增强版,加入了命令补全功能,提供了更加强大的语法支持。
- ash:一个简单的轻量级的 Shell,占用资源少,适合运行于低内存环境。
可以通过命令 cat /etc/shells
命令来查看自己电脑的兼容的shell版本,另外,还可以通过命令echo $SHELL
来查看自己电脑默认的SHELL环境。
$ cat /etc/shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash
$ echo $SHELL
/bin/bash
2. 运行shell脚本的几种方式
在你的工作空间新建一个文本test.sh(扩展名并不影响脚本执行,扩展名.sh不写,或者随便取也可以,只不过约定俗成用.sh),然后输入以下内容:
#!/bin/bash
echo "Hello World !"
第一行为注释,表示使用bash的shell,不写也无所谓。在终端输入以下命令都可以运行该脚本:
./test.sh #方式1
source test.sh # 方式2
sh test.sh # 方式3
bash test.sh # 方式4
/bin/sh test.sh #方式5
/bin/bash test.sh # 方式6
3. 注释
- 单行注释
#--------------------------------------------
# 这是一个注释
# 这是一个注释
# 这是一个注释
# 这是一个注释
#--------------------------------------------
- 多行注释
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
其中EOF可以用其他符号代替
4. 变量
变量的命名规则与C/C++等类似,它的规则如下:
- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
- 不能使用bash里的关键字(可用终端输入help命令查看保留关键字)。
4.1 系统变量
系统变量也称为环境变量,所有的程序都可以访问环境变量,一般用大写字母表示,例如$HOME、$PWD、$SHELL、$USER等。例如:
$ echo $HOME
/home/myComputer
4.2 局部变量
局部变量也称为自定义变量,局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
1)定义和使用变量
$ my_name="QLee" #定义变量,=号左右两侧不能有空格
$ echo $my_name #变量的使用
$ echo ${my_name} #变量的使用
QLee
QLee
变量外的括号,是可选的,加花括号是为了帮助解释器识别变量的边界。
2)只读变量
#!/bin/bash
my_name="QLee"
readonly my_name #指定只读变量,不用$
my_name="lisa"
输出:
test.sh: 行 4: my_name: 只读变量
3)删除变量
#!/bin/bash
my_name="QLee"
unset my_name #删除变量,不用$
echo $my_name
以上echo不会有输出,因为my_name被删除。另外,注意不能删除只读变量
。
4)字符串
shell只有数字和字符串的数据类型,所以这里介绍一下字符串类型。
- 单引号形式
str='this is a string'
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的。单引号只能成对的出现,即使使用转义符,也不能单独出现。
- 双引号形式
my_name='QLee'
str="Hello,my name is \\"$my_name\\"! " #拼接
echo $str
输出:
Hello,my name is "QLee"!
- 字符串拼接
my_name="QLee"
# 使用双引号拼接
greeting="hello, "$my_name" !"
greeting_1="hello, ${my_name} !"
echo $greeting $greeting_1
# 使用单引号拼接
greeting_2='hello, '$my_name' !'
greeting_3='hello, ${my_name} !' # 原样输出!!!
echo $greeting_2 $greeting_3
输出:
hello, QLee ! hello, QLee !
hello, QLee ! hello, ${my_name} !
- 获取字符串长度
string="abcd"
echo ${#string} #输出 4
- 提取字符串
从位置1开始,提取4个字符
string="my name is QLee"
echo ${string:1:4} # 从第1个字符开始截取4个字符,输出y na
- 查找字符串
以下表示查找n或i的第一个位置
string="my name is QLee"
echo `expr index "$string" ni` # 输出4
5. shell数组
bash中只有一维数组,它的下标从0开始,数组的大小没有限定,定义数组时用空格分开,例如:
array_name=(value0 value1 value2 value3)# 数组的定义
或者:
array_name=(
value0
value1
value2
value3
)
或者:
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
5.1 读取数组
my_array=(A B "C" D)
echo "第一个元素为: ${my_array[0]}"
echo "第二个元素为: ${my_array[1]}"
echo "第三个元素为: ${my_array[2]}"
echo "第四个元素为: ${my_array[3]}"
输出:
第一个元素为: A
第二个元素为: B
第三个元素为: C
第四个元素为: D
5.2 获取数组长度
# 取得数组元素的个数
length=${#array_name[@]} # @或*可以获取数组中的所有元素。
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}
6. 基本运算符
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
关于运算符需要注意以下两点:
- 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2;
- 完整的表达式要被
包含,注意这个字符不是常用的单引号,在 Esc 键下边。
6.1 算数运算
下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20:
运算符 | 说明 | 举例 |
---|---|---|
+ | 加法 | `expr $a + $b` 结果为 30。 |
- | 减法 | `expr $a - $b` 结果为 -10。 |
* | 乘法 | `expr $a \\* $b` 结果为 200。 |
/ | 除法 | `expr $b / $a` 结果为 2。 |
% | 取余 | `expr $b % $a` 结果为 0。 |
= | 赋值 | a=$b 将把变量 b 的值赋给 a。 |
== | 相等。用于比较两个数字,相同则返回 true。 | [ $a == $b ] 返回 false。 |
!= | 不相等。用于比较两个数字,不相同则返回 true。 | [ $a != $b ] 返回 true。 |
注意:
- *的前面需要用"\\",其他符号不用
- 条件表达式要放在方括号之间,并且要
有空格
,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]。
6.2 关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:
运算符 | 单词 | 说明 | 举例 |
---|---|---|---|
-eq | equal | 相等 | [ $a -eq $b ] 返回 false。 |
-ne | not equal | 不相等 | [ $a -ne $b ] 返回 true。 |
-gt | great than | 大于 | [ $a -gt $b ] 返回 false。 |
-lt | less than | 小于 | [ $a -lt $b ] 返回 true。 |
-ge | great than or equal | 大于或等于 | [ $a -ge $b ] 返回 false。 |
-le | less than or equal | 小于等于 | [ $a -le $b ] 返回 true。 |
6.3 布尔运算符
下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:
运算符 | 说明 | 举例 |
---|---|---|
! | 非运算 | [ ! false ] 返回 true。 |
-o | 或运算 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a | 与运算 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。 |
6.4 逻辑运算符
以下介绍 Shell 的逻辑运算符,假定变量 a 为 10,变量 b 为 20:
运算符 | 说明 | 举例 |
---|---|---|
&& | 逻辑"与" | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
|| | 逻辑"或" | [[ $a -lt 100 || $b -gt 100 ]] 返回 true |
注意:必须两个"[ ]"
6.5 字符串运算符
下表列出了常用的字符串运算符,假定变量 a 为 “abc”,变量 b 为 “efg”:
运算符 | 说明 | 举例 |
---|---|---|
= | 是否相等 | [ $a = $b ] 返回 false。 |
!= | 是否不相等 | [ $a != $b ] 返回 true。 |
-z | 是否字符串长度为0 | [ -z $a ] 返回 false。 |
-n | 是否字符创长度不为0 | [ -n “$a” ] 返回 true。 |
$ | 字符串是否为空,不为空返回 true。 | [ $a ] 返回 true。 |
6.6 文件测试运算符
文件测试运算符用于检测 Unix 文件的各种属性。属性检测描述如下:
操作符 | 说明 | 举例 |
---|---|---|
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] |
-d file | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] |
-g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] |
-k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] |
-p file | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] |
-u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] |
-r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] |
-w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] |
-x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] |
-s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] |
7. echo命令
echo为显示命令,它有下列几种用法。
- 1)显示普通字符串
echo "It is a test" # It is a test
- 2)显示转移字符
echo "\\"It is a test\\"" # "It is a test"
- 3)显示变量
read name # 从标准输入读取一行给name
echo "$name It is a test"
- 4)显示换行
echo -e "OK! \\n" # -e 开启转义
echo "It is a test"
输出:
OK!
It is a test
- 5)不显示换行
echo -e "OK! \\c" # -e 开启转义 \\c 不换行
echo "It is a test"
输出:
OK! It is a test
- 6)显示结果定向至文件
echo "It is a test" > myfile
- 原样输出字符串,不进行转义或取变量(用单引号)
echo '$name\\"' #输出:$name\\"
- 7)显示命令执行结果
echo `date`# `为反引号,显示现在的时间
8. printf命令
它的使用形式:
printf format-string [arguments...]
说明:
- format-string: 为格式控制字符串
- arguments: 为参数列表。
默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \\n。
$ echo "Hello, Shell"
Hello, Shell
$ printf "Hello, Shell\\n"
Hello, Shell
格式输出中,%s 输出一个字符串,%d 整型输出,%c 输出一个字符,%f 输出实数,以小数形式输出。举例说明如下:
printf "%-10s %-8s %-4s\\n" name 性别 体重kg
printf "%-10s %-8s %-4.2f\\n" QLee 男 65.3
printf "%-10s %-8s %-4.2f\\n" lisa 男 75.6
printf "%-10s %-8s %-4.2f\\n" yoyo 女 45.39
输出:
printf 的转义序列
序列 | 说明 |
---|---|
\\a | 警告字符,通常为ASCII的BEL字符 |
\\b | 后退 |
\\c | 不显示输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 |
\\f | 换页(formfeed) |
\\n | 换行 |
\\r | 回车(Carriage return) |
\\t | 水平制表符 |
\\v | 垂直制表符 |
\\\\ | 一个字面上的反斜杠字符 |
\\ddd | 表示1到3位数八进制值的字符。仅在格式字符串中有效 |
\\0ddd | 表示1到3位的八进制值字符 |
9. 流程控制
9.1 if语句
- 情况1:if
if condition
then
command1
command2
...
commandN
fi
- 情况2:if else
if condition
then
command1
command2
...
commandN
else
command
fi
- 情况3:if else-if else
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
9.2 for循环
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
举例:
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
输出:
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
for str in This is a string
do
echo $str
done
输出:
This
is
a
string
9.3 while语句
语法格式:只要condition为true,则继续运行
while condition
do
command
done
无限循环:
while :
do
command
done
或者:
while true
do
command
done
或者
for (( ; ; ))
9.4 until循环
语法格式:直至条件为 true 时停止
until condition
do
command
done
9.5 case … esac多分支语句
每个 case 分支用右圆括号开始,用两个分号 ;; 表示执行结束。
基本语法:
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
9.6 break和continue
和c语言的break和continue语句一样使用。
- break:跳出整个循环
- continue:跳出这一次的循环,继续执行一次循环。
10. 函数
shell中函数的定义格式如下:
[ function ] funname [()]
{
action;
[return int;]
}
说明:
- 可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
- 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。
10.1 无参数、无返回值的函数
demoFun(){
echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"
输出:
-----函数开始执行-----
这是我的第一个 shell 函数!
-----函数执行完毕-----
10.2 无参数、有返回值的函数
funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"
输出:
这个函数会对输入的两个数字进行相加运算...
输入第一个数字:
1
输入第二个数字:
2
两个数字分别为 1 和 2 !
输入的两个数字之和为 3 !
说明:
- 函数返回值在调用该函数后通过 $? 来获得。
- 所有函数在使用前必须定义
10.3 有参数的函数
funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
输出:
第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11 个!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
注意:当n>=10时,需要使用${n}来获取参数。
还有几个特殊字符用来处理参数:
参数处理 | 说明 |
---|---|
$# | 传递到脚本或函数的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
11. 输入/输出重定向
默认情况下,标准输入和输出的地方都是终端,但是我们可以通过命令对输入/输出进行重定向。
重定向命令列表如下:
命令 | 说明 |
---|---|
command > file | 将输出重定向到 file。 |
command < file | 将输入重定向到 file。 |
command >> file | 将输出以追加的方式重定向到 file。 |
n > file | 将文件描述符为 n 的文件重定向到 file。 |
n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 |
n >& m | 将输出文件 m 和 n 合并。 |
n <& m | 将输入文件 m 和 n 合并。 |
<< tag | 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 |
需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
- Here Document
Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。
例如:
cat << EOF
hello, everybody
my name is QLee
EOF
打印如下:
hello, everybody
my name is QLee
- /dev/null 文件
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
$ command > /dev/null
如果希望屏蔽 stdout 和 stderr,可以这样写:
$ command > /dev/null 2>&1 # 0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
12. 文件包含
与C语言中的#include语句类似,shell中也有自己的包含命令,它的语法格式如下:
. filename # 注意点号(.)和文件名中间有一空格
或
source filename
因为是解释语言,所以文件包含的意义,其实就是写一条命令运行被包含的文件。
13. 其他命令
(1)data
显示当前时间,格式如下:
date [选项] [+时间格式] #使用(+)号开始的参数用来指定时间格式
例如:
nowTime=`date +%Y-%M-%d-%H:%M:%S`
echo $nowTime
date的格式有以下几种:
%H 小时(以00-23来表示)。
%I 小时(以01-12来表示)。
%K 小时(以0-23来表示)。
%l 小时(以0-12来表示)。
%M 分钟(以00-59来表示)。
%P AM或PM。
%r 时间(含时分秒,小时以12小时AM/PM来表示)。
%s 总秒数。起算时间为1970-01-01 00:00:00 UTC。
%S 秒(以本地的惯用法来表示)。
%T 时间(含时分秒,小时以24小时制来表示)。
%X 时间(以本地的惯用法来表示)。
%Z 市区。
%a 星期的缩写。
%A 星期的完整名称。
%b 月份英文名的缩写。
%B 月份的完整英文名称。
%c 日期与时间。只输入date指令也会显示同样的结果。
%d 日期(以01-31来表示)。
%D 日期(含年月日)。
%j 该年中的第几天。
%m 月份(以01-12来表示)。
%U 该年中的周数。
%w 该周的天数,0代表周日,1代表周一,异词类推。
%x 日期(以本地的惯用法来表示)。
%y 年份(以00-99来表示)。
%Y 年份(以四位数来表示)。
%n 在显示时,插入新的一行。
%t 在显示时,插入tab。
MM 月份(必要)
DD 日期(必要)
hh 小时(必要)
mm 分钟(必要)
ss 秒(选择性)
(2)后台运行脚本
test.sh &
对于基础应用,以上知识已经足够了。由于本人所在行业原因,暂时还不需要那么高深的shell知识,如果后续有深入研究,会继续补充更新。
给大家推荐一个很赞的Linux下的命令行网址:https://www.linuxcool.com/
以上是关于一文详解shell编程(shell编程笔记)的主要内容,如果未能解决你的问题,请参考以下文章