Shell 运算符详解(新手详细版)
本文档面向零基础新手,从“Shell 里有哪些运算、怎么写”讲起,详细说明算术运算、字符串操作、文件测试、比较运算、逻辑运算、位运算等,并配有大量示例。
一、Shell 里有哪些“运算”?
1.1 大致分类
- 算术运算:加减乘除、取余、自增等(数字)
- 字符串:拼接、长度、子串、比较、是否为空
- 文件测试:是否存在、是否可读、是文件还是目录等
- 比较运算:相等、不等、大于、小于(数字或字符串)
- 逻辑运算:与、或、非(用于条件判断)
- 位运算:按位与、或、异或等(少用,了解即可)
Shell 默认没有“浮点数”,算术一般是整数;需要小数可用 bc 等外部命令。
二、算术运算
2.1 用 $(( )) 做运算(推荐)
$(( 表达式 )) 会先算表达式,再把结果当成一个“值”参与命令。
表达式里变量可以不加 $(加了也行),运算符两侧可以有空格。
a=10
b=3
echo $(( a + b ))
# 13
echo $(( a - b ))
# 7
echo $(( a * b ))
# 30
echo $(( a / b ))
# 3(整除)
echo $(( a % b ))
# 1(取余)
运算符:+、-、*、/、%(取余);** 表示乘方(部分 Shell 支持,Bash 支持)。
echo $(( 2 ** 10 ))
# 1024
2.2 自增、自减
在 $(( )) 或 let 里可以用 ++、—:
i=5
echo $(( i++ ))
# 5(先取值再加 1,i 变成 6)
echo $(( ++i ))
# 7(先加 1 再取值,i 变成 7)
j=10
echo $(( j-- ))
# 10
echo $(( --j ))
# 8
2.3 把结果存回变量
用赋值把运算结果存起来:
sum=$(( 3 + 5 ))
echo $sum
# 8
count=0
count=$(( count + 1 ))
echo $count
# 1
2.4 let 命令(也可做算术)
let “表达式” 会计算表达式,并可用于给变量赋值;表达式里不要写 $,且通常用引号包起来。
let "x=5+3"
echo $x
# 8
let "y+=2"
# 等价于 y=$(( y+2 ))
日常更常用 $(( )),因为可以直接 echo $(( … )) 或 x=$(( … ))。
2.5 expr(老式,了解即可)
expr 是外部命令,用于简单算术和字符串长度等;运算符和数字之间要有空格,* 要转义。
expr 10 + 2
# 12
expr 10 * 2
# 20
新脚本建议用 $(( )),不必依赖 expr。
2.6 小数运算:用 bc
Shell 自身只做整数运算;要小数用 bc:
echo "scale=2; 10/3" | bc
# 3.33
echo "3.14 + 2.5" | bc
# 5.64
scale=2 表示保留 2 位小数。
2.7 算术运算小结
| 运算符 | 含义 | 示例 |
|---|---|---|
| + – * / | 加减乘除 | $(( a + b )) |
| % | 取余 | $(( 10 % 3 )) → 1 |
| ** | 乘方(Bash) | $(( 2 ** 3 )) → 8 |
| ++ — | 自增自减 | $(( i++ )) |
| = += -= 等 | 赋值、复合赋值 | 在 (( )) 或 let 里用 |
三、字符串相关
3.1 字符串拼接(直接并排)
Shell 里字符串“拼接”就是把变量或字面量写在一起,中间可加空格(空格会保留在结果里)。
a="Hello"
b="World"
echo "$a $b"
# Hello World
echo "$a, $b"
# Hello, World
3.2 字符串长度:${#变量名}
s="hello"
echo ${#s}
# 5
3.3 子串:${变量:起始:长度}
- 起始从 0 开始;可省略长度表示到末尾。
- 若起始为负数,表示从末尾往前数(部分 Shell 支持)。
s="abcdef"
echo ${s:0:3}
# abc
echo ${s:2:2}
# cd
echo ${s:2}
# cdef(从下标 2 到末尾)
3.4 字符串“替换”:${var/旧/新}
- ${var/旧/新}:第一个“旧”换成“新”。
- ${var//旧/新}:所有“旧”换成“新”。
- 不写“新”相当于删除。
path="/home/user/file.txt"
echo ${path/file/newfile}
# /home/user/newfile.txt
echo ${path////_}
# _home_user_file.txt(把所有 / 换成 _)
3.5 默认值、空值处理(和参数类似)
- ${var:-默认}:var 未设或为空时用“默认”。
- ${var:=默认}:未设或为空时给 var 赋值并展开。
- ${var:?错误信息}:未设或为空时报错退出。
- ${var:+有值时的替换}:有值才替换。
unset x
echo "${x:-未设置}"
# 未设置
echo "${x:=0}"
# 0(x 被设为 0)
3.6 字符串比较(在 [ ] 或 [[ ]] 里)
- =、==:相等;!=:不等。
- -z “串”:长度为 0;-n “串”:长度非 0。
- 建议变量加双引号:
[ "$a" = "$b" ]。
a="hello"
b="hello"
if [ "$a" = "$b" ]; then
echo "相等"
fi
if [ -z "$a" ]; then
echo "为空"
else
echo "非空"
fi
四、文件测试运算符
在 [ ] 或 [[ ]] 里用来判断“文件/路径”的状态;test 命令等价于 [ ]。
4.1 存在与类型
| 写法 | 含义 |
|---|---|
| -e 路径 | 存在(文件或目录) |
| -f 路径 | 存在且为普通文件 |
| -d 路径 | 存在且为目录 |
| -L 路径 | 存在且为符号链接 |
| -b 路径 | 块设备 |
| -c 路径 | 字符设备 |
4.2 权限与属性
| 写法 | 含义 |
|---|---|
| -r 路径 | 可读 |
| -w 路径 | 可写 |
| -x 路径 | 可执行 |
| -s 路径 | 存在且大小大于 0 |
| -N 路径 | 自上次读后是否被修改(部分 Shell) |
4.3 比较两个文件
| 写法 | 含义 |
|---|---|
| 文件1 -nt 文件2 | 文件1 比 文件2 新(newer than) |
| 文件1 -ot 文件2 | 文件1 比 文件2 旧(older than) |
| 文件1 -ef 文件2 | 同一设备同一 inode(硬链接或同一文件) |
示例:
if [ -f "$1" ]; then
echo "$1 是文件"
fi
if [ -d "/tmp" ]; then
echo "/tmp 是目录"
fi
if [ -r "$file" ]; then
echo "可读"
fi
五、数值比较(在 [ ] 里)
在 [ ] 中比较数字要用下面这些,不能用 =、<、>(会按字符串比较):
| 写法 | 含义 |
|---|---|
| -eq | 等于(equal) |
| -ne | 不等于(not equal) |
| -gt | 大于(greater than) |
| -ge | 大于等于 |
| -lt | 小于(less than) |
| -le | 小于等于 |
示例:
a=10
b=5
if [ "$a" -gt "$b" ]; then
echo "a 大于 b"
fi
if [ "$a" -eq 10 ]; then
echo "a 等于 10"
fi
注意:变量建议加引号;若变量为空,会变成 [ -gt 5 ] 等错误形式,所以先检查是否为空或数字更稳妥。
六、逻辑运算
6.1 在 [ ] 里:-a、-o、!
| 写法 | 含义 |
|---|---|
| ! 条件 | 非 |
| 条件1 -a 条件2 | 与(and) |
| 条件1 -o 条件2 | 或(or) |
if [ -f file.txt -a -r file.txt ]; then
echo "存在且可读"
fi
if [ ! -d "/不存在的路径" ]; then
echo "不是目录或不存在"
fi
6.2 用 &&、|| 连接命令(推荐)
命令1 && 命令2:命令1 成功(退出码 0)才执行命令2。
命令1 || 命令2:命令1 失败(非 0)才执行命令2。
[ -f file.txt ] && echo "存在"
mkdir backup || echo "创建失败"
在 [[ ]] 里可以直接写 &&、||、!:
if [[ -f file.txt && -r file.txt ]]; then
echo "存在且可读"
fi
6.3 逻辑与算术混合(在 (( )) 里)
(( )) 里可以做算术并当条件:非 0 为真,0 为假。
a=5
if (( a > 0 && a < 10 )); then
echo "a 在 0 到 10 之间"
fi
七、位运算(了解即可)
在 $(( )) 里支持按位运算:
| 运算符 | 含义 |
|---|---|
| & | 按位与 |
| | | 按位或 |
| ^ | 按位异或 |
| ~ | 按位取反 |
| <> | 左移、右移 |
echo $(( 5 & 3 ))
# 1
echo $(( 5 | 3 ))
# 7
echo $(( 5 ^ 3 ))
# 6
echo $(( 1 << 4 ))
# 16
八、赋值与复合赋值
8.1 普通赋值
name="张三"
count=0
注意:= 两边不能有空格,否则会被当成命令和参数。
8.2 算术复合赋值(在 (( )) 里)
x=10
(( x += 5 ))
echo $x
# 15
(( x *= 2 ))
echo $x
# 30
8.3 字符串“追加”(拼接后赋值)
s="hello"
s="$s world"
echo $s
# hello world
九、[[ ]] 里的比较与正则
在 [[ ]] 中:
- ==、!= 可用于字符串比较;<、> 按字典序(或当前 locale)。
- =~ 表示“左边字符串”匹配“右边正则”。
s="hello world"
if [[ "$s" =~ ^hello ]]; then
echo "以 hello 开头"
fi
if [[ "$s" == *world* ]]; then
echo "包含 world(通配)"
fi
十、运算符优先级(简要)
算术 $(( )) 里:先乘除取余,后加减;可用 () 改变顺序。
逻辑:! 最高,再 &&,再 ||。
写复杂式子时多用小括号提高可读性。
echo $(( (1+2) * 3 ))
# 9
十一、实用示例汇总
11.1 算术
# 求和
sum=$(( 10 + 20 ))
# 自增
i=0
i=$(( i + 1 ))
# 取余判断奇偶
if [ $(( n % 2 )) -eq 0 ]; then
echo "偶数"
fi
# 小数
result=$(echo "scale=2; 10/3" | bc)
11.2 字符串
# 长度
len=${#str}
# 子串
sub=${str:0:5}
# 默认值
name=${1:-"访客"}
# 比较
[ "$a" = "$b" ]
[ -n "$a" ]
11.3 文件与数值比较
[ -f "$file" ]
[ -d "$dir" ]
[ "$count" -gt 0 ]
[ "$count" -le 100 ]
11.4 逻辑组合
[[ -f f && -r f ]]
[ "$x" -gt 0 -a "$x" -lt 10 ]
cmd1 && cmd2
cmd1 || cmd2
十二、常见坑与建议
- 赋值 = 两边不能有空格:
x=1正确,x = 1会报错。 - 数字比较用 -eq 等:在 [ ] 里用 -eq、-gt,不用 =、>(会当字符串)。
- 变量加引号:
[ "$a" = "$b" ],避免空或含空格出错。 - 整数除法:
$(( 10/3 ))是 3,要小数用 bc。 - [[ ]] 与 [ ]:Bash 下推荐 [[ ]] 做条件,支持 &&、||、=~。
十三、速查表
| 类型 | 写法示例 |
|---|---|
| 算术 | $(( a+b ))、$(( a**2 ))、$(( i++ )) |
| 字符串长度 | ${#var} |
| 子串 | ${var:0:5}、${var:2} |
| 字符串替换 | ${var/旧/新}、${var//旧/新} |
| 默认值 | ${var:-默认}、${var:=默认} |
| 文件测试 | -f、-d、-r、-e 等 |
| 数值比较 | -eq、-ne、-gt、-lt、-ge、-le |
| 字符串比较 | =、!=、-z、-n |
| 逻辑 | !、-a、-o、&&、|| |
| 位运算 | $(( a & b ))、$(( a << 1 )) |
十四、小结
- 算术:用 $(( )) 或 let;只有整数,小数用 bc;注意 / 整除、% 取余、++/–。
- 字符串:拼接并排写;${#var} 长度;${var:起:长} 子串;${var/旧/新} 替换;比较用 =、!=、-z、-n。
- 文件:-f、-d、-r、-e 等在 [ ] 里用。
- 数值比较:[ ] 里用 -eq、-gt 等;逻辑用 -a、-o、! 或 &&、||;[[ ]] 里可用 =~ 正则。
- 赋值:= 两边无空格;复合赋值在 (( )) 里用 += 等。
多写几段脚本练算术、字符串长度与子串、文件判断、数值与逻辑比较,再结合 Shell流程控制.md、Shell传递参数.md 一起用,运算符就能用熟。
*文档以 Bash 为准;sh 或其它 Shell 在 *、(( )) 等上可能略有差异。