一、运行第一个 Shell 脚本
1.1 Shell 脚本
Shell 脚本(shell script),是一种为 shell 编写的脚本程序。
业界所说的 shell 通常都是指 shell 脚本,但读者朋友要知道,shell 和 shell script 是两个不同的概念。
由于习惯的原因,简洁起见,本文出现的 "shell编程" 都是指 shell 脚本编程,不是指开发 shell 自身。
1.2 Shell 环境
Shell 编程跟 JavaScript、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
Linux 的 Shell 种类众多,常见的有:
- Bourne Shell(/usr/bin/sh或/bin/sh)
- Bourne Again Shell(/bin/bash)
- C Shell(/usr/bin/csh)
- K Shell(/usr/bin/ksh)
- Shell for Root(/sbin/sh)
- ……
本教程关注的是 Bash,也就是 Bourne Again Shell,由于易用和免费,Bash 在日常工作中被广泛使用。同时,Bash 也是大多数Linux 系统默认的 Shell。
在一般情况下,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh,它同样也可以改为 #!/bin/bash。
#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序。
1.3 运行脚本
打开文本编辑器(可以使用 vi/vim 命令来创建文件),新建一个文件 test.sh,扩展名为 sh(sh代表shell)
1. #!/bin/bash 2. echo "Hello World !"
chmod +x ./test.sh #使脚本具有执行权限 ./test.sh #执行脚本
另一种则是,直接运行解释器
/bin/sh test.sh
二、Shell 变量
2.1 变量名规则
注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:
- 只包含字母、数字和下划线: 变量名可以包含字母(大小写敏感)、数字和下划线 _,不能包含其他特殊字符。
- 不能以数字开头: 变量名不能以数字开头,但可以包含数字。
- 避免使用 Shell 关键字: 不要使用Shell的关键字(例如 if、then、else、fi、for、while 等)作为变量名,以免引起混淆。
- 使用大写字母表示常量: 习惯上,常量的变量名通常使用大写字母,例如 PI=3.14。
- 避免使用特殊符号: 尽量避免在变量名中使用特殊符号,因为它们可能与 Shell 的语法产生冲突。
- 避免使用空格: 变量名中不应该包含空格,因为空格通常用于分隔命令和参数。
有效的 Shell 变量名示例如下:
man="handsome" LD_LIBRARY_PATH="/bin/" _var="123" var2="abc"
除了显式地直接赋值,还可以用语句给变量赋值,如:
for file in `ls /etc` 或 for file in $(ls /etc)
2.2 使用变量
使用一个定义过的变量,只要在变量名前面加美元符号即可,如:
#!/bin/bash name="zhangsan" echo $name echo ${name}
推荐给所有变量加上花括号,这是个好的编程习惯
已定义的变量,可以被重新定义
#!/bin/bash your_name="tom" echo $your_name your_name="alibaba" echo $your_name
2.3 只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变
#!/bin/bash your_name="tom" echo $your_name readonly your_name your_name="alibaba" echo $your_name
结果报错
2.4 删除变量
使用 unset 命令可以删除变量
#!/bin/bash your_name="tom" echo $your_name unset your_name echo $your_name
删除了,值为空
2.5 变量类型
declare
命令
declare
命令用于声明和定义变量的属性,如只读、数组、整数等。
常用选项
-a
:声明数组变量。-A
:声明关联数组变量。-i
:声明整数变量。-r
:声明只读变量。-x
:声明环境变量(类似于export
)。-l
:将变量值转换为小写字母。-u
:将变量值转换为大写字母。
typeset
命令
typeset
命令在功能上与 declare
相似,用于声明和定义变量的属性。在 Bash 中,typeset
是 declare
的同义词,但在一些其他 Shell 中(如 ksh),typeset
更常用。
与 declare
的选项基本相同,typeset
也支持以下选项:
-a
:声明数组变量。-A
:声明关联数组变量。-i
:声明整数变量。-r
:声明只读变量。-x
:声明环境变量(类似于export
)。
-l
:将变量值转换为小写字母。-u
:将变量值转换为大写字母。
#!/bin/bash typeset -i num num=66 age=19 typeset -r age typeset -l name name="MORANT" typeset -u sex sex="man" typeset -a array array=(1 2 3) typeset -x export_var="Hello!" typeset -A assoc assoc[city]="Enshi" assoc[fell]="good" echo ${num} echo ${name} echo ${sex} echo ${array[1]} echo ${export_var} echo ${assoc[city]}
2.6 Shell 字符串
单引号字符串的限制:
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
- 单引号字符串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
双引号的优点:
- 双引号里可以有变量
- 双引号里可以出现转义字符
拼接字符串
#!/bin/bash name="joker" say="I am $name" echo ${say}
获取字符串长度
#!/bin/bash name="joker" say="I am $name" echo ${#say}
提取子字符串
#!/bin/bash name="joker" say="I am $name" echo ${say:0:4}
查找子字符串
#!/bin/bash name="joker" say="I am $name" echo `expr index "$say" j`
- 反引号(`)的使用:反引号用于命令替换,将其内部的命令输出作为外部命令的参数。换句话说,
expr index "$say" j
的输出将作为echo
的参数。 expr
命令:expr
是一个用于计算表达式的命令,支持算术运算、字符串操作等。这里使用的是index
操作。index
操作:expr index
返回字符串中第一次出现给定字符的位置(从1开始计数)。
2.7 Shell 数组
读取数组
#!/bin/bash typeset -a array_num array_num=(1 2 3 4 5) echo ${array_num[0]} echo ${array_num[1]} echo ${array_num[2]} echo ${array_num[3]} echo ${array_num[4]} echo ${array_num[@]}
使用 @ 符号可以获取数组中的所有元素
获取数组的长度
#!/bin/bash typeset -a array_num array_num=(1 2 3 4 5) length=${#array_num[@]} echo $length length=${#array_num[*]} echo $length length=${#array_num[0]} echo $length
获取数组长度的方法与获取字符串长度的方法相同
2.8 注释
#-------------------------------------------- # 这是一个注释 :<<EOF 注释内容... 注释内容... 注释内容... EOF : <<'COMMENT' 这是注释的部分。 可以有多行内容。 COMMENT :<<' 注释内容... 注释内容... 注释内容... ' :<<! 注释内容... 注释内容... 注释内容... ! : ' 这是注释的部分。 可以有多行内容。 '
三、Shell 传递参数
#!/bin/bash echo "请传递三个参数" echo "执行文件名:$0" echo "第一个参数为:$1" echo "第一个参数为:$2" echo "第一个参数为:$3"
$0 为执行的文件名(包含文件路径)
参数处理 | 说明 |
$# | 传递到脚本的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数。 如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
#!/bin/bash echo "请传递三个参数" echo "执行文件名:$0" echo "第一个参数为:$1" echo "第一个参数为:$2" echo "第一个参数为:$3" echo "将所有参数作为一个字符串显示:$*" echo "脚本当前运行的 ID:$$" echo "后台运行的最后一个进程的 ID:$!" echo "引号分割所有参数:$@" echo "显示 Shell 使用的当前选项:$-" echo "显示最后命令的退出状态,0 表示没有错误:$?"
四、Shell 基本运算符
4.1 算术运算符
运算符 | 说明 | 举例 |
+ | 加法 | `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。 |
#!/bin/bash a=10 b=20 val=`expr $a + $b` echo "a + b : $val" val=`expr $a - $b` echo "a - b : $val" val=`expr $a \* $b` echo "a * b : $val" val=`expr $b / $a` echo "b / a : $val" val=`expr $b % $a` echo "b % a : $val" if [ $a == $b ] then echo "a 等于 b" fi if [ $a != $b ] then echo "a 不等于 b" fi
4.2 关系运算符
运算符 | 说明 | 举例 |
-eq | 检测两个数是否相等,相等返回 true。 | [ $a -eq $b ] 返回 false。 |
-ne | 检测两个数是否不相等,不相等返回 true。 | [ $a -ne $b ] 返回 true。 |
-gt | 检测左边的数是否大于右边的,如果是,则返回 true。 | [ $a -gt $b ] 返回 false。 |
-lt | 检测左边的数是否小于右边的,如果是,则返回 true。 | [ $a -lt $b ] 返回 true。 |
-ge | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | [ $a -ge $b ] 返回 false。 |
-le | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | [ $a -le $b ] 返回 true。 |
#!/bin/bash a=10 b=20 if [ $a -eq $b ] then echo "$a -eq $b : a 等于 b" else echo "$a -eq $b: a 不等于 b" fi if [ $a -ne $b ] then echo "$a -ne $b: a 不等于 b" else echo "$a -ne $b : a 等于 b" fi if [ $a -gt $b ] then echo "$a -gt $b: a 大于 b" else echo "$a -gt $b: a 不大于 b" fi if [ $a -lt $b ] then echo "$a -lt $b: a 小于 b" else echo "$a -lt $b: a 不小于 b" fi if [ $a -ge $b ] then echo "$a -ge $b: a 大于或等于 b" else echo "$a -ge $b: a 小于 b" fi if [ $a -le $b ] then echo "$a -le $b: a 小于或等于 b" else echo "$a -le $b: a 大于 b" fi
4.3 布尔运算符
运算符 | 说明 | 举例 |
! | 非运算,表达式为 true 则返回 false,否则返回 true。 | [ ! false ] 返回 true。 |
-o | 或运算,有一个表达式为 true 则返回 true。 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a | 与运算,两个表达式都为 true 才返回 true。 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。 |
#!/bin/bash a=10 b=20 if [ $a != $b ] then echo "$a != $b : a 不等于 b" else echo "$a == $b: a 等于 b" fi if [ $a -lt 100 -a $b -gt 15 ] then echo "$a 小于 100 且 $b 大于 15 : 返回 true" else echo "$a 小于 100 且 $b 大于 15 : 返回 false" fi if [ $a -lt 100 -o $b -gt 100 ] then echo "$a 小于 100 或 $b 大于 100 : 返回 true" else echo "$a 小于 100 或 $b 大于 100 : 返回 false" fi if [ $a -lt 5 -o $b -gt 100 ] then echo "$a 小于 5 或 $b 大于 100 : 返回 true" else echo "$a 小于 5 或 $b 大于 100 : 返回 false" fi
4.4 逻辑运算符
运算符 | 说明 | 举例 |
&& | 逻辑的 AND | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
|| | 逻辑的 OR | [[ $a -lt 100 || $b -gt 100 ]] 返回 true |
#!/bin/bash a=10 b=20 if [[ $a -lt 100 && $b -gt 100 ]] then echo "返回 true" else echo "返回 false" fi if [[ $a -lt 100 || $b -gt 100 ]] then echo "返回 true" else echo "返回 false" fi
4.5 字符串运算符
运算符 | 说明 | 举例 |
= | 检测两个字符串是否相等,相等返回 true。 | [ $a = $b ] 返回 false。 |
!= | 检测两个字符串是否不相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
-z | 检测字符串长度是否为0,为0返回 true。 | [ -z $a ] 返回 false。 |
-n | 检测字符串长度是否不为 0,不为 0 返回 true。 | [ -n "$a" ] 返回 true。 |
$ | 检测字符串是否不为空,不为空返回 true。 | [ $a ] 返回 true。 |
#!/bin/bash a="abc" b="efg" if [ $a = $b ] then echo "$a = $b : a 等于 b" else echo "$a = $b: a 不等于 b" fi if [ $a != $b ] then echo "$a != $b : a 不等于 b" else echo "$a != $b: a 等于 b" fi if [ -z $a ] then echo "-z $a : 字符串长度为 0" else echo "-z $a : 字符串长度不为 0" fi if [ -n "$a" ] then echo "-n $a : 字符串长度不为 0" else echo "-n $a : 字符串长度为 0" fi if [ $a ] then echo "$a : 字符串不为空" else echo "$a : 字符串为空" fi
4.6 文件测试运算符
操作符 | 说明 | 举例 |
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] 返回 false。 |
-d file | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] 返回 false。 |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] 返回 true。 |
-g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] 返回 false。 |
-k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] 返回 false。 |
-p file | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] 返回 false。 |
-u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] 返回 false。 |
-r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回 true。 |
-w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
-x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回 true。 |
-s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] 返回 true。 |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true。 |
#!/bin/bash file="/var/www/html/index.html" if [ -r $file ] then echo "文件可读" else echo "文件不可读" fi if [ -w $file ] then echo "文件可写" else echo "文件不可写" fi if [ -x $file ] then echo "文件可执行" else echo "文件不可执行" fi if [ -f $file ] then echo "文件为普通文件" else echo "文件为特殊文件" fi if [ -d $file ] then echo "文件是个目录" else echo "文件不是个目录" fi if [ -s $file ] then echo "文件不为空" else echo "文件为空" fi if [ -e $file ] then echo "文件存在" else echo "文件不存在" fi
五、Shell echo 命令
显示普通字符串
显示转义字符
显示变量
#!/bin/sh read name echo "$name It is a test"
read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量
显示换行
显示不换行
将 \n 改为 \c 即可
显示结果定向至文件
显示命令执行结果
六、Shell printf 命令
printf 命令的语法:
printf format-string [arguments...]
参数说明:
- format-string: 一个格式字符串,它包含普通文本和格式说明符。
- arguments: 用于填充格式说明符的参数列表。。
格式说明符由 % 字符开始,后跟一个或多个字符,用于指定输出的格式。常用的格式说明符包括:
%s
:字符串%d
:十进制整数%f
:浮点数%c
:字符%x
:十六进制数%o
:八进制数%b
:二进制数%e
:科学计数法表示的浮点数
#!/bin/bash printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234 printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543 printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
%-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐)