Shell 脚本基础完全指南:语法、调试、运行与实战详解
Shell 脚本基础完全指南:语法、调试、运行与实战详解
一、脚本基础
1、Shell编程介绍与特性
Shell是命令解释程序,也是命令语言解释器。用户输入的命令行需符合Shell语法语义规范,才能被Shell理解执行;其使用的命令语言称为Shell语言。
Shell语言兼具交互式与可编程性:将多个Shell命令写入文件即构成Shell程序(脚本),可通过变量、参数、控制结构组织命令流程,实现复杂工作的自动化。
在Linux系统中,Shell程序广泛用于系统初启、配置、管理与维护,熟练掌握Shell能深入理解系统运行机制,提升系统管理效率。
Shell语言的核心特点
-
解释性语言:无需编译,由Shell进程直接解释执行,即编即用,便捷性高,但运行速度低于编译型语言。
-
基于字符串的语言:仅处理字符串,不支持复杂数据结构与运算,输出均为字符方式。
-
命令级语言:程序由命令(非语句)构成,几乎所有Shell命令与可执行程序均可用于编写脚本;命令丰富且组合能力强,脚本简洁高效。
注意:不同版本的Shell程序(如Bash、Sh)不完全兼容,差异可能细微或显著。
定义补充
Shell程序也称Shell脚本,是由Shell命令构成的文本文件:
- 简单脚本:仅命令序列;
- 高级脚本:包含复杂命令组合、变量、参数、条件命令、控制结构等。
2、变量
(1)什么是变量?
变量是程序中保存用户数据的一块内存空间,变量名是该内存空间的地址。程序执行时,内存空间的内容(变量值)可变化,但变量名固定。
变量是几乎所有编程语言的基础,Shell中变量的核心作用是存储临时数据,供脚本后续调用。
(2)变量的命名规则
- 组成:仅允许字母、数字、下划线(
_
); - 开头:必须以字母或下划线开头(不能以数字开头);
- 长度:Shell无明确限制,但为提高可读性,建议使用短字符串;
- 规范:优先选择有明确意义的英文单词(如
age
表示年龄、salary
表示薪资),避免拼音或无意义字符(如a1
、abc
)。
(3)变量的类型
Shell中变量类型无需显式声明,根据赋值内容自动识别,主要包括以下4类:
类型 | 定义与说明 | 示例代码 |
---|---|---|
整形(int) | 存储整数,直接赋值,无需特殊符号 | age=10 |
浮点型(float) | 存储小数,直接赋值 | salary=3.1 |
字符型 | 存储字符串,可加单引号、双引号或不加引号;字符串含空格时必须加引号 | - 无空格:msg=cy 或 msg="cy" 或 msg='cy' - 有空格: msg="hello cy" (必须加引号) |
布尔型 | 仅两个值:True (真)或 False (假),用于条件判断 | is_ok=True 、has_error=False |
关键:单引号与双引号的区别
- 双引号(" "):弱引用:引号内的特殊字符(如
$
、\
)仍保留原有意义; - 单引号(’ '):强引用:引号内所有特殊字符均取消意义,按原始字符串解析。
示例(实际执行效果):
[root@server ~]# name="cy" # 定义变量name
[root@server ~]# echo "${name} is good" # 双引号保留$的变量引用意义
cy is good
[root@server ~]# echo '${name} is good' # 单引号取消$的意义
${name} is good
3、脚本执行方式
Shell脚本执行有5种方式,核心差异在于是否需要脚本文件具备执行权限:
执行方式 | 语法格式 | 核心说明 |
---|---|---|
绝对路径执行 | /path/to/脚本名(如/root/a.sh ) | 需脚本具备执行权限(chmod +x 脚本名 ),通过完整路径调用。 |
相对路径执行 | ./脚本名(如./a.sh ) | 需脚本具备执行权限,仅在脚本所在目录下使用(. 表示当前目录)。 |
sh/bash命令执行 | sh 脚本名 或 bash 脚本名(如sh a.sh ) | 无需执行权限,通过Sh/Bash解释器直接解析脚本。 |
.(空格)执行 | . 脚本名(如. a.sh ) | 无需执行权限,注意“.”后必须加空格;本质是在当前Shell环境执行脚本。 |
source命令执行 | source 脚本名(如source a.sh ) | 无需执行权限,核心用途是“生效配置文件”(如source /etc/profile ),与“.”执行效果一致。 |
4、Shebang机制
#!
(称为Shebang)用于告诉系统脚本使用的解释器,必须写在脚本文件的第一行。常见解析器路径如下:
#!/bin/bash
:指定Bash解释器(最常用);#!/usr/bin/python
:指定Python解释器;#!/usr/bin/perl
:指定Perl解释器。
二、脚本的组成结构
1、脚本的组成
Shell脚本由“开头声明、注释、命令行”三部分构成,具体规则如下:
组成部分 | 说明 |
---|---|
开头 | 必须通过Shebang指定解释器(如#!/bin/bash ),告知系统解析方式。 |
注释 | 以# 开头的行(#! 除外),用于说明脚本功能、逻辑,不参与执行;建议多写注释提升可读性。 |
命令行规则 | 1. 一行为一条命令; 2. 一行多条命令需用 ; 分隔;3. 长命令可通过 \ 换行(续行符)。 |
示例:最简单的Shell脚本(Hello World)
#!/bin/bash
# 功能:输出Hello World(这是注释)
echo "Hello World!" # 单行命令,输出字符串
2、程序返回值
Shell程序执行后,会产生两类返回值,用于判断执行结果:
- 程序执行的结果:即脚本输出的内容(如
echo
打印的字符串、命令执行的输出); - 程序状态返回代码:范围
0-255
,用于标识执行成功/失败:0
:脚本/命令正确执行;1-255
:脚本/命令错误执行(不同错误码对应不同错误类型,如权限不足、文件不存在等)。
关键:获取状态返回代码
通过特殊变量$?
可获取“上一行命令/脚本的状态返回代码”,示例:
# 执行成功的命令
echo "test"
echo $? # 输出0(表示上一条echo命令执行成功)# 执行失败的命令(访问不存在的文件)
cat /tmp/nonexistent.txt
echo $? # 输出1(表示上一条cat命令执行失败)
三、Shell脚本调试
1、脚本执行的过程解析
当Shell脚本运行时,执行流程如下:
-
查找系统环境变量
ENV
,该变量指定了需加载的环境文件,加载顺序通常为:
/etc/profile
→ 当前用户家目录.bash_profile
→ 当前用户家目录.bashrc
→/etc/bashrc
;注意:如果环境中有变量名相同起冲突时,优先级低的值会覆盖优先级高的值
-
加载完环境变量后,Shell开始执行脚本内容:
- 从上至下、从左至右依次执行每一行命令/语句;
- 若遇到子脚本(脚本嵌套),先执行子脚本,完成后返回父脚本,继续执行后续内容。
2、脚本执行的原理
Shell本身不直接执行命令,而是通过派生子进程让子进程执行命令,核心原因是“降低风险”:
- 子进程执行命令时,即使出现错误(如崩溃),也不会影响父Shell进程;
- 若Shell直接执行命令,一旦命令出错导致Shell崩溃,将无法再解释其他命令(Shell是用户与系统交互的核心)。
- 本质:Shell仅负责“发起命令请求”,子进程负责“执行命令并返回结果”,Shell等待子进程完成后继续后续逻辑。
3、写脚本注意事项
为提升脚本稳定性、可读性、可维护性,需遵循以下8条规则:
-
开头必须加Shebang(如
#!/bin/bash
),指定解释器; -
语法缩进使用4个空格(避免用Tab),逻辑块(如条件、循环)需缩进;多写注释说明功能、参数、逻辑;
-
命名规范:
- 变量名:全局变量大写(如
AGE
),局部变量小写(如local age
); - 函数名:小写(如
calculate_sum
); - 所有名称需体现实际作用(如
salary
表示薪资,避免a1
、b2
等无意义名称);
- 变量名:全局变量大写(如
-
变量作用域:默认变量为全局变量(整个脚本可访问);在函数中需用
local
关键字声明局部变量,避免污染全局作用域; -
调试辅助命令:
set -e
:脚本中遇到“返回码非0的命令”时,立即退出脚本(避免错误扩散);set -x
:执行脚本时,打印每一条命令的执行过程(便于定位错误);
-
脚本上线前必须先测试:在测试环境验证功能、兼容性、错误处理,确认无误后再部署到生产环境;
-
脚本中的命令必须使用绝对路径(如
/bin/echo
而非echo
、/usr/bin/cat
而非cat
),避免因环境变量PATH
配置问题导致命令无法找到; -
引用命令执行结果时,需用反引号(
`命令`
)或$(命令)
(推荐,兼容性更好),示例:current_date=`date +%Y%m%d` #或 current_date=$(date +%Y%m%d) #两种方式均获取当前日期
·current_date=
date +%Y%m%d`` 或
current_date=$(date +%Y%m%d)`(两种方式均获取当前日期)。
4、脚本调试方法
Shell提供专门的调试命令,用于检查脚本语法错误、跟踪执行过程:
调试命令 | 功能说明 |
---|---|
bash -n 脚本名 | 检查脚本是否存在语法错误(如括号不匹配、命令拼写错误),不执行脚本;若无输出,说明语法无错。 |
bash -x 脚本名 | 执行脚本并打印每一步的执行过程(包括变量展开、命令执行细节),用于定位逻辑错误(如变量赋值错误、条件判断错误)。 |
示例:调试脚本
# 假设有脚本test.sh,先检查语法
bash -n test.sh # 无输出,说明语法正确# 跟踪执行过程
bash -x test.sh # 输出每一步命令及变量值,如+ echo "Hello"、+ age=10等
5、调试案例(实战练习)
需求:完成以下操作,熟悉脚本编写与调试:
- 创建目录
/tmp/hrz
; - 切换到
/tmp/hrz
目录; - 在该目录下创建目录
a1b
、b2c
、6cy
; - 在该目录下创建空文件
xy
、x123y
、123
; - 列出当前目录下“以
a
或6
开头”的文件/目录,将结果导入/tmp/file1
; - 列出当前目录下“以字母开头、后接1个任意数字、再后接任意长度字符”的文件/目录,将结果导入
/tmp/file2
。
实现脚本(含调试辅助)
#!/bin/bash
set -x # 打印执行过程(调试用)
set -e # 出错立即退出(避免错误扩散)# 步骤1-2:创建目录并切换
mkdir -p /tmp/hrz # -p:确保目录存在,不存在则创建
cd /tmp/hrz# 步骤3:创建3个目录
mkdir -p a1b b2c 6cy# 步骤4:创建3个空文件(touch用于创建空文件)
touch xy x123y 123# 步骤5:筛选a/6开头的文件/目录,导入file1
# ls:列出内容;grep ^[a6]:匹配以a或6开头的行
ls | grep ^[a6] > /tmp/file1# 步骤6:筛选“字母+数字+任意字符”的文件/目录,导入file2
# ^[[:alpha:]]:以任意字母开头;[[:digit:]]:1个任意数字;.*:任意长度字符
ls | grep ^[[:alpha:]][[:digit:]].* > /tmp/file2echo "操作完成,结果已保存到/tmp/file1和/tmp/file2"
关键正则说明
[[:alpha:]]
:匹配任意大小写字母(等价于[a-zA-Z]
);[[:digit:]]
:匹配任意整数(等价于[0-9]
);^
:行首匹配;.*
:匹配任意长度的任意字符(包括空字符)。
四、运算符
原生Bash不支持简单的数学运算,需通过expr
、let
、$[]
、$(( ))
等工具/语法实现。
1、算术运算符
用于整数运算(Bash原生不支持浮点运算,需借助bc
等工具),假设a
和b
为变量,常见运算符及语法如下:
运算类型 | 语法格式 | 说明 |
---|---|---|
加法 | expr $a + $b | 注意:+ 前后必须加空格;expr 是外部命令,需用反引号或$() 获取结果。 |
减法 | expr $a - $b | 同理,- 前后必须加空格。 |
乘法 | expr $a \* $b | 需用\ 转义* (否则* 会被解析为通配符)。 |
除法 | expr $b / $a | 仅支持整数除法(舍去小数部分,如expr 5 / 2 结果为2)。 |
取余 | expr $b % $a | 求余数(如expr 5 % 2 结果为1)。 |
赋值 | a=$b | 将b 的值赋给a (无需空格)。 |
相等 | [ $a == $b ] | 判断a 与b 是否相等(== 前后需加空格,[ ] 是条件判断符号)。 |
不相等 | [ $a != $b ] | 判断a 与b 是否不相等(同理,!= 前后需加空格)。 |
算术运算的4种实现方式
以“计算A=3
和B=6
的和”为例,4种常用方式:
A=3
B=6# 方式1:let(内置命令,无需空格,直接运算)
let C=$A+$B
echo "let方式:$C" # 输出9# 方式2:$[](内置语法,直接运算,无需空格)
C=$[ $A+$B ]
echo "$[]方式:$C" # 输出9# 方式3:$(( ))(推荐,兼容性好,直接运算)
C=$(( $A+$B ))
echo "$(( ))方式:$C" # 输出9# 方式4:expr(外部命令,需空格和反引号)
C=`expr $A + $B` # 或 C=$(expr $A + $B)
echo "expr方式:$C" # 输出9
例题:计算两个整数的四则运算与取余
脚本功能:定义两个整数,计算并显示其和、差、积、商、余:
#!/bin/bash
# 功能:计算两个整数的四则运算与取余
a=40
b=20# 计算各结果
let jia=$a+$b # 和
let jian=$a-$b # 差
let cheng=$a*$b # 积
let chu=$a/$b # 商(整数)
let yu=$a%$b # 余# 输出结果
echo "a=$a"
echo "b=$b"
echo "a+b=$jia"
echo "a-b=$jian"
echo "a*b=$cheng"
echo "a/b=$chu"
echo "a%b=$yu"
执行结果
[root@hrz2 tmp]# . ./bbb.sh
a=40
b=20
a+b=60
a-b=20
a*b=800
a/b=2
a%b=0
2、逻辑运算
用于判断多个条件的“与/或”关系,核心运算符为&&
(逻辑与)和||
(逻辑或),基于“上一条命令的状态返回码”判断是否执行下一条命令。
(1)逻辑与(&&):AND关系
- 规则:前一条命令执行成功(返回码0)时,才执行后一条命令;若前一条失败(返回码非0),则后一条不执行。
- 场景:用于“依赖前序操作成功的后续操作”(如“安装成功后,才启动服务”)。
(2)逻辑或(||):OR关系
- 规则:前一条命令执行失败(返回码非0)时,才执行后一条命令;若前一条成功(返回码0),则后一条不执行。
- 场景:用于“前序操作失败后的兜底处理”(如“安装失败时,输出错误信息”)。
示例:安装HTTP服务并部署简单网页
脚本功能:通过yum
安装httpd服务,部署首页,启动服务并测试访问:
#!/bin/bash
set -e
# 功能:安装HTTP服务,部署简单网页并测试
yum -y install httpd &>/dev/null && echo "HTTP服务安装成功" || echo "HTTP服务安装失败"
# 说明:
# 1. yum -y install httpd:自动安装httpd(-y跳过确认);
# 2. &>/dev/null:将标准输出(stdout)和标准错误(stderr)都重定向到/dev/null(不显示安装过程);
# 3. &&:安装成功则输出“成功”;||:安装失败则输出“失败”。# 部署简单网页(写入首页内容)
echo "welcome luoqi" > /var/www/html/index.html# 启动服务并设置开机自启
systemctl restart httpd # 重启httpd服务
systemctl enable httpd # 设置开机自启# 测试访问(假设服务器IP为192.168.100.20)
curl http://192.168.100.20
执行说明
- 若
yum
安装成功(返回码0),则打印“HTTP服务安装成功”,继续执行后续的写网页、启动服务、curl测试; - 若
yum
安装失败(如网络问题、源不可用),则打印“HTTP服务安装失败”