// Linux · 2025-02-02

Shell 运算符完全指南

本文系统梳理 Bash Shell 中各类运算符的语法、用法、优先级与实战场景,涵盖算术运算、比较运算、逻辑运算、位运算、字符串运算及文件测试运算符,帮助你全面掌握 Shell 脚本中的运算逻辑。


一、运算符分类总览

Shell 中的运算符按功能可分为六大类:

类别 主要用途 典型场景
算术运算符 整数数学计算 计数、累加、取模
比较运算符 数值/字符串大小判断 条件分支、循环控制
逻辑运算符 多条件组合判断 复杂条件筛选
位运算符 二进制位级操作 权限掩码、标志位处理
字符串运算符 字符串内容判断 文本处理、参数校验
文件测试运算符 文件属性检查 文件存在性、权限判断
mindmap
  root((Shell 运算符))
    算术运算符
      + - * / % **
      ++ --
      += -= *= /=
    比较运算符
      数值比较
        -eq -ne -gt -lt -ge -le
      字符串比较
        = != < >
    逻辑运算符
      && || !
      -a -o
    位运算符
      & | ^ ~ << >>
    字符串运算符
      -z -n
      =~ 正则匹配
    文件测试运算符
      -e -f -d -r -w -x
      -s -L -nt -ot

二、算术运算符

2.1 基本算术运算

Shell 中进行算术运算主要有三种方式:

方式 语法 示例 说明
$(( )) $((表达式)) echo $((5 + 3)) 推荐,支持完整 C 风格运算
$[ ] $[表达式] echo $[5 + 3] 旧语法,已废弃
let let 变量=表达式 let a=5+3 不输出结果,仅赋值
expr expr 表达式 expr 5 + 3 外部命令,效率较低

2.2 算术运算符表

运算符 含义 示例 结果
+ 加法 $((10 + 3)) 13
- 减法 $((10 - 3)) 7
* 乘法 $((10 * 3)) 30
/ 整数除法 $((10 / 3)) 3
% 取模(余数) $((10 % 3)) 1
** 幂运算 $((2 ** 4)) 16
++ 自增 a=5; echo $((++a)) 6
-- 自减 a=5; echo $((--a)) 4

2.3 赋值运算符

运算符 含义 示例 等价于
= 赋值 a=10 —
+= 加后赋值 a+=5 a=$((a + 5))
-= 减后赋值 a-=3 a=$((a - 3))
*= 乘后赋值 a*=2 a=$((a * 2))
/= 除后赋值 a/=2 a=$((a / 2))
%= 模后赋值 a%=3 a=$((a % 3))

2.4 算术运算示例

#!/bin/bash
a=17
b=5

# 基本运算
echo "加法:   $((a + b))"    # 22
echo "减法:   $((a - b))"    # 12
echo "乘法:   $((a * b))"    # 85
echo "除法:   $((a / b))"    # 3  (整数除法)
echo "取模:   $((a % b))"    # 2
echo "幂运算: $((2 ** 3))"   # 8

# 复合赋值
let "a += 10"
echo "a += 10 后: $a"       # 27

# 自增自减
x=5
echo "++x: $((++x))"        # 6 (先增后用)
echo "x++: $((x++))"        # 6 (先用后增)
echo "x:   $x"              # 7

注意:Shell 算术运算只支持整数,浮点数运算需借助 bc 或 awk。


三、比较运算符

3.1 数值比较运算符

用于在 [ ] 或 [[ ]] 中进行整数比较。

运算符 含义 示例 说明
-eq 等于 [ $a -eq $b ] equal
-ne 不等于 [ $a -ne $b ] not equal
-gt 大于 [ $a -gt $b ] greater than
-lt 小于 [ $a -lt $b ] less than
-ge 大于等于 [ $a -ge $b ] greater or equal
-le 小于等于 [ $a -le $b ] less or equal

3.2 字符串比较运算符

运算符 含义 示例 说明
= 字符串相等 [ "$a" = "$b" ] 在 [] 中使用
== 字符串相等 [[ "$a" == "$b" ]] 在 [[]] 中使用,支持模式匹配
!= 字符串不相等 [ "$a" != "$b" ] —
< 字符串小于 [[ "$a" < "$b" ]] 按 ASCII 码比较
> 字符串大于 [[ "$a" > "$b" ]] 按 ASCII 码比较
-z 字符串为空 [ -z "$str" ] zero length
-n 字符串非空 [ -n "$str" ] non-zero length

3.3 数值比较 vs 字符串比较

#!/bin/bash
a=10
b=2

# 数值比较:10 > 2 为 true
if [ "$a" -gt "$b" ]; then
    echo "数值比较: $a > $b 成立"
fi

# 字符串比较:"10" < "2" 为 true(按ASCII: '1' < '2')
if [[ "$a" < "$b" ]]; then
    echo "字符串比较: '$a' < '$b' 成立(ASCII顺序)"
fi

关键区别:数值比较用 -eq/-gt 等,字符串比较用 =/!=/< 等,不可混用!

3.4 空字符串判断

#!/bin/bash
str=""

# 判断为空
if [ -z "$str" ]; then
    echo "字符串为空"
fi

# 判断非空
str="hello"
if [ -n "$str" ]; then
    echo "字符串非空: $str"
fi

# 更简洁的非空判断
if [ "$str" ]; then
    echo "字符串有值"
fi

重要:测试字符串时务必加引号,否则空字符串会导致语法错误。


四、逻辑运算符

4.1 逻辑运算符表

运算符 含义 适用环境 示例
! 逻辑非(取反) [ ] / [[ ]] [ ! -f file ]
-a 逻辑与(AND) [ ] 内 [ $a -gt 0 -a $a -lt 100 ]
-o 逻辑或(OR) [ ] 内 [ $a -lt 0 -o $a -gt 100 ]
&& 逻辑与(AND) [[ ]] / 命令间 [[ $a -gt 0 && $a -lt 100 ]]
|| 逻辑或(OR) [[ ]] / 命令间 [[ $a -lt 0 || $a -gt 100 ]]

4.2 [] vs [[]] 中的逻辑运算

#!/bin/bash
score=75

# 在 [] 中使用 -a / -o
if [ "$score" -ge 60 -a "$score" -le 100 ]; then
    echo "[] 方式: 成绩合格"
fi

# 在 [[]] 中使用 && / ||(更直观,推荐)
if [[ "$score" -ge 60 && "$score" -le 100 ]]; then
    echo "[[]] 方式: 成绩合格"
fi

# 命令间的短路求值
[ -f "config.txt" ] && echo "文件存在" || echo "文件不存在"

4.3 短路求值(命令级)

#!/bin/bash

# && : 前命令成功才执行后命令
cd /tmp && ls        # cd 成功后才执行 ls

# || : 前命令失败才执行后命令
cd /notexist || echo "目录不存在"

# 组合使用
[ -f "file.txt" ] && echo "存在" || echo "不存在"

4.4 逻辑运算真值表

flowchart TD
    subgraph AND运算
        A1[A] --> B1{B}
        B1 -->|true| C1[结果: true]
        B1 -->|false| D1[结果: false]
        A1 -->|false| D1
    end

    subgraph OR运算
        A2[A] --> B2{B}
        B2 -->|true| C2[结果: true]
        B2 -->|false| D2{结果}
        A2 -->|true| C2
        D2 -->|A=false| E2[结果: false]
    end

    subgraph NOT运算
        A3[A] --> B3{取反}
        B3 -->|true| C3[结果: false]
        B3 -->|false| D3[结果: true]
    end

五、位运算符

5.1 位运算符表

运算符 含义 示例 说明
& 按位与 $((5 & 3)) 两位都为1则为1
| 按位或 $((5 | 3)) 有一位为1则为1
^ 按位异或 $((5 ^ 3)) 两位不同则为1
~ 按位取反 $((~5)) 0变1,1变0
<< 左移 $((5 << 2)) 左移n位,相当于 x2^n
>> 右移 $((16 >> 2)) 右移n位,相当于 /2^n

5.2 位运算详解

#!/bin/bash

# 按位与: 5(101) & 3(011) = 1(001)
echo "5 & 3 = $((5 & 3))"    # 1

# 按位或: 5(101) | 3(011) = 7(111)
echo "5 | 3 = $((5 | 3))"    # 7

# 按位异或: 5(101) ^ 3(011) = 6(110)
echo "5 ^ 3 = $((5 ^ 3))"    # 6

# 按位取反: ~5 = -6
# 原理: ~x = -x - 1
echo "~5 = $((~5))"          # -6
echo "~0 = $((~0))"          # -1

# 左移: 5(101) << 2 = 20(10100)
echo "5 << 2 = $((5 << 2))"  # 20 (5 * 4)

# 右移: 16(10000) >> 2 = 4(100)
echo "16 >> 2 = $((16 >> 2))" # 4 (16 / 4)

5.3 位运算应用场景

#!/bin/bash

# 场景1: 权限掩码处理
permission=755
owner=$((permission >> 6 & 7))     # 7 (111)
group=$((permission >> 3 & 7))     # 5 (101)
other=$((permission & 7))           # 5 (101)
echo "权限分解: owner=$owner, group=$group, other=$other"

# 场景2: 判断奇偶
num=17
if [ $((num & 1)) -eq 1 ]; then
    echo "$num 是奇数"
else
    echo "$num 是偶数"
fi

# 场景3: 设置/清除标志位
FLAG_READ=1    # 001
FLAG_WRITE=2   # 010
FLAG_EXEC=4    # 100

# 设置标志
flags=0
flags=$((flags | FLAG_READ | FLAG_WRITE))  # 001 | 010 = 011 (3)
echo "当前标志: $flags"

# 检查标志
if [ $((flags & FLAG_READ)) -ne 0 ]; then
    echo "READ 标志已设置"
fi

# 清除标志
flags=$((flags & ~FLAG_WRITE))  # 011 & ~010 = 011 & 101 = 001
echo "清除 WRITE 后: $flags"

六、文件测试运算符

6.1 文件存在性与类型

运算符 含义 示例
-e 文件/目录存在 [ -e file ]
-f 是普通文件 [ -f file ]
-d 是目录 [ -d dir ]
-L 是符号链接 [ -L link ]
-b 是块设备文件 [ -b /dev/sda ]
-c 是字符设备文件 [ -c /dev/tty ]
-p 是命名管道(FIFO) [ -p pipe ]
-S 是套接字文件 [ -S socket ]

6.2 文件权限测试

运算符 含义 示例
-r 可读 [ -r file ]
-w 可写 [ -w file ]
-x 可执行 [ -x script.sh ]
-s 文件大小非0 [ -s file ]
-u 设置了SUID [ -u /usr/bin/passwd ]
-g 设置了SGID [ -g file ]
-k 设置了粘滞位 [ -k /tmp ]

6.3 文件比较

运算符 含义 示例
-nt 文件1比文件2新 [ file1 -nt file2 ]
-ot 文件1比文件2旧 [ file1 -ot file2 ]
-ef 是同一文件(硬链接) [ file1 -ef file2 ]

6.4 文件测试实战

#!/bin/bash
FILE="/etc/passwd"

# 综合文件检查
if [ -e "$FILE" ]; then
    echo "$FILE 存在"

    if [ -f "$FILE" ]; then
        echo "  -> 是普通文件"
    fi

    if [ -r "$FILE" ]; then
        echo "  -> 可读"
    fi

    if [ -s "$FILE" ]; then
        echo "  -> 文件非空"
        echo "  -> 大小: $(wc -c < "$FILE") 字节"
    fi
else
    echo "$FILE 不存在"
fi

# 创建文件前的安全检查
CONFIG="app.conf"
if [ -f "$CONFIG" ]; then
    if [ "$CONFIG" -nt "app.conf.bak" ]; then
        echo "配置文件已更新"
    else
        echo "使用备份恢复..."
        cp "app.conf.bak" "$CONFIG"
    fi
else
    echo "创建默认配置..."
    echo "# 默认配置" > "$CONFIG"
fi

七、运算符优先级

7.1 优先级总表(从高到低)

flowchart LR
    A["运算符优先级"] --> B1["1. 括号 ()"]
    A --> B2["2. 单目运算符 ! ~ ++ --"]
    A --> B3["3. 算术运算符 ** * / % + -"]
    A --> B4["4. 移位运算符 << >>"]
    A --> B5["5. 比较运算符 < <= > >="]
    A --> B6["6. 相等运算符 == !="]
    A --> B7["7. 位运算符 & ^ |"]
    A --> B8["8. 逻辑与 &&"]
    A --> B9["9. 逻辑或 ||"]
    A --> B10["10. 条件运算符 ?:"]
    A --> B11["11. 赋值运算符 = += -= 等"]

7.2 优先级速查

优先级 运算符 结合性
最高 ( ) 左到右
2 ++ --(前缀) 右到左
3 ! ~ 右到左
4 ** 右到左
5 * / % 左到右
6 + - 左到右
7 << >> 左到右
8 < <= > >= 左到右
9 == != 左到右
10 & 左到右
11 ^ 左到右
12 | 左到右
13 && 左到右
14 || 左到右
15 ?: 右到左
最低 = += -= 等 右到左

7.3 优先级陷阱示例

#!/bin/bash

# 陷阱1: 不加括号导致意外结果
a=5
b=3
c=2

# 本意: (a + b) * c = 16
# 实际: a + (b * c) = 11
echo "不加括号: $((a + b * c))"     # 11
echo "加括号:   $(((a + b) * c))"   # 16

# 陷阱2: 逻辑运算优先级
x=10
# && 优先级高于 ||,但建议始终加括号
if [ "$x" -gt 5 ] && [ "$x" -lt 20 ] || [ "$x" -eq 100 ]; then
    echo "条件满足"
fi

# 更清晰的写法
if [[ ("$x" -gt 5 && "$x" -lt 20) || "$x" -eq 100 ]]; then
    echo "条件满足(清晰版)"
fi

八、条件表达式:test、[]、[[]] 对比

8.1 三种条件测试方式

特性 test [ ] [[ ]]
类型 命令 内置命令 关键字
支持 && || 否 否(用 -a -o) 是
支持 < > 需转义 需转义 直接支持
支持 == 模式匹配 否 否 是
支持 =~ 正则 否 否 是
字符串比较 支持 支持 支持
数值比较 支持 支持 支持
性能 一般 一般 最快
空变量安全 需引号 需引号 更安全

8.2 性能对比

# 性能测试(10万次循环)
# test 方式: 约 0.658s
# [ ] 方式:   约 0.609s
# [[ ]] 方式: 约 0.311s

# 结论:[[ ]] 性能最优,功能最强,推荐优先使用

8.3 实战对比

#!/bin/bash
str="hello world"

# test 命令
test "$str" = "hello world" && echo "test: 匹配"

# [ ] 方式
[ "$str" = "hello world" ] && echo "[]: 匹配"

# [[ ]] 方式(支持模式匹配)
[[ "$str" == h* ]] && echo "[[]]: 匹配前缀 h"
[[ "$str" =~ ^h.*d$ ]] && echo "[[]]: 正则匹配成功"

# [[ ]] 空变量更安全
empty=""
[[ "$empty" == "" ]] && echo "空字符串判断安全"

# [[ ]] 不需要对 < > 转义
[[ "apple" < "banana" ]] && echo "apple < banana"

九、正则表达式运算符 =~

9.1 基本用法

#!/bin/bash

email="user@example.com"

# 正则匹配
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "$email 是有效的邮箱格式"
else
    echo "$email 邮箱格式无效"
fi

# 提取匹配内容
string="Version: 2.5.1"
if [[ "$string" =~ Version:\ ([0-9]+\.[0-9]+\.[0-9]+) ]]; then
    echo "版本号: ${BASH_REMATCH[1]}"
fi

9.2 BASH_REMATCH 数组

#!/bin/bash
text="IP: 192.168.1.100"

if [[ "$text" =~ ([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then
    echo "完整匹配: ${BASH_REMATCH[0]}"
    echo "第1段:    ${BASH_REMATCH[1]}"
    echo "第2段:    ${BASH_REMATCH[2]}"
    echo "第3段:    ${BASH_REMATCH[3]}"
    echo "第4段:    ${BASH_REMATCH[4]}"
fi

十、常见陷阱与最佳实践

10.1 变量未加引号

# 错误:空变量会导致语法错误
[ $str = "hello" ]        # str为空时: [ = hello ] -> 语法错误

# 正确:始终加双引号
[ "$str" = "hello" ]      # str为空时: [ "" = "hello" ] -> 正常

# 更推荐:使用 [[ ]]
[[ $str == "hello" ]]     # 即使不加引号也安全

10.2 = vs == 混淆

# 在 [ ] 中:= 和 == 效果相同(字符串比较)
[ "$a" = "$b" ]
[ "$a" == "$b" ]   # 部分Shell支持,但非标准

# 在 [[ ]] 中:== 支持模式匹配
[[ "$file" == *.txt ]]   # 匹配以 .txt 结尾
[[ "$file" == "*.txt" ]] # 精确匹配字符串 "*.txt"

10.3 数值与字符串比较混用

a=10
b=2

# 错误:数值用字符串比较
[ "$a" > "$b" ]    # 实际重定向到文件 "2"!

# 正确:数值用数值比较符
[ "$a" -gt "$b" ]  # true

# 正确:字符串比较用 [[ ]]
[[ "$a" > "$b" ]]  # false("10" ASCII < "2")

10.4 浮点数运算

# Shell 不支持浮点数
# echo $((5 / 2))  # 2,不是 2.5

# 解决方案:使用 bc
result=$(echo "scale=2; 5 / 2" | bc)
echo $result   # 2.50

# 或使用 awk
awk 'BEGIN{print 5/2}'   # 2.5

10.5 最佳实践总结

场景 推荐做法
算术运算 result=$((a + b))
整数比较 [[ $a -gt $b ]]
字符串比较 [[ "$str" == "value" ]]
空值判断 [[ -z "$str" ]] 或 [[ "$str" ]]
多条件组合 [[ $a -gt 0 && $a -lt 100 ]]
正则匹配 [[ "$str" =~ ^regex$ ]]
文件测试 [[ -f "$file" && -r "$file" ]]
命令结果判断 cmd1 && cmd2 || cmd3

总结

Shell 运算符虽然看似简单,但细节之处暗藏陷阱。核心要点:

  1. 算术运算用 $(( )),只支持整数,浮点用 bc
  2. 数值比较用 -eq/-gt 系列,字符串比较用 =/!=
  3. 逻辑运算在 [[ ]] 中用 && ||,在 [ ] 中用 -a -o
  4. 位运算在 $(( )) 中使用,常用于权限/标志位处理
  5. 文件测试运算符丰富,是脚本健壮性的基础
  6. 优先使用 [[ ]] 而非 [ ],功能更强、性能更好、更安全
  7. 变量始终加引号,避免空值导致的语法错误

掌握这些运算符的用法和区别,是写出高质量、高可靠 Shell 脚本的必经之路。

原文 https://blog.csdn.net/2301_79518550/article/details/145422809