Linux Shell 是操作系统的命令行界面(CLI),也是用户与内核交互的桥梁 —— 用户输入命令后,Shell 解析并执行,最终将结果返回。它不仅是执行单个命令的工具,更是强大的脚本语言环境,可自动化重复任务、批量处理数据等。
1)Shell 脚本
1. 脚本入门:第一个 Shell 脚本
规定
#!/bin/bash # 声明脚本解释器(必须放在第一行)
步骤 1:创建脚本文件
touch hello.sh # 创建脚本文件
步骤 2:编写脚本内容(用 vim 编辑)
vim hello.sh
输入以下内容:
#!/bin/bash # 声明脚本解释器(必须放在第一行)
# 这是注释(#开头的行是注释,不执行)
echo "Hello, Linux Shell!" # 输出字符串
echo "当前时间:$(date)" # 执行date命令并输出结果($()表示执行命令)
echo "当前用户:$USER" # 引用环境变量($+变量名)
步骤 3:添加执行权限(直接运行的情况)
脚本默认没有执行权限,需用 chmod 授权:
chmod +x hello.sh # +x 表示添加“执行权限”,是给a(所有用户)添加x
步骤 4:运行脚本
第一种(不用赋予脚本x的权限):采用 bash 或者 sh + 脚本的相对路径或绝对路径
[root@node01 scripts]# bash hello.sh
hello,world
当前时间:2025年 11月 08日 星期六 10:26:39 CST
当前用户:root[root@node01 scripts]# sh hello.sh
hello,world
当前时间:2025年 11月 08日 星期六 10:26:50 CST
当前用户:root
第二种(需要执行权限):直接运行
[root@node01 scripts]# ./hello.sh
-bash: ./hello.sh: 权限不够
这时区看步骤3,然后
./hello.sh # 直接运行(必须加./,表示当前目录下的脚本)
输出结果:
[root@node01 scripts]# ./hello.sh
hello,world
当前时间:2025年 11月 08日 星期六 10:36:34 CST
当前用户:root
第三种,使用 source
与直接运行脚本不同,source 命令不会创建新的子 shell,而是在当前 shell 中直接执行文件内容。
可以把 source 命令想象成"复制粘贴"操作:
- 普通执行脚本:像是把文件发给别人执行(新开终端)
- source 命令:像是把文件内容直接粘贴到你当前的终端中执行
[root@node01 scripts]# source hello.sh
hello,world
当前时间:2025年 11月 08日 星期六 10:46:19 CST
当前用户:root
2)变量
1、全局变量
- 快速查看所有全局变量:
env或printenv; - 查看单个全局变量:
echo $变量名或printenv 变量名; - 区分全局与局部:
export列出全局变量,set列出所有变量; - 自定义全局变量:
export 变量名=值(临时生效,重启终端失效;永久生效需写入配置文件,如~/.bashrc)。
一、查看所有全局变量(含系统 + 用户自定义)
- 系统预定义的全局环境变量(如
PATH、USER、HOME等,对所有 Shell 进程和子进程生效); - 用户自定义的全局变量(需通过
export声明,可被当前 Shell 及后续启动的子进程继承)。
1. env - 查看全局变量
直接列出当前 Shell 中所有 已导出的全局环境变量(不含局部变量),格式简洁,适合快速查看:
env
2. printenv - 查看全局变量,支持单个
功能与 env 几乎一致,支持单独查看某个全局变量(后面加变量名),更灵活:
# 查看所有全局变量
printenv# 查看单个全局变量(推荐用这种方式查特定变量)
printenv PATH # 查看环境变量 PATH
printenv USER # 查看当前用户变量
3. export - 自定义全局变量
列出当前 Shell 中 用户自定义的全局变量 + 系统全局变量,同时会显示变量的导出状态(部分系统会标注 declare -x):
export
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
二、查看单个指定的全局变量
如果知道变量名,直接用以下方式快速查询(无需罗列所有变量):
1. echo $变量名(最常用)
echo $PATH # 查看环境变量 PATH(命令搜索路径)
echo $HOME # 查看用户家目录
echo $SHELL # 查看当前使用的 Shell
echo $MY_GLOBAL_VAR # 查看自定义全局变量
注意:变量名前必须加
$,否则会直接输出变量名字符串(如echo PATH会输出PATH,而非变量值)。
2. printenv 变量名(更严谨,变量不存在时无输出)
printenv PATH # 输出 PATH 的值
printenv NO_SUCH_VAR # 变量不存在时,无任何输出(不会报错)
三、区分:全局变量 vs 局部变量
默认情况下,用户在 Shell 中直接定义的变量是 局部变量(仅当前 Shell 生效,子进程无法继承),需用 export 声明为全局变量:
# 1. 定义局部变量(仅当前 Shell 可用)
local_var="我是局部变量"
echo $local_var # 输出:我是局部变量# 2. 声明为全局变量(当前 Shell + 子进程可用)
export global_var="我是全局变量"# 3. 验证:子进程中能否访问
bash # 启动一个新的子 Shell(子进程)
echo $local_var # 输出空(子进程无法访问局部变量)
echo $global_var # 输出:我是全局变量(子进程可访问全局变量)
exit # 退出子 Shell,回到原 Shell
通过 set 命令可以查看 所有变量(全局 + 局部)+ 函数,对比 env/printenv 可区分全局与局部:
set # 列出所有变量(全局+局部)和函数,输出较多
四、常用系统全局变量(速查)
| 变量名 | 功能描述 |
|---|---|
PATH |
命令搜索路径(Shell 会在这些目录中找可执行命令) |
HOME |
当前用户的家目录(如 root 的家目录是 /root) |
USER |
当前登录的用户名 |
SHELL |
当前使用的 Shell 路径(如 /bin/bash) |
LANG |
系统语言 / 字符编码(如 en_US.UTF-8) |
PWD |
当前工作目录(等同于 pwd 命令的输出) |
OLDPWD |
上一个工作目录(用 cd - 可切换回去) |
LOGNAME |
当前登录用户的用户名(与 USER 通常一致) |
HOSTNAME |
服务器主机名 |
$SECONDS |
Shell 启动后经过的秒数(可用于计时) |
$RANDOM |
生成 0-32767 之间的随机整数 |
2、自定义变量
自定义变量是用户根据需求手动定义的变量,用于存储脚本/命令中的临时数据(如字符串、数字、路径等),是最常用的变量类型。
核心特性
- 需用户手动定义(格式:变量名=值);
- 默认是「局部变量」(仅当前 Shell 生效,子进程不可继承);
- 可通过
export声明为「全局变量」; - 变量名规则:字母/下划线开头,可包含字母、数字、下划线(区分大小写)。
- 撤销变量:使用 unset
- 只读变量,使用 readonly,但是不能使用 unset,
exit退出当前进程就行了
关键用法
1. 定义与使用
# 定义变量(等号前后无空格,值含空格需用引号包裹)
name="Linux" # 字符串变量
age=100 # 数字变量
file_path="/home/user/file.txt" # 路径变量# 使用变量($变量名 或 ${变量名},推荐用 ${} 避免歧义)
echo "名称:${name}" # 输出:名称:Linux
echo "年龄:${age}岁" # 输出:年龄:100岁
echo "文件路径:${file_path}" # 输出:文件路径:/home/user/file.txt
2. 局部变量 → 全局变量(export)
# 定义局部变量(仅当前 Shell 可用)
local_var="我是局部变量"
echo $local_var # 输出:我是局部变量# 声明为全局变量(当前 Shell + 子进程可用)
export global_var="我是全局变量"# 验证:子进程中访问
bash # 启动新的子 Shell(子进程)
echo $local_var # 输出空(子进程无法访问局部变量)
echo $global_var # 输出:我是全局变量(子进程可访问)
exit # 退出子 Shell
3. 变量运算($((...)) 或 expr)
a=10
b=20
sum=$((a + b)) # 算术运算,支持 +-*/%(取余)
echo "sum:${sum}" # 输出:sum:30c=$(( (a + b) * 2 ))
echo "c:${c}" # 输出:c:60
3、特殊变量(Shell 内置,特定功能)
特殊变量是 Shell 内置的「功能型变量」,命名多为单个字符(如 $?、$#),专门用于处理命令行参数、命令执行状态、进程 ID 等场景,脚本开发中高频使用。
核心特性
- 无需定义,Shell 自动赋值;
- 功能固定,不可修改(如
$?只能读取,不能手动赋值); - 多与脚本参数、命令执行结果相关。
常用特殊变量(脚本开发必备)
| 变量名 | 功能描述 | 适用场景 | 示例(脚本中) |
|---|---|---|---|
$0 |
当前脚本/命令的名称 | 脚本中获取自身名称 | 脚本 test.sh 中,echo $0 输出 ./test.sh |
$1~$n |
脚本/命令的第 1~n 个参数 | 接收外部传入参数 | 运行 ./test.sh 张三 20,$1=张三、$2=20 |
| $# | 脚本/命令的参数总数 | 判断参数是否足够 | 上述示例中 echo $# 输出 2 |
| $* | 所有参数(作为一个整体字符串) | 遍历所有参数(整体处理) | 上述示例中 echo $* 输出 张三 20 |
| $@ | 所有参数(每个参数独立为字符串) | 遍历所有参数(逐个处理) | 循环 for arg in $@; do echo $arg; done 输出 张三、20 |
| $? | 上一条命令的执行状态码(0=成功,非0=失败) | 判断命令是否执行成功 | ls /tmp; echo $? 输出 0(成功);ls /nonexist; echo $? 输出 2(失败) |
$$ |
当前 Shell/脚本的进程 ID(PID) | 区分进程、生成临时文件名 | echo $$ 输出 12345(当前进程 PID) |
$! |
上一个后台进程的 PID | 管理后台进程 | nohup ./long.sh &; echo $! 输出后台进程 PID |
$- |
当前 Shell 的运行选项(如 hB 表示开启的功能) |
查看 Shell 状态 | echo $- 输出 himBH |
$_ |
上一条命令的最后一个参数 | 快速复用参数 | echo hello world; echo $_ 输出 world |
示例1:脚本中使用特殊变量
创建脚本 param_demo.sh:
#!/bin/bash
echo "脚本名:$0"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "参数总数:$#"
echo "所有参数(整体):$*"
echo "所有参数(逐个):$@"
echo "当前脚本 PID:$$"
echo "上一个后台进程 PID:$!"
echo "当前Shell运行状态:$-"
echo "上一条命令最后一个参数:$_" # 复用上一条echo,这里是 "当前Shell运行状态:$-"
echo "hello"
echo "上一条命令最后一个参数:$_" # 复用上一条echo,这里是 "hello"# 遍历所有参数(推荐用 $@,支持含空格的参数)
echo -e "\n遍历参数:"
for arg in "$@"; doecho "参数:${arg}"
done# 判断上一条命令是否执行成功($?)
echo -e "\n判断遍历是否成功:"
if [ $? -eq 0 ]; thenecho "遍历成功(状态码:$?)"
elseecho "遍历失败(状态码:$?)"
fi
运行脚本并传入参数:
chmod +x param_demo.sh
./param_demo.sh 张三 20 "北京 朝阳"
输出结果:
脚本名:./param_demo.sh # $0
第一个参数:张三 # $1
第二个参数:20 # $2
参数总数:3 # $#
所有参数(整体):张三 20 北京 朝阳 #$*
所有参数(逐个):张三 20 北京 朝阳 #$@
当前脚本 PID:6842 # $$
上一个后台进程 PID: # $!
当前Shell运行状态:hB
上一条命令最后一个参数:当前Shell运行状态:hB # $_
hello
上一条命令最后一个参数:hello # $_遍历参数:
参数:张三
参数:20
参数:北京 朝阳判断遍历是否成功:
遍历成功(状态码:0) # $?
示例2:使用 $0-10`
创建 hello.sh
#!/bin/bash
echo "hello,world"
a=b
b="www"
c=123
echo "当前时间:$(date)"
echo "当前用户:$USER"
echo "当前变量:$a"
echo "当前\$0:$0"
echo "当前\$1:$1"
echo "当前\$2:$2"
echo "当前\$3:$3"
echo "当前\$4:$4"
echo "当前\$5:$5"
echo "当前\$6:$6"
echo "当前\$7:$7"
echo "当前\$8:$8"
echo "当前\$9:$9"
echo "当前\$10:${10}"
运行
[root@node01 scripts]# ./hello.sh 001 20 "aaa 啊"
hello,world
当前时间:2025年 11月 08日 星期六 15:45:36 CST
当前用户:root
当前变量:b
当前$0:./hello.sh
当前$1:001
当前$2:20
当前$3:aaa 啊
当前$4:
当前$5:
当前$6:
当前$7:
当前$8:
当前$9:
当前$10:{10}
4、三类变量核心差异对比
| 维度 | 预定义变量 | 自定义变量 | 特殊变量 |
|---|---|---|---|
| 来源 | 系统/Shell 内置 | 用户手动定义 | Shell 内置 |
| 定义方式 | 无需定义,直接使用 | 变量名=值(如 a=10) |
无需定义,Shell 自动赋值 |
| 作用 | 获取系统环境、用户信息 | 存储临时数据 | 处理参数、执行状态、PID 等 |
| 可修改性 | 部分可修改(如 $PATH) |
完全可修改(用户控制) | 不可修改(功能固定) |
| 作用范围 | 多为全局变量 | 默认局部,可通过 export 全局 |
随 Shell/脚本进程生效 |
| 命名风格 | 多为大写单词(如 $HOME) |
自定义(字母/下划线开头) | 单个字符(如 $?、$#) |
5、单引号和双引号的区别
在 Shell(如 Bash)中,双引号("")和单引号('')的核心区别是 是否对变量、命令、特殊字符进行「解析/扩展」 —— 单引号是「强引用」(完全原样输出),双引号是「弱引用」(仅保留部分解析能力),具体区别和用法如下:
一、核心区别对比表
| 特性 | 单引号('') |
双引号("") |
|---|---|---|
变量解析($var) |
❌ 不解析,原样输出$var |
✅ 解析变量,替换为变量值 |
命令替换($(cmd)) |
❌ 不解析,原样输出$(cmd) |
✅ 解析命令,替换为命令执行结果 |
转义字符(\) |
❌ 仅单引号本身(\')不能转义,其他\原样输出 |
✅ 仅解析 5 个特殊转义:\$(保留$)、\"(保留")、\\(保留\)、\n(换行)、\t(制表符),其余\原样输出 |
通配符(*、?) |
❌ 不解析,原样输出通配符 | ❌ 双引号内也不解析通配符(通配符仅在无引号/反引号时生效) |
| 空白字符(空格、回车) | ✅ 保留原样(无需额外处理空格) | ✅ 保留原样(同样支持空格原样显示) |
二、逐个特性详细说明
1. 变量解析(最常用区别)
- 单引号:会把
$变量名当作普通字符串,不替换为变量值; - 双引号:会识别
$变量名,并替换为变量的实际内容。
name="张三"
echo 'Hello, $name' # 输出:Hello, $name(单引号不解析$name)
echo "Hello, $name" # 输出:Hello, 张三(双引号解析$name为变量值)
2. 命令替换($(命令) 或 `命令`)
- 单引号:不执行括号内的命令,原样输出命令字符串;
- 双引号:会执行括号内的命令,将结果替换到字符串中。
date_cmd=$(date +%Y-%m-%d) # 变量存储当前日期
echo '今天是 $(date +%Y-%m-%d)' # 输出:今天是 $(date +%Y-%m-%d)(单引号不执行命令)
echo "今天是 $date_cmd" # 输出:今天是 2025-11-09(双引号解析变量,间接获取命令结果)
echo "当前目录:$(pwd)" # 输出:当前目录:/root/scripts(双引号直接解析命令替换)
3. 转义字符(\)
- 单引号:几乎不支持转义,只有单引号本身无法在单引号内直接使用(需用其他方式规避),其他
\都会原样显示; - 双引号:仅支持 5 个特殊转义(
\$、\"、\\、\n、\t),用于保留特殊字符或插入控制字符。
# 单引号转义示例
echo '这是\单引号\的\转义' # 输出:这是\单引号\的\转义(\原样输出)
echo '不能直接用'单引号'' # 报错(单引号内无法直接写')
echo '用'\''拼接单引号' # 输出:用'拼接单引号(正确写法:单引号关闭+转义' + 重新打开单引号)# 双引号转义示例
price=100
echo "价格:\$price 元" # 输出:价格:$price 元(\$保留$符号,不解析变量)
echo "换行\n制表符\t演示" # 输出:换行(换行)制表符(制表符)演示(解析\n和\t)
echo "双引号:\"Hello\"" # 输出:双引号:"Hello"(\\"保留双引号)
echo "反斜杠:\\" # 输出:反斜杠:\(\\保留反斜杠)
4. 空白字符(空格、回车)
两种引号都能保留空白字符的原样,避免被 Shell 当作「参数分隔符」,这是引号的共同作用(无引号时,Shell 会忽略多余空格)。
# 无引号:多余空格被忽略
echo Hello World # 输出:Hello World(多个空格合并为一个)# 单引号/双引号:保留所有空格
echo 'Hello World' # 输出:Hello World(保留 3 个空格)
echo "Hello World" # 输出:Hello World(同样保留 3 个空格)
5. 通配符(*、? 等)
两种引号内的通配符都会被「禁用」,不会被 Shell 解析为文件匹配模式,仅原样输出。
# 无引号:* 匹配当前目录所有文件
echo * # 输出:file1.txt file2.sh scripts(当前目录文件/文件夹)# 单引号/双引号:* 原样输出
echo '*' # 输出:*
echo "*" # 输出:*
三、常见使用场景总结
用单引号('')的场景:
- 字符串中包含
$、(、)、\等特殊字符,且不需要解析(原样输出); - 固定文本(如提示语、不含变量/命令的常量)。
示例:
echo '请输入密码(无需替换$变量)' # 输出:请输入密码(无需替换$变量)
echo '命令格式:ssh user@ip -p 22' # 输出:命令格式:ssh user@ip -p 22(无解析)
用双引号("")的场景:
- 字符串中包含变量(
$var)或命令替换($(cmd)),需要解析其值; - 字符串中包含空格、换行,需要保留原样;
- 字符串中需要保留
$、"、\等特殊字符(用转义符\)。
示例:
user=$(whoami)
echo "当前用户:$user,当前目录:$(pwd)" # 输出:当前用户:root,当前目录:/root/scripts(解析变量和命令)
echo "文件名:\"my file.txt\"" # 输出:文件名:"my file.txt"(保留双引号和空格)
四、易错点提醒
- 单引号内不能直接嵌套单引号:需用
'\''拼接(关闭单引号 + 转义单引号 + 重新打开单引号); - 双引号内的通配符不生效:如果需要文件匹配(如
*.txt),不要用双引号包裹; - 反引号
`cmd`与$(cmd)功能一致,但双引号内推荐用$(cmd)(嵌套更方便)。
一句话总结
- 单引号:所见即所得,完全不解析任何特殊字符;
- 双引号:解析变量和命令,保留空格,仅对 5 个特殊字符转义。
根据是否需要「解析变量/命令」选择即可 —— 不需要解析就用单引号,需要解析就用双引号!
3)运算符 $((表达式))
用于整数运算(Bash 原生不支持浮点数,需借助 bc 工具),
常用语法:$(( 表达式 )) 或 $[ 表达式 ](推荐前者,兼容性更好)。
a=10
b=3# 基础运算
echo "a + b = $((a + b))" # 输出:a + b = 13
echo "a * b = $[a * b]" # 输出:a * b = 30($[] 语法兼容)# 自增自减
echo "a++ = $((a++))" # 输出:a++ = 10(a 变为11)
echo "++a = $((++a))" # 输出:++a = 12(a 变为12)# 浮点数运算(需用 bc 工具,scale 指定小数位数)
echo "10 / 3 = $(echo "scale=2; 10 / 3" | bc)" # 输出:10 / 3 = 3.33
4)条件判断
写法一:[...]、[[...]]条件组合、((...))数值
Shell(以 Bash 为例)的条件判断是脚本编写的核心,用于根据「变量值、文件状态、命令执行结果」等判断逻辑走向(比如 if-else 分支)。核心语法有 3 种:[ ... ](原生语法)、[[ ... ]](Bash 扩展语法,推荐)、(( ... ))(数值判断专用),以下是详细用法(含中文示例,适配新手):
一、核心判断语法对比(先选对语法,避免踩坑)
| 语法格式 | 适用场景 | 优点 | 注意事项 |
|---|---|---|---|
[ 条件 ] |
所有 Shell 兼容(跨平台) | 兼容性强,基础场景够用 | 1. 条件两端必须加空格;2. 变量需加双引号;3. 特殊字符(如 < >)需转义 |
[[ 条件 ]] |
Bash 专属(推荐优先用) | 支持通配符、&&/` |
|
(( 条件 )) |
纯数值判断(比较/运算) | 支持 ==/>/< 等算术符号,简洁 |
仅处理整数,不支持字符串/文件判断 |
✅ 新手建议:优先用 [[ ... ]](字符串/文件判断)和 (( ... ))(数值判断),语法简洁、少踩坑!
二、最常用的判断场景(附中文示例)
1. 数值判断(比较大小/相等)
用 (( ... )) 最直观,支持 ==/!=/>/</>=/<= 等符号,无需记 -eq/-gt 等关键字。
常用判断条件
| 判断逻辑 | 语法((( ... ))) |
示例(a=10, b=5) |
|---|---|---|
| 等于 | ((a == b)) |
((10 == 5)) → 假 |
| 不等于 | ((a != b)) |
((10 != 5)) → 真 |
| 大于 | ((a > b)) |
((10 > 5)) → 真 |
| 小于 | ((a < b)) |
((10 < 5)) → 假 |
| 大于等于 | ((a >= b)) |
((10 >= 10)) → 真 |
| 小于等于 | ((a <= b)) |
((5 <= 10)) → 真 |
示例脚本(判断分数等级)
read -p "请输入你的分数(0-100):" score# 数值判断:用 ((...)) 简洁明了
if ((score >= 90 && score <= 100)); thenecho "优秀!"
elif ((score >= 60 && score < 90)); thenecho "及格!"
elseecho "不及格!"
fi
2. 字符串判断(比较内容/长度)
用 [[ ... ]] 最佳,支持通配符、空格处理,变量必须加双引号(避免空格导致语法错误)。
常用判断条件
| 判断逻辑 | 语法([[ ... ]]) |
示例(str1="张三", str2="李四") |
|---|---|---|
| 字符串相等 | [[ "$str1" == "$str2" ]] |
[[ "张三" == "李四" ]] → 假 |
| 字符串不相等 | [[ "$str1" != "$str2" ]] |
[[ "张三" != "李四" ]] → 真 |
| 字符串为空(长度=0) | [[ -z "$str" ]] |
[[ -z "" ]] → 真(空字符串) |
| 字符串非空(长度≠0) | [[ -n "$str" ]] |
[[ -n "张三" ]] → 真 |
| 字符串包含子串 | [[ "$str" == *"子串"* ]] |
[[ "张三李四" == *"三"* ]] → 真 |
| 字符串以某字符开头 | [[ "$str" == "前缀"* ]] |
[[ "张三" == "张"* ]] → 真 |
| 字符串以某字符结尾 | [[ "$str" == *"后缀" ]] |
[[ "张三" == *"三" ]] → 真 |
示例脚本(验证用户输入)
read -p "请输入用户名(至少3个字符):" usernameif [[ -z "$username" ]]; thenecho "错误:用户名不能为空!"
elif [[ "${#username}" -lt 3 ]]; then # ${#username} 是获取字符串长度echo "错误:用户名长度不能少于3个字符!"
elif [[ "$username" == *" "*. ]]; then # 禁止包含空格echo "错误:用户名不能包含空格!"
elseecho "用户名合法:$username"
fi
3. 文件/目录判断(运维常用)
用 [[ ... ]] 或 [ ... ],通过 -f/-d 等关键字判断文件状态,是脚本自动化运维的核心。
常用判断条件
| 判断逻辑 | 语法([[ ... ]]) |
示例 |
|---|---|---|
| 是否为普通文件(非目录) | [[ -f "文件路径" ]] |
[[ -f "/etc/passwd" ]] → 真 |
| 是否为目录 | [[ -d "目录路径" ]] |
[[ -d "/home" ]] → 真 |
| 文件/目录是否存在 | [[ -e "路径" ]] |
[[ -e "/tmp/test.txt" ]] → 假(若不存在) |
| 文件是否可读写执行 | [[ -r "文件" ]](读)/ [[ -w "文件" ]](写)/ [[ -x "文件" ]](执行) |
[[ -x "/bin/ls" ]] → 真(ls 可执行) |
| 文件是否为空(大小=0) | [[ -s "文件" ]] |
[[ -s "empty.txt" ]] → 假(空文件) |
示例脚本(备份文件前判断)
file="/etc/nginx/nginx.conf" # 要备份的文件
backup_dir="/tmp/backup" # 备份目录# 1. 判断原文件是否存在且是普通文件
if [[ ! -f "$file" ]]; then # ! 表示“非”(取反)echo "错误:文件 $file 不存在!"exit 1 # 退出脚本,状态码 1 表示错误
fi# 2. 判断备份目录是否存在,不存在则创建
if [[ ! -d "$backup_dir" ]]; thenecho "备份目录 $backup_dir 不存在,正在创建..."mkdir -p "$backup_dir" # -p 确保父目录也创建
fi# 3. 执行备份(复制文件到备份目录)
cp "$file" "$backup_dir/nginx.conf.$(date +%Y%m%d)"
echo "备份成功:$backup_dir/nginx.conf.$(date +%Y%m%d)"
4. 命令执行结果判断
判断某个命令是否执行成功(比如 ping 是否通、curl 是否请求成功),核心是利用「命令退出状态码」:
- 命令执行成功 → 退出状态码
0→ 判断为「真」; - 命令执行失败 → 退出状态码非
0→ 判断为「假」。
语法格式
# 格式1:直接写命令(推荐,简洁)
if 命令; then# 命令执行成功的逻辑
fi# 格式2:用 $? (命令执行后的状态码,0=成功)
命令
if (( $? == 0 )); then# 成功逻辑
fi
示例脚本(判断网络是否通畅)
ip="www.baidu.com"# 判断 ping 命令是否执行成功(-c 1 发送1个包,-W 2 超时2秒)
if ping -c 1 -W 2 "$ip" > /dev/null 2>&1; then # >/dev/null 2>&1 隐藏输出echo "网络通畅:$ip 可访问!"
elseecho "网络异常:$ip 无法访问!"
fi
三、条件组合(与/或/非)
当需要多个条件同时满足(与)、任一满足(或)、取反(非)时,用以下语法:
| 逻辑关系 | [[ ... ]] 语法 |
[ ... ] 语法 |
示例(a=10, b=5, str="abc") |
|---|---|---|---|
| 与(同时成立) | [[ 条件1 && 条件2 ]] |
[ 条件1 -a 条件2 ] |
[[ $a -gt $b && -n "$str" ]] → 真 |
| 或(任一成立) | `[[ 条件1 | 条件2 ]]` | |
| 非(取反) | [[ ! 条件 ]] |
[ ! 条件 ] |
[[ ! -f "/tmp/test.txt" ]] → 真(文件不存在) |
✅ 推荐用 [[ ... ]] 的 &&/||,比 [ ... ] 的 -a/-o 更直观、不易出错!
四、新手必避的 3 个坑
-
空格问题:
[[/[后面、]]/]前面必须加空格!
❌ 错误:[[-$a == 10]](无空格)
✅ 正确:[[ -$a == 10 ]](有空格) -
变量引号:字符串/文件路径变量必须加双引号!避免变量含空格导致语法错误。
❌ 错误:[[ $filename == "test.txt" ]](filename 含空格会报错)
✅ 正确:[[ "$filename" == "test.txt" ]] -
特殊字符转义:
[ ... ]中</>需转义(\</\>),[[ ... ]]无需转义!
❌ 错误:[ $a > $b ](>被当作重定向)
✅ 正确:[ $a \> $b ]或[[ $a > $b ]]
五、实战示例:完整的条件判断脚本
#!/bin/bash
# 功能:判断文件是否存在,若存在则显示其权限和大小,否则创建文件read -p "请输入文件路径:" file_path# 1. 判断输入是否为空
if [[ -z "$file_path" ]]; thenecho "错误:文件路径不能为空!"exit 1
fi# 2. 判断文件是否存在
if [[ -f "$file_path" ]]; thenecho "文件 $file_path 已存在:"echo "权限:$(ls -l "$file_path" | awk '{print $1}')" # 提取权限echo "大小:$(du -sh "$file_path" | awk '{print $1}')" # 提取大小
elseecho "文件 $file_path 不存在,正在创建..."touch "$file_path" # 创建文件if (( $? == 0 )); then # 判断 touch 命令是否成功echo "文件创建成功!"elseecho "错误:文件创建失败(可能权限不足)!"fi
fi
运行脚本后,输入文件路径(如 /tmp/test.txt),会根据文件是否存在执行不同逻辑,完美覆盖「字符串判断、文件判断、命令结果判断」!
写法二:test 原始判断命令
test 是 Shell 中最原始的条件判断命令,核心作用和 [ ... ] 完全等价([ 本质就是 test 命令的符号链接),用于判断「数值、字符串、文件状态」是否满足条件,语法简单直接,适合快速验证或兼容老脚本。
一、核心用法(3种等价写法)
test 的语法非常固定,三种写法效果完全一致,选一种习惯的即可:
# 写法1:直接用 test 命令(最直观)
test 条件# 写法2:用 [ ] 包裹(最常用,本质是 test 的简写)
[ 条件 ]# 写法3:用 [[ ]] 包裹(Bash 扩展,兼容 test 的所有条件,更强大)
[[ 条件 ]]
✅ 关键提醒:[ 条件 ] 中,[ 后面和 ] 前面必须加空格,否则报错!
二、test 能判断什么?(3大核心场景)
test 的判断逻辑和之前讲的条件判断一致,只是通过「命令参数」表达条件,常用场景如下(附示例):
1. 数值判断(比较整数)
用 -eq(等于)、-gt(大于)等参数,语法:test 数值1 比较参数 数值2
| 判断逻辑 | test 参数 | 示例(a=10, b=5) | 结果 |
|---|---|---|---|
| 等于 | -eq |
test $a -eq $b |
假(10≠5) |
| 不等于 | -ne |
test $a -ne $b |
真(10≠5) |
| 大于 | -gt |
test $a -gt $b |
真(10>5) |
| 小于 | -lt |
test $a -lt $b |
假(10<5) |
| 大于等于 | -ge |
test $a -ge 10 |
真(10≥10) |
| 小于等于 | -le |
test $b -le 5 |
真(5≤5) |
示例:
a=10
if test $a -gt 8; thenecho "$a 大于 8" # 输出:10 大于 8
fi
2. 字符串判断(比较内容/长度)
用 =(相等)、-z(空字符串)等参数,语法:test "字符串1" 参数 "字符串2"
| 判断逻辑 | test 参数 | 示例(str1="abc", str2="def") | 结果 |
|---|---|---|---|
| 字符串相等 | = 或 == |
test "$str1" = "$str2" |
假(abc≠def) |
| 字符串不相等 | != |
test "$str1" != "$str2" |
真(abc≠def) |
| 字符串为空 | -z |
test -z "" |
真(空字符串) |
| 字符串非空 | -n |
test -n "$str1" |
真(abc非空) |
示例:
username="root"
if test -n "$username" && test "$username" = "root"; thenecho "当前是管理员用户" # 输出:当前是管理员用户
fi
3. 文件/目录判断(运维常用)
用 -f(普通文件)、-d(目录)等参数,语法:test 参数 "文件路径"
| 判断逻辑 | test 参数 | 示例 | 结果 |
|---|---|---|---|
| 是否为普通文件(非目录) | -f |
test -f "/etc/passwd" |
真(是文件) |
| 是否为目录 | -d |
test -d "/home" |
真(是目录) |
| 文件/目录是否存在 | -e |
test -e "/tmp/test.txt" |
假(不存在) |
| 文件是否可执行 | -x |
test -x "/bin/ls" |
真(ls可执行) |
示例:
file="/tmp/test.txt"
if test -f "$file"; thenecho "文件已存在"
elseecho "文件不存在,创建中..."touch "$file" # 创建文件
fi
三、条件组合(与/或/非)
test 本身不支持 &&/|| 直接写在命令内,需用 Shell 的逻辑运算符组合多个 test 命令:
| 逻辑关系 | 语法 | 示例(a=10, b=5) | 结果 |
|---|---|---|---|
| 与(同时成立) | test 条件1 && test 条件2 |
test $a -gt $b && test $a -lt 20 |
真 |
| 或(任一成立) | `test 条件1 | test 条件2` | |
| 非(取反) | ! test 条件 |
! test -f "/tmp/xxx.txt" |
真(文件不存在) |
示例:
# 判断文件存在且可写
file="/tmp/test.txt"
if test -f "$file" && test -w "$file"; thenecho "文件可写入"
elseecho "文件不存在或无写入权限"
fi
四、test 和 [ ] / [[ ]] 的关系(关键理解)
[ 条件 ]就是test 条件的简写,比如[ $a -gt $b ]等价于test $a -gt $b,所以两者的条件参数完全一致;[[ 条件 ]]是 Bash 对test/[ ]的增强版,支持&&/||直接写在内部、支持通配符,更简洁(比如[[ $a -gt $b && $b -lt 20 ]]无需拆成两个命令);- 兼容性:
test和[ ]兼容所有 Shell(如 sh、ksh),[[ ]]仅兼容 Bash。
5)测试运算符 (-n|-z|-r)
这类运算符专门搭配 [[ ... ]]/[ ... ]/test 使用,核心分为「字符串运算符」「文件 / 目录运算符」两大类(数值运算符之前讲过,这里聚焦和 -n 同类型的「状态测试符」),以下是最常用、最实用的汇总(附示例 + 场景):
一、字符串相关运算符(和 -n 最贴近,操作字符串)
专门判断字符串的「长度、相等性、包含关系」等,和 -n 功能互补,日常脚本高频使用:
| 运算符 | 含义(核心功能) | 示例(str1="abc", str2="def", empty="") |
结果 |
|---|---|---|---|
-n "$str" |
字符串非空(长度≠0) | [[ -n "$str1" ]] → 检测 "abc" 是否非空 |
真 |
-z "$str" |
字符串为空(长度=0) | [[ -z "$empty" ]] → 检测空字符串 |
真 |
="$str1" "$str2" |
字符串相等([[ ]]也可用==) |
[[ "$str1" = "abc" ]] → 检测 str1 是否等于 "abc" |
真 |
!="$str1" "$str2" |
字符串不相等 | [[ "$str1" != "$str2" ]] → 检测 "abc" 和 "def" 是否不等 |
真 |
=~ |
字符串匹配正则表达式(仅[[ ]]支持) |
[[ "$str1" =~ ^a ]] → 检测 str1 是否以 "a" 开头 |
真 |
== *"子串"* |
字符串包含子串(仅[[ ]]支持) |
[[ "$str1" == *"b"* ]] → 检测 str1 是否包含 "b" |
真 |
实用示例(字符串校验)
username="admin123"
# 1. 非空 + 长度≥6 + 包含数字
if [[ -n "$username" && ${#username} -ge 6 && "$username" =~ [0-9] ]]; thenecho "用户名合法" # 输出:用户名合法
fi
二、文件/目录相关运算符(运维高频,判断文件状态)
和 -n 逻辑类似,但操作对象是「文件/目录路径」,用于判断文件是否存在、是否可读写、是文件还是目录等,是自动化脚本的核心:
| 运算符 | 含义(核心功能) | 示例(文件路径:file="/etc/passwd", dir="/home") |
结果 |
|---|---|---|---|
-e "$path" |
路径存在(文件/目录均可) | [[ -e "$file" ]] → 检测 /etc/passwd 是否存在 |
真 |
-f "$path" |
是普通文件(非目录/设备文件) | [[ -f "$file" ]] → 检测 /etc/passwd 是否是普通文件 |
真 |
-d "$path" |
是目录 | [[ -d "$dir" ]] → 检测 /home 是否是目录 |
真 |
-r "$path" |
文件可读(当前用户有读权限) | [[ -r "$file" ]] → 检测是否能读 /etc/passwd |
真 |
-w "$path" |
文件可写(当前用户有写权限) | [[ -w "$file" ]] → 检测是否能写 /etc/passwd(root用户为真,普通用户为假) |
视权限而定 |
-x "$path" |
文件可执行(脚本/命令文件) | [[ -x "/bin/ls" ]] → 检测 ls 命令是否可执行 |
真 |
-s "$path" |
文件非空(大小≠0) | [[ -s "$file" ]] → 检测 /etc/passwd 是否有内容 |
真 |
-L "$path" |
是符号链接(软链接) | [[ -L "/etc/rc.local" ]] → 检测是否是软链接 |
真(多数系统) |
-nt "$path1" "$path2" |
path1 比 path2 新(修改时间) | [[ "$file" -nt "/tmp/test.txt" ]] → 检测 passwd 是否比 test.txt 新 |
真(test.txt不存在时也为真) |
-ot "$path1" "$path2" |
path1 比 path2 旧(修改时间) | [[ "$file" -ot "/tmp/test.txt" ]] → 检测 passwd 是否比 test.txt 旧 |
假(test.txt不存在时) |
实用示例(文件操作前校验)
backup_file="/tmp/backup.tar.gz"
# 1. 文件存在 + 非空 + 可读 → 解压
if [[ -f "$backup_file" && -s "$backup_file" && -r "$backup_file" ]]; thentar -zxf "$backup_file" -C /tmpecho "解压成功"
elseecho "文件不存在/空文件/无读权限"
fi
三、核心总结(快速选型)
- 和
-n同属「状态测试符」,核心是「判断某个对象的状态」(字符串状态/文件状态); - 记准「前缀含义」:
-z(zero,空)、-n(not zero,非空)、-f(file,文件)、-d(directory,目录)、-r(read,可读)、-w(write,可写)、-x(execute,可执行); - 使用场景:
- 校验用户输入、处理字符串 → 用「字符串运算符」;
- 操作文件/目录(创建、删除、解压、备份)→ 用「文件/目录运算符」;
- 必加双引号:变量(尤其是文件路径、含空格的字符串)必须加
"$var",避免空格/特殊字符导致语法错误。
这些运算符是 Shell 条件判断的「基础积木」,搭配 &&/||/! 组合,就能覆盖绝大多数脚本场景(比如校验输入、自动化运维、文件处理等)!
6)条件判断
Shell(以 Bash 为例)的流程控制是脚本的核心,用于控制命令执行的顺序(顺序、分支、循环),核心分为 4 大类:if-else(分支判断)、for(循环)、while/until(循环)、case(多分支匹配),以下是简洁实用的中文讲解(含示例,适配日常脚本场景):
一、分支控制:根据条件走不同逻辑
1. if-else(条件分支,最常用)
根据条件真假执行不同命令,支持单分支、双分支、多分支。
核心语法
# 单分支(满足条件才执行)
if 条件; then命令1
fi# 双分支(满足/不满足各执行)
if 条件; then命令1 # 条件真
else命令2 # 条件假
fi# 多分支(多个条件递进判断)
if 条件1; then命令1
elif 条件2; then命令2
else命令3 # 所有条件都假
fi
示例:判断文件是否存在
#!/bin/bash
file="/tmp/test.txt"
if [[ -f "$file" ]]; thenecho "文件已存在"
elif [[ -d "$file" ]]; thenecho "这是目录,不是文件"
elseecho "文件不存在,创建中..."touch "$file"
fi
2. case(多值匹配,替代多个 elif)
用于「变量等于多个固定值」的场景(如菜单选择、参数匹配),比 if-elif-else 简洁。
核心语法
case $变量 in值1)命令1;; # 结束当前分支(必须写)值2|值3) # 多个值用 | 分隔(或逻辑)命令2;;*) # 默认分支(所有值都不匹配时执行)命令3;;
esac # 结束 case(反过来写 case)
示例:简单菜单脚本
echo "请选择操作:1-备份 2-恢复 3-退出"
read choicecase $choice in1)echo "开始备份文件..."# 实际备份命令(如 tar);;2)echo "开始恢复文件..."# 实际恢复命令;;3)echo "退出脚本"exit 0 # 退出脚本;;*)echo "无效选择!只能输入 1/2/3";;
esac
二、循环控制:重复执行命令
1. for 循环(遍历列表/范围,最常用)
适合「已知循环次数」的场景(如遍历文件、数字范围、字符串列表)。
核心语法(3种常用形式)
# 形式1:遍历列表(直接写元素)
for 变量 in 元素1 元素2 元素3; do命令(使用 $变量)
done# 形式2:遍历数字范围(如 1-10)
for 变量 in {起始..结束}; do命令
done# 形式3:遍历文件(如当前目录的 .txt 文件)
for 变量 in 路径通配符; do命令
done#形式4:C 风格 for 循环
for (( 初始值; 循环条件; 变量变化 )); do循环体命令(可使用 $变量 引用当前循环值)
done
示例1:遍历数字(计算 1-10 求和)
sum=0
for i in {1..10}; dosum=$((sum + i))
done
echo "1-10的和:$sum" # 输出:55# let 方法
sum=0
for i in {1..10}; dolet sum=sum + i # 等价于 sum=$((sum + i)),let 自动赋值# 简化写法:let sum+=i(复合运算符,更简洁,推荐)# let sum+=i
done
echo "1-10的和:$sum" # 输出:55
示例2:遍历文件(批量重命名 .txt 文件)
# 把当前目录的所有 .txt 文件改成 .bak(备份)
for file in *.txt; domv "$file" "${file%.txt}.bak" # 替换后缀
done
[root@node01 scripts]# for file in *.sh;do echo "$file" ;done
for_test.sh
hello.sh
if_test.sh
param_demo.sh
示例3:循环 10 次,输出 1-10
# 循环 10 次,输出 1-10
for ((i=1; i<=10; i++)); doecho "当前数字:$i"
done
2. while 循环(条件为真就循环)
适合「未知循环次数,满足条件继续」的场景(如用户输入正确才退出、等待服务启动)。
核心语法
while 条件; do命令
done
示例1:等待用户输入正确密码
correct_pwd="123456"
read -p "请输入密码:" pwdwhile [[ "$pwd" != "$correct_pwd" ]]; doecho "密码错误!"read -p "重新输入:" pwd
done
echo "密码正确,欢迎使用!"
示例2:读取文件内容(逐行遍历)
# 逐行读取 /etc/passwd 文件
while read -r line; doecho "行内容:$line"
done < /etc/passwd # < 表示从文件读取输入
3. until 循环(条件为假就循环,反向 while)
和 while 相反:条件为「假」时继续循环,条件为「真」时退出(用得较少,场景特定)。
核心语法
until 条件; do命令
done
示例:等待网络连通(直到 ping 通百度)
ip="www.baidu.com"
echo "等待 $ip 连通..."until ping -c 1 -W 2 "$ip" > /dev/null 2>&1; dosleep 1 # 每隔1秒试一次
done
echo "$ip 已连通!"
三、循环控制符:改变循环执行流程
在循环中用 break/continue/exit 控制节奏,避免死循环或冗余执行:
| 控制符 | 作用 | 示例场景 |
|---|---|---|
break |
立即退出当前循环(不再执行后续迭代) | 找到目标文件后退出遍历 |
continue |
跳过当前迭代,直接进入下一次循环 | 跳过空文件,只处理非空文件 |
exit N |
直接退出整个脚本(N 是退出状态码,0=成功) | 遇到严重错误时终止脚本 |
示例:遍历文件时跳过空文件
for file in *.txt; doif [[ -s "$file" ]]; thenecho "处理非空文件:$file"elseecho "跳过空文件:$file"continue # 跳过当前空文件,进入下一次循环fi
done
示例:找到目标文件后退出循环
target="important.txt"
for file in *.txt; doif [[ "$file" == "$target" ]]; thenecho "找到目标文件:$file"break # 找到后立即退出循环,不再遍历fi
done
四、实战:完整流程控制脚本(备份文件)
#!/bin/bash
# 功能:批量备份指定目录的 .conf 配置文件到 /tmp/backup# 1. 定义变量
source_dir="/etc" # 源目录
backup_dir="/tmp/backup"
file_type="*.conf" # 要备份的文件类型# 2. 检查源目录是否存在
if [[ ! -d "$source_dir" ]]; thenecho "错误:源目录 $source_dir 不存在!"exit 1 # 退出脚本(状态码1表示错误)
fi# 3. 创建备份目录(不存在则创建)
if [[ ! -d "$backup_dir" ]]; thenmkdir -p "$backup_dir"echo "创建备份目录:$backup_dir"
fi# 4. 遍历文件并备份(跳过空文件)
count=0
for file in "$source_dir"/$file_type; do# 跳过不存在的文件(避免通配符不匹配时的无效路径)[[ ! -f "$file" ]] && continue# 跳过空文件if [[ -s "$file" ]]; thencp "$file" "$backup_dir/"echo "备份成功:$file"((count++)) # 计数+1fi
done# 5. 输出结果
if ((count > 0)); thenecho "备份完成!共备份 $count 个文件,路径:$backup_dir"
elseecho "未找到可备份的 $file_type 文件"
fi
五、核心总结(新手必记)
- 分支选择:
- 简单条件 →
if-else; - 多固定值匹配 →
case(比elif简洁)。
- 简单条件 →
- 循环选择:
- 已知列表/范围 →
for(最常用); - 条件触发循环 →
while; - 条件不满足才循环 →
until(少用)。
- 已知列表/范围 →
- 避坑要点:
- 循环变量加双引号(如
"$file"),避免空格报错; case每个分支必须以;;结束;- 用
read -r读取文件(保留特殊字符); - 避免死循环:确保循环条件最终会变为「假」(如
while加退出逻辑)。
- 循环变量加双引号(如
流程控制的核心是「按场景选对结构」,多练几次上面的示例(求和、备份、菜单),就能熟练应对绝大多数 Shell 脚本场景!
7)let 整数算术运算
在 Shell(Bash)中,let 是专门用于 整数算术运算 的命令,同时支持简单的 逻辑判断(本质是通过算术运算的「退出状态码」实现逻辑判定),核心作用是「执行整数运算+自动赋值」,语法简洁,适合简单的数值计算和逻辑判断场景。
一、核心作用:算术运算 + 自动赋值
let 最基础的用法是替代 $((...)) 进行整数运算,且无需手动赋值(运算结果直接赋给变量),语法比 $((...)) 更简洁。
1. 基础语法
let 表达式 # 表达式中变量无需加 $,直接写变量名
- 支持的算术运算符:
+(加)、-(减)、*(乘)、/(整数除)、%(取余)、++(自增)、--(自减)、**(幂运算,Bash 4.0+); - 运算结果直接赋给表达式中的变量(无需
变量=$((...))这种写法); - 仅支持整数运算,不支持浮点数(浮点数需用
bc工具)。
2. 基础运算示例
# 1. 简单赋值运算(等价于 a=$((3+5)))
let a=3+5
echo "a = $a" # 输出:a = 8# 2. 自增/自减(等价于 i=$((i+1)))
i=10
let i++ # 自增(i 变为 11)
echo "i++ = $i" # 输出:i++ = 11let i-- # 自减(i 变为 10)
echo "i-- = $i" # 输出:i-- = 10# 3. 复合运算(+=、-=、*= 等)
let b=2
let b*=3 # 等价于 b=$((b*3)) → 2*3=6
echo "b *= 3 → $b" # 输出:b *= 3 → 6# 4. 幂运算(Bash 4.0+)
let c=2**3 # 2的3次方=8
echo "2**3 = $c" # 输出:2**3 = 8
二、关键用法:结合逻辑判断(核心关联你的疑问)
let 本身不直接支持 &&/|| 这类逻辑符,但它的 退出状态码 可以当作「逻辑结果」使用:
- 当
let运算表达式的 结果为 0 → 退出状态码1(逻辑「假」); - 当
let运算表达式的 结果非 0 → 退出状态码0(逻辑「真」);
简单说:let 表达式 执行后,可通过 if 判断其是否「逻辑为真」(本质是判断运算结果是否非 0)。
1. 逻辑判断示例(替代数值比较)
a=10
b=5# 示例1:判断 a > b(等价于 ((a > b)) 或 [ $a -gt $b ])
# 表达式 a - b = 10-5=5(非0)→ 逻辑真
if let "a - b"; then # 注意:表达式含空格时需加引号echo "a > b" # 输出:a > b
fi# 示例2:判断 a == b(a - b = 0 → 逻辑假)
if ! let "a - b"; then # ! 取反(假→真)echo "a != b" # 输出:a != b
fi# 示例3:结合比较运算符(直接写 ==、>、< 等,需加引号)
if let "a > b && a < 20"; then # 逻辑与:a>b 且 a<20echo "a 在 (b, 20) 之间" # 输出:a 在 (b, 20) 之间
fi
2. 逻辑运算的本质(退出状态码)
Shell 中所有命令的执行结果都有「退出状态码」($? 查看,0=成功,非0=失败):
let "10-5"→ 运算结果 5(非0)→ 退出状态码0(成功)→if判断为「真」;let "10-10"→ 运算结果 0 → 退出状态码1(失败)→if判断为「假」;
可以通过 $? 验证:
let "10-5"
echo $? # 输出:0(结果非0 → 状态码0 → 真)let "10-10"
echo $? # 输出:1(结果0 → 状态码1 → 假)
三、let 的进阶用法(多表达式、引号规则)
1. 一行执行多个表达式(用逗号 , 分隔)
let x=1, y=x+2, z=y*3 # 依次执行 3 个表达式
echo "x=$x, y=$y, z=$z" # 输出:x=1, y=3, z=9
2. 引号规则(避免特殊字符解析)
- 表达式含空格、
>/<、&&/||等特殊字符时,必须加引号(单引号/双引号均可); - 表达式无空格时,可省略引号。
# 正确:含空格,加引号
let "a = (10 + 5) * 2"
echo $a # 输出:30# 正确:含逻辑与 &&,加引号
let "b=3, c=4"
if let "b > 2 && c < 5"; thenecho "b>2 且 c<5" # 输出:b>2 且 c<5
fi# 错误:含空格未加引号,Shell 会解析为多个参数
# let a = 10 + 5 → 报错(= 前后有空格,未加引号)
四、let 与其他算术方式的区别(怎么选?)
| 方式 | 语法示例 | 核心特点 | 适用场景 |
|---|---|---|---|
let |
let a=10+5 |
简洁,自动赋值,支持逻辑判断 | 简单整数运算、快速逻辑判断 |
$((...)) |
a=$((10+5)) |
可嵌套,支持复杂表达式 | 复杂运算、变量插值(如 echo $((a+b))) |
expr |
a=$(expr 10 + 5) |
POSIX 兼容,需转义 * |
兼容老 Shell(如 sh) |
选择建议:
- 简单运算(如自增、加减乘除)→ 用
let(最简洁); - 运算结果需直接输出或嵌套表达式 → 用
$((...)); - 需兼容非 Bash Shell(如 sh)→ 用
expr(少用)。
五、新手必避的坑
- 不支持浮点数:
let a=3.14会报错,浮点数需用echo "3.14+2.86" | bc; - 变量不加
$:let $a=10错误,必须写let a=10(表达式中变量直接写名称); - 特殊字符加引号:含空格、
>/<、&&等时,必须加引号,否则报错; - 逻辑判断的反向性:
let 表达式结果为 0 → 逻辑假,结果非 0 → 逻辑真(别搞反)。
总结
let 是 Shell 中「轻量整数运算工具」,核心功能:
- 简化整数运算(自动赋值,比
$((...))简洁); - 间接实现逻辑判断(通过退出状态码,替代
((...))进行数值比较);
日常脚本中,若只是简单运算或数值逻辑判断,用 let 更高效;若需复杂表达式或嵌套,优先用 $((...))。记住「结果非0=真,结果0=假」的逻辑规则,就能灵活结合 if 进行条件判断啦!