在 Shell 脚本中,&&
和 ||
是逻辑操作符,用于根据前一个命令的退出状态(成功或失败)决定是否执行后续命令。这种语法称为 命令链(Command Chaining)。
语法解析
[ -d student.txt ] && echo yes || echo no
[ -d student.txt ]
- 这是一个条件测试命令,用于检查
student.txt
是否是一个目录(-d
参数)。 - 如果目录存在,返回退出状态
0
(成功);否则返回非0
(失败)。
- 这是一个条件测试命令,用于检查
&&
(逻辑与)- 仅当左侧命令成功时(退出状态为
0
),才会执行右侧的命令。 - 示例:
若目录存在,则执行[ -d student.txt ] && echo yes
echo yes
;若不存在,跳过echo yes
。
- 仅当左侧命令成功时(退出状态为
||
(逻辑或)- 仅当左侧命令失败时(退出状态非
0
),才会执行右侧的命令。 - 示例:
若目录不存在,则执行[ -d student.txt ] || echo no
echo no
;若存在,跳过echo no
。
- 仅当左侧命令失败时(退出状态非
组合效果
目录存在时:
[ -d student.txt ]
成功 → 执行&&
后的echo yes
→echo yes
成功 → 跳过||
后的echo no
。
输出:yes
目录不存在时:
[ -d student.txt ]
失败 → 跳过&&
后的echo yes
→ 由于左侧整体失败,执行||
后的echo no
。
输出:no
等价于 if-else 语句
上述命令链等价于以下 if-else
结构:
if [ -d student.txt ]; then
echo yes
else
echo no
fi
注意事项
&&
和||
的优先级
&&
的优先级高于||
,因此命令按以下分组执行:
( [ -d student.txt ] && echo yes ) || echo no
命令退出状态是关键
- 每个命令的退出状态决定后续操作(
0
=成功,非0
=失败)。 - 例如,若
echo yes
意外失败(极罕见),也会触发|| echo no
。
- 每个命令的退出状态决定后续操作(
其他常见用法
# 1. 仅当命令成功时执行操作
rm file.txt && echo "删除成功"
# 2. 仅当命令失败时执行操作
cp a.txt b.txt || echo "复制失败"
# 3. 复杂组合(注意括号分组)
make && ( echo "编译成功"; ./app ) || echo "编译失败"
通过命令链可以简洁地实现条件逻辑,但复杂的场景建议使用 if-else
增强可读性。
在 Shell 脚本中,单层中括号 [ ]
和双层中括号 [[ ]]
都是条件测试语法,但 [[ ]]
是 Bash 的增强版测试命令,提供了更强大、更安全的功能。以下是主要区别和优势:
1. 避免变量扩展问题(更安全)
[[ ]]
会自动处理变量中的空格和特殊字符,不需要额外加引号:
file="my file.txt"
# 单括号会报错(解析成 [ -f my file.txt ])
[ -f $file ] && echo "Exists" # ❌ 错误:too many arguments
# 双括号正确处理
[[ -f $file ]] && echo "Exists" # ✅ 正确
2. 支持高级模式匹配
[[ ]]
支持通配符扩展和正则表达式:
# 通配符匹配(* 匹配任意字符)
[[ "hello.jpg" == *.jpg ]] && echo "JPEG file" # ✅ 输出
# 正则表达式匹配(=~ 操作符)
[[ "hello123" =~ ^[a-z]+[0-9]+$ ]] && echo "Valid ID" # ✅ 输出
3. 更直观的逻辑运算符
可直接使用 &&
/||
代替 -a
/-o
:
# 单括号(必须用 -a/-o)
[ -f file.txt -a -r file.txt ] && echo "Readable file"
# 双括号(可直接用 &&/||)
[[ -f file.txt && -r file.txt ]] && echo "Readable file" # ✅ 更直观
4. 支持字符串比较运算符
字符串比较时不需要转义:
# 单括号需转义 >(否则被识别为重定向)
[ "apple" \> "banana" ] && echo "True"
# 双括号无需转义
[[ "apple" > "banana" ]] && echo "True" # ✅ 更简洁
5. 防止路径名扩展
[[ ]]
不会展开通配符(如 *
):
# 当前目录有 a.txt, b.txt
[ "*.txt" == *.txt ] && echo "Equal" # ❌ 实际比较 "*.txt" == "a.txt b.txt"
[[ "*.txt" == *.txt ]] && echo "Equal" # ✅ 正确比较字符串(输出 Equal)
6. 增强的数值比较
支持更自然的数值比较语法:
num=10
[[ num -gt 5 ]] && echo "大于 5" # ✅ 传统写法
(( num > 5 )) && echo "大于 5" # ✅ 推荐数值比较写法(双括号专用于算术)
何时使用?
场景 | 建议语法 |
---|---|
需要兼容 /bin/sh |
[ ] (POSIX) |
Bash 脚本(推荐) | [[ ]] |
数值比较 | (( )) |
经典示例对比
# 检查文件是否存在且可读(兼容 POSIX)
[ -f "my file.txt" -a -r "my file.txt" ] && echo "OK"
# Bash 推荐写法(更安全简洁)
[[ -f my\ file.txt && -r my\ file.txt ]] && echo "OK"
💡 总结:在 Bash 脚本中优先使用
[[ ]]
,它能避免许多常见陷阱,提供更强大的模式匹配能力,同时代码更简洁直观。但若需兼容 POSIX Shell(如dash
),则需使用[ ]
。