一、Shell介绍
1.1 简介
- Shell是用C语言编写的程序,能让用户访问操作系统内核服务,类似DOS下的
command
和cmd.exe
。 - 它兼具命令语言和程序设计语言的特性,Shell编程通常指编写Shell脚本,而非开发Shell自身。
1.2 Shell解释器
- 进行Shell编程,只需文本编辑器(如vi)和脚本解释器。
- Linux系统中有多种Shell解释器,可通过
cat /etc/shells
命令查看系统已安装的Shell。 - bash因易用且免费,在日常工作中应用广泛,也是多数Linux系统的默认Shell。
二、快速入门
2.1 编写Shell脚本
- 创建文件:使用vi编辑器新建脚本文件,通常以
.sh
为扩展名(扩展名不影响脚本执行),例如vi hello.sh
。 - 脚本格式
- 首行需指定解释器,格式为
#!/bin/bash
,告知系统该脚本使用的Shell解释器。 - 后续编写具体代码,如
echo "Hello World !"
(echo
命令用于向窗口输出文本)。
- 首行需指定解释器,格式为
- 赋予执行权限:新创建的脚本默认无执行权限,需通过
chmod +x ./hello.sh
命令赋予可执行权限。
2.2 执行Shell脚本
执行方式 | 命令示例 | 说明 |
---|---|---|
当前目录执行 | ./hello.sh |
告知系统在当前目录寻找并执行脚本,需先赋予执行权限 |
全路径执行 | /root/shelldemo/hello.sh |
通过完整路径指定脚本位置并执行,需先赋予执行权限 |
解释器参数执行 | sh hello.sh 或sh /root/shelldemo/hello.sh |
直接运行解释器,将脚本作为参数,无需考虑脚本执行权限 |
2.3 小结
- 用vi编辑器创建Shell程序文件,推荐以
.sh
为后缀名。 - 程序基本组成包括变量、数据类型、运算符号、流程控制语句、数组和函数。
三、Shell程序:变量
3.1 语法格式
- 定义格式:
变量名=值
,等号两边不能有空格。 - 命名规则
- 首个字符必须为字母(a-z,A-Z)。
- 中间不能有空格,可使用下划线(_)。
- 不能使用标点符号。
- 不能使用bash里的关键字(可用
help
命令查看保留关键字)。
- 命名惯例:通常为小写字母单词,多个单词间用“_”分隔,如
name="bigdata.com"
,做到见名知其意。
3.2 变量使用
- 调用变量:在变量名前加
$
,如echo $name
;也可加花括号${your_name}
,花括号用于帮助解释器识别变量边界,可选。 - 重新定义:已定义的普通变量可重新赋值,如
name="bigdata"
后可再写name="hadoop"
。 - 只读变量:用
readonly
命令定义,语法为readonly 变量名=值
或先定义变量再执行readonly 变量名
,只读变量的值不能被修改。 - 删除变量:使用
unset 变量名
命令,不能删除只读变量。
3.3 变量类型
类型 | 定义 | 访问范围 |
---|---|---|
局部变量 | 在脚本或命令中定义 | 仅在当前Shell实例中有效,其他Shell启动的程序无法访问 |
全局变量(环境变量) | 系统或用户配置 | 所有程序(包括Shell启动的程序)都能访问,部分程序依赖其正常运行,可通过set 命令查看 |
四、字符串
字符串是Shell编程中常用的数据类型,可使用单引号、双引号或不加引号定义。
4.1 单引号
- 特性:单引号里的任何字符都会原样输出,变量无效;单引号字串中不能出现单独一个的单引号(转义后也不行),但可成对出现用于字符串拼接。
- 示例:
4.2 双引号
- 优点:可识别变量,也可出现转义字符。
- 示例:
4.3 获取字符串长度
- 语法:
${#字符串}
- 示例:
4.4 提取子字符串
从指定索引截取到最后:
${字符串:索引}
(索引从0开始)。从指定索引截取指定长度:
${字符串:索引:长度}
。示例:
注意:字符串中的空格也算一个字符。
4.5 查找子字符串·`
- 语法:
expr index "$字符串" 子字符串
(反引号`,位于Esc键下方,不是单引号’),位置从1开始计算。 - 示例:
五、Shell程序:参数传递
5.1 参数传递方式
- 执行脚本时传递参数:
./shell程序 [空格] 参数1 [空格] 参数2 …
。 - 脚本内获取参数:用
$n
(n为数字),$1
表示第一个参数,$2
表示第二个参数,以此类推,$0
表示当前脚本名称。 - 示例:
5.2 特殊字符
特殊字符 | 含义 |
---|---|
$# |
传递到脚本的参数个数 |
$* |
以一个单字符串显示所有向脚本传递的参数 |
$$ |
脚本运行的当前进程ID号 |
$! |
后台运行的最后一个进程的ID号 |
$@ |
与$* 相同,但使用时加引号,会在引号中返回每个参数 |
$? |
显示最后命令的退出状态,0表示无错误,其他值表示有错误 |
5.3 $*
和$@
的区别
- 相同点:都表示传递给脚本的所有参数。
- 不同点
- 不被
""
包含时:均以$1 $2… $n
的形式组成参数列表。 - 被
""
包含时:"$*"
将所有参数作为一个整体,以"$1 $2 … $n"
形式组成整串;"$@"
将各个参数分开,以"$1" "$2" … "$n"
形式组成参数列表。
- 不被
六、Shell程序:运算符
Shell支持算术、关系、逻辑、字符串等运算符,原生/bin/bash
不支持简单数学运算,需借助expr
、(())
、$[]
等实现。
6.1 算术运算符
实现方式 | 语法/示例 | 注意事项 |
---|---|---|
b= expr 2 + 2`(反引号包裹) |
运算数和运算符之间要有空格;完整表达式需用`符号包含(非单引号) | |
(()) |
((a ++)) (自增)、((a=90+1)) |
可直接进行算术运算,语法简洁 |
$(( )) |
val=$((1+1)) |
将运算结果赋值给变量 |
$[] |
val=$[1+2] |
与$(( )) 类似,可直接进行算术运算 |
6.2 关系运算符
- 特性:仅支持数字,不支持字符串(字符串值为数字时除外)。
|运算符|含义|示例(a=10,b=20)|
| ---- | ---- | ---- |
|-eq
|检测两个数是否相等,相等返回true|[ $a -eq $b ]
(返回false)| =
|-ne
|检测两个数是否不相等,不相等返回true|[ $a -ne $b ]
(返回true)|>或<
|-lt
|检测左边的数是否小于右边,是则返回true|[ $a -lt $b ]
(返回true)|<
|-gt
|检测左边的数是否大于右边,是则返回true|[ $a -gt $b ]
(返回false)|>
|-le
|检测左边的数是否小于或等于右边,是则返回true|[ $a -le $b ]
(返回true)|<=
|-ge
|检测左边的数是否大于或等于右边,是则返回true|[ $a -ge $b ]
(返回false)|>=
6.3 逻辑运算符
运算符 | 含义 | 示例 |
---|---|---|
-a |
双方都成立(and),用于if 语句 |
`if [ 1000>100 |
-o |
单方成立(or),用于if 语句 |
[ 1000>100 &&20>40 ]` |
&& |
并且(and),可直接用echo 测试 |
echo "$[10>5 && 10%2==0]" (输出1,代表true) |
6.4 字符串运算符
运算符 | 含义 | 示例(a=“abc”,b=“def”) |
---|---|---|
-n STRING |
字符串长度不为零(非空字符串) | [ -n "$a" ] (返回true) |
-z STRING |
字符串长度为0(空字符串) | [ -z "$a" ] (返回false) |
= |
判断两个字符串是否一样 | [ $a = $b ] (返回false) |
!= |
判断两个字符串是否不一样 | [ $a != $b ] (返回true) |
6.5 文件测试运算符
运算符 | 含义 | 示例 |
---|---|---|
-f |
存在且是普通文件 | [ -f /root/hello.sh ] |
-d |
存在且是目录 | [ -d /root/shelldemo ] |
-s |
文件不为空 | [ -s /root/hello.sh ] |
-e |
文件存在 | [ -e /root/hello.sh ] |
-r |
文件存在并且可读 | [ -r /root/hello.sh ] |
-w |
文件存在并且可写 | [ -w /root/hello.sh ] |
-x |
文件存在并且可执行 | [ -x /root/hello.sh ] |
七、流程控制
程序默认从上向下逐行执行,流程控制可实现选择执行或重复执行代码,Shell支持if…else
、for
、while
、case
等语句。
7.1 if…else
格式1:单支
if [ 条件 ]; then
# 条件成立时执行的命令
fi
- 执行机制:判断一次,条件成立则执行命令,失败则无操作。
- 示例:
#!/bin/bash
num1=$1
num2=$2
if [ $num1 -gt $num2 ]; then
echo "$num1 大于 $num2"
fi
# 执行命令:./ifdemo.sh 10 5,输出:10 大于 5
格式2:双支
if [ 条件 ]; then
# 条件成立时执行的命令1
else
# 条件失败时执行的命令2
fi
- 执行机制:判断一次,条件成立执行命令1,失败执行命令2。
- 示例:
#!/bin/bash
num=10
if [ $((num%2)) == 0 ];then
echo "偶数";
else
echo "奇数";
fi
# 执行命令:sh ifdemo.sh,输出:偶数
格式3:多支
if [ 条件1 ]; then
# 条件1成立执行的命令1
elif [ 条件2 ]; then
# 条件2成立执行的命令2
...
else
# 所有条件都不成立执行的默认命令
fi
- 执行机制:多个判断条件,每个条件对应一个结果;若第一个条件成立,执行对应命令后不再判断后续条件;所有条件均不成立则执行
else
下的命令。 - 示例:
#!/bin/bash
score=$1
if [ $score -ge 90 ]; then
echo "优秀"
elif [ $score -ge 80 ]; then
echo "良好"
elif [ $score -ge 60 ]; then
echo "及格"
else
echo "不及格"
fi
# 执行命令:./ifdemo.sh 85,输出:良好
7.2 for
循环用于重复性执行代码,需避免死循环,通常包含循环初始值、循环条件、修改循环条件。
方式1:数值处理
for ((循环初始值; 循环条件; 修改循环条件))
do
# 循环体代码
done
- 示例1:打印5次Hello World
#!/bin/bash
for ((i=0;i<5;i++))
do
echo "$i - Hello World~"
done
# 输出:0 - Hello World~ 、1 - Hello World~ … 4 - Hello World~
- 示例2:求10以内数值的累加和
#!/bin/bash
count=0 # 存储累加结果
for((i=1;i<=10;i++))
do
count=$(($count+$i));
done
echo "累加后的值:${count}"; # 输出:累加后的值:55
方式2:遍历数据集
for var in 数据1 数据2 数据3 …
do
# 循环体代码($var依次取数据集里的每个值)
done
- 示例1:遍历动物名称
#!/bin/bash
for animal in cat dog elephant
do
echo ${animal}
done
# 输出:cat、dog、elephant
- 示例2:打印指定目录下所有文件名
#!/bin/bash
# 查询/root/shelldemo目录下所有文件名,赋值给filelist变量
filelist=$(ls /root/shelldemo);
# 循环遍历filelist,获取每个文件名
for filename in $filelist
do
echo $filename
done
# 输出该目录下所有文件名,如demo1.sh、hello.sh等
7.3 while
方式1:条件判断循环
while [ expression ]
do
# 循环体命令
# 修改循环条件(避免死循环)
done
- 示例:输入yes/YES停止循环
#!/bin/bash
while [ "$y" != "yes" -a "$y" != "YES" ]
do
echo "请输入yes/YES停止循环:"
read y # 接收键盘录入的值
done
echo "循环停止了!";
# 执行后,输入非yes/YES会继续提示,输入yes/YES则停止循环
方式2:数值循环(用(( ))
)
初始化变量
while((循环条件))
do
# 循环体命令
# 修改循环条件(如let 变量++或((变量++)))
done
let
命令:BASH中用于计算的工具,执行表达式,变量计算无需加$
,如let no++
(自加)、let no--
(自减)。- 示例:计算10以内数值的累加和
#!/bin/bash
count=0 # 记录累加值结果
num=1 # 循环初始值
while((num<=10))
do
count=$[$count+$num]
let num++ # 修改循环条件
done
echo "10以内数值的累加和:${count}"; # 输出:10以内数值的累加和:55