运算符是Shell脚本编程中执行各种操作的基础工具。Shell提供了丰富的运算符类型,可以用于算术计算、条件判断、字符串操作和文件测试等。
用于执行数学运算,如加减乘除、取模等。
用于比较数值或字符串的大小、相等等关系。
用于组合多个条件,实现复杂的逻辑判断。
用于字符串的比较、连接、长度计算等操作。
用于检查文件的属性,如是否存在、可读、可写等。
包括位运算符、赋值运算符、特殊运算符等。
[ ]和[[ ]]中使用不同的运算符语法(( ))或let命令用于执行数学运算的运算符。
| 运算符 | 说明 | 示例 | 结果 |
|---|---|---|---|
+ |
加法 | expr 10 + 3 |
13 |
- |
减法 | expr 10 - 3 |
7 |
* |
乘法 | expr 10 \* 3 |
30 |
/ |
除法 | expr 10 / 3 |
3 |
% |
取余 | expr 10 % 3 |
1 |
** |
幂运算 | echo $((2 ** 3)) |
8 |
++ |
自增 | ((a++)) |
a的值加1 |
-- |
自减 | ((a--)) |
a的值减1 |
#!/bin/bash
# 算术运算符示例
# 方法1: 使用 expr (注意: 运算符前后要有空格,*需要转义)
echo "=== 使用 expr ==="
a=10
b=3
echo "a + b = `expr $a + $b`" # 13
echo "a - b = `expr $a - $b`" # 7
echo "a * b = `expr $a \* $b`" # 30
echo "a / b = `expr $a / $b`" # 3
echo "a % b = `expr $a % $b`" # 1
# 方法2: 使用 $(( ))
echo -e "\n=== 使用 \$(( )) ==="
echo "a + b = $((a + b))" # 13
echo "a - b = $((a - b))" # 7
echo "a * b = $((a * b))" # 30
echo "a / b = $((a / b))" # 3
echo "a % b = $((a % b))" # 1
echo "a ** b = $((a ** b))" # 1000
# 方法3: 使用 let
echo -e "\n=== 使用 let ==="
let "c = a + b"
echo "c = $c" # 13
let "c += 5"
echo "c += 5 → $c" # 18
let "c++"
echo "c++ → $c" # 19
# 方法4: 使用 bc (浮点运算)
echo -e "\n=== 使用 bc (浮点运算) ==="
echo "scale=2; $a / $b" | bc # 3.33
echo "scale=4; $a / $b" | bc # 3.3333
# 复合运算
echo -e "\n=== 复合运算 ==="
x=5
y=2
((x += y)) # x = x + y
echo "x += y → $x" # 7
((x -= y)) # x = x - y
echo "x -= y → $x" # 5
((x *= y)) # x = x * y
echo "x *= y → $x" # 10
((x /= y)) # x = x / y
echo "x /= y → $x" # 5
((x %= y)) # x = x % y
echo "x %= y → $x" # 1
# 自增自减
echo -e "\n=== 自增自减 ==="
counter=5
((counter++))
echo "counter++ → $counter" # 6
((counter--))
echo "counter-- → $counter" # 5
((++counter))
echo "++counter → $counter" # 6
((--counter))
echo "--counter → $counter" # 5
expr: 老式方法,需要转义和空格$(( )): 推荐方法,简洁高效let: 另一种算术运算方式bc: 支持浮点运算,需要安装用于比较数值或字符串的关系运算符。
| 运算符 | 说明 | 示例 |
|---|---|---|
-eq |
等于 | [ $a -eq $b ] |
-ne |
不等于 | [ $a -ne $b ] |
-gt |
大于 | [ $a -gt $b ] |
-lt |
小于 | [ $a -lt $b ] |
-ge |
大于等于 | [ $a -ge $b ] |
-le |
小于等于 | [ $a -le $b ] |
| 运算符 | 说明 | 示例 |
|---|---|---|
= |
等于 | [ "$a" = "$b" ] |
== |
等于 | [ "$a" == "$b" ] |
!= |
不等于 | [ "$a" != "$b" ] |
\< |
小于 | [[ "$a" < "$b" ]] |
\> |
大于 | [[ "$a" > "$b" ]] |
-z |
长度为0 | [ -z "$a" ] |
-n |
长度不为0 | [ -n "$a" ] |
#!/bin/bash
# 关系运算符示例
# 数值比较
echo "=== 数值比较 ==="
a=10
b=20
if [ $a -eq $b ]; then
echo "$a -eq $b : a 等于 b"
else
echo "$a -eq $b : a 不等于 b"
fi
if [ $a -ne $b ]; then
echo "$a -ne $b : a 不等于 b"
else
echo "$a -ne $b : a 等于 b"
fi
if [ $a -gt $b ]; then
echo "$a -gt $b : a 大于 b"
else
echo "$a -gt $b : a 不大于 b"
fi
if [ $a -lt $b ]; then
echo "$a -lt $b : a 小于 b"
else
echo "$a -lt $b : a 不小于 b"
fi
if [ $a -ge $b ]; then
echo "$a -ge $b : a 大于或等于 b"
else
echo "$a -ge $b : a 小于 b"
fi
if [ $a -le $b ]; then
echo "$a -le $b : a 小于或等于 b"
else
echo "$a -le $b : a 大于 b"
fi
# 字符串比较
echo -e "\n=== 字符串比较 ==="
str1="apple"
str2="banana"
str3=""
if [ "$str1" = "$str2" ]; then
echo "$str1 = $str2 : str1 等于 str2"
else
echo "$str1 = $str2 : str1 不等于 str2"
fi
if [ "$str1" != "$str2" ]; then
echo "$str1 != $str2 : str1 不等于 str2"
else
echo "$str1 != $str2 : str1 等于 str2"
fi
if [[ "$str1" < "$str2" ]]; then
echo "$str1 < $str2 : str1 在字典序上小于 str2"
else
echo "$str1 < $str2 : str1 在字典序上不小于 str2"
fi
if [[ "$str1" > "$str2" ]]; then
echo "$str1 > $str2 : str1 在字典序上大于 str2"
else
echo "$str1 > $str2 : str1 在字典序上不大于 str2"
fi
if [ -z "$str3" ]; then
echo "str3 是空字符串"
else
echo "str3 不是空字符串"
fi
if [ -n "$str1" ]; then
echo "str1 不是空字符串"
else
echo "str1 是空字符串"
fi
# 使用双括号进行数值比较
echo -e "\n=== 使用双括号进行数值比较 ==="
if (( a == b )); then
echo "((a == b)) : a 等于 b"
else
echo "((a == b)) : a 不等于 b"
fi
if (( a < b )); then
echo "((a < b)) : a 小于 b"
else
echo "((a < b)) : a 不小于 b"
fi
if (( a > b )); then
echo "((a > b)) : a 大于 b"
else
echo "((a > b)) : a 不大于 b"
fi
-eq, -ne, -gt, -lt, -ge, -le
=, ==, !=, \<, \>, -z, -n
==, !=, \<, \>, \<=, >=
用于组合多个条件的逻辑运算符。
| 运算符 | 说明 | 示例 | 说明 |
|---|---|---|---|
&& |
逻辑与 | [ $a -gt 0 ] && [ $b -gt 0 ] |
两个条件都为真 |
|| |
逻辑或 | [ $a -gt 0 ] || [ $b -gt 0 ] |
至少一个条件为真 |
! |
逻辑非 | [ ! $a -gt 0 ] |
条件取反 |
-a |
逻辑与 | [ $a -gt 0 -a $b -gt 0 ] |
两个条件都为真 |
-o |
逻辑或 | [ $a -gt 0 -o $b -gt 0 ] |
至少一个条件为真 |
#!/bin/bash
# 逻辑运算符示例
echo "=== 逻辑与 (&&) ==="
age=25
score=85
# 使用 && 连接多个命令
if [ $age -ge 18 ] && [ $score -ge 60 ]; then
echo "年龄合格且成绩及格"
else
echo "年龄或成绩不合格"
fi
# 在 [[ ]] 中使用 &&
if [[ $age -ge 18 && $score -ge 60 ]]; then
echo "年龄合格且成绩及格"
else
echo "年龄或成绩不合格"
fi
# 使用 -a (单括号内)
if [ $age -ge 18 -a $score -ge 60 ]; then
echo "年龄合格且成绩及格"
else
echo "年龄或成绩不合格"
fi
echo -e "\n=== 逻辑或 (||) ==="
file1="test.txt"
file2="backup.txt"
# 使用 || 连接多个命令
if [ -f "$file1" ] || [ -f "$file2" ]; then
echo "至少有一个文件存在"
else
echo "两个文件都不存在"
fi
# 在 [[ ]] 中使用 ||
if [[ -f "$file1" || -f "$file2" ]]; then
echo "至少有一个文件存在"
else
echo "两个文件都不存在"
fi
# 使用 -o (单括号内)
if [ -f "$file1" -o -f "$file2" ]; then
echo "至少有一个文件存在"
else
echo "两个文件都不存在"
fi
echo -e "\n=== 逻辑非 (!) ==="
file="nonexistent.txt"
# 使用 ! 取反
if [ ! -f "$file" ]; then
echo "文件不存在"
else
echo "文件存在"
fi
# 在 [[ ]] 中使用 !
if [[ ! -f "$file" ]]; then
echo "文件不存在"
else
echo "文件存在"
fi
echo -e "\n=== 复杂逻辑组合 ==="
username="admin"
password="123456"
is_admin=true
# 复杂的逻辑组合
if [[ ($username == "admin" && $password == "123456") || $is_admin == true ]]; then
echo "登录成功或用户是管理员"
else
echo "登录失败且用户不是管理员"
fi
# 使用括号分组
if [ \( "$username" = "admin" -a "$password" = "123456" \) -o "$is_admin" = true ]; then
echo "登录成功或用户是管理员"
else
echo "登录失败且用户不是管理员"
fi
echo -e "\n=== 短路求值 ==="
# && 短路求值:第一个命令成功才执行第二个
[ -f "$file1" ] && echo "文件存在,执行操作"
# || 短路求值:第一个命令失败才执行第二个
[ -f "$file1" ] || echo "文件不存在,执行备用操作"
# 组合使用
[ -f "$file1" ] && echo "文件存在" || echo "文件不存在"
&& 和 || 而不是 -a 和 -o[[ ]] 中使用逻辑运算符更安全用于字符串操作和处理的运算符。
| 运算符 | 说明 | 示例 | 结果 |
|---|---|---|---|
${#str} |
字符串长度 | ${#name} |
字符串长度 |
${str:pos} |
子字符串提取 | ${str:2} |
从位置2开始 |
${str:pos:len} |
子字符串提取 | ${str:2:3} |
从位置2开始取3个字符 |
${str/sub/repl} |
替换第一个匹配 | ${str/old/new} |
替换第一个old为new |
${str//sub/repl} |
替换所有匹配 | ${str//old/new} |
替换所有old为new |
${str/#sub/repl} |
替换开头匹配 | ${str/#old/new} |
如果开头是old则替换 |
${str/%sub/repl} |
替换结尾匹配 | ${str/%old/new} |
如果结尾是old则替换 |
${str#pattern} |
删除前缀匹配 | ${str#prefix} |
删除最短匹配前缀 |
${str##pattern} |
删除前缀匹配 | ${str##prefix} |
删除最长匹配前缀 |
${str%pattern} |
删除后缀匹配 | ${str%suffix} |
删除最短匹配后缀 |
${str%%pattern} |
删除后缀匹配 | ${str%%suffix} |
删除最长匹配后缀 |
#!/bin/bash
# 字符串运算符示例
echo "=== 字符串长度 ==="
str="Hello, World!"
echo "字符串: $str"
echo "长度: ${#str}" # 13
echo -e "\n=== 子字符串提取 ==="
echo "从位置7开始: ${str:7}" # World!
echo "从位置0开始取5个字符: ${str:0:5}" # Hello
echo "从位置7开始取5个字符: ${str:7:5}" # World
echo -e "\n=== 字符串替换 ==="
text="apple orange apple banana"
echo "原始文本: $text"
echo "替换第一个apple: ${text/apple/fruit}" # fruit orange apple banana
echo "替换所有apple: ${text//apple/fruit}" # fruit orange fruit banana
echo "替换开头的apple: ${text/#apple/fruit}" # fruit orange apple banana
echo "替换结尾的banana: ${text/%banana/fruit}" # apple orange apple fruit
echo -e "\n=== 删除匹配模式 ==="
filename="backup.tar.gz"
echo "文件名: $filename"
echo "删除 .tar.gz: ${filename%.tar.gz}" # backup
echo "删除 backup.: ${filename#backup.}" # tar.gz
path="/home/user/documents/file.txt"
echo -e "\n文件路径: $path"
echo "删除最短前缀匹配: ${path#/*/}" # user/documents/file.txt
echo "删除最长前缀匹配: ${path##/*/}" # file.txt
echo "删除最短后缀匹配: ${path%.*}" # /home/user/documents/file
echo "删除最长后缀匹配: ${path%%.*}" # /home/user/documents/file
echo -e "\n=== 大小写转换 ==="
mixed="Hello World"
echo "原始: $mixed"
echo "大写: ${mixed^^}" # HELLO WORLD
echo "小写: ${mixed,,}" # hello world
echo "首字母大写: ${mixed^}" # Hello World
echo -e "\n=== 默认值处理 ==="
unset undefined_var
echo "变量值: ${undefined_var:-默认值}" # 默认值
echo "变量值: ${undefined_var:=默认值}" # 默认值 (并赋值)
echo "设置后的值: $undefined_var" # 默认值
defined_var="有值"
echo "有值的变量: ${defined_var:-默认值}" # 有值
echo -e "\n=== 检查变量设置 ==="
unset check_var
echo "变量是否设置: ${check_var+是}" # 空 (未设置)
echo "变量是否设置: ${defined_var+是}" # 是 (已设置)
echo -e "\n=== 字符串分割 ==="
csv="apple,orange,banana,grape"
IFS=',' read -ra fruits <<< "$csv"
echo "CSV字符串: $csv"
echo "分割后的数组:"
for fruit in "${fruits[@]}"; do
echo " - $fruit"
done
echo -e "\n=== 字符串连接 ==="
part1="Hello"
part2="World"
combined="$part1, $part2!"
echo "连接结果: $combined" # Hello, World!
# 使用 += 连接字符串
greeting="Hello"
greeting+=", World!"
echo "使用+=连接: $greeting" # Hello, World!
用于测试文件属性和类型的运算符。
| 运算符 | 说明 | 示例 |
|---|---|---|
-e |
文件/目录是否存在 | [ -e file ] |
-f |
是否是普通文件 | [ -f file ] |
-d |
是否是目录 | [ -d dir ] |
-r |
文件是否可读 | [ -r file ] |
-w |
文件是否可写 | [ -w file ] |
-x |
文件是否可执行 | [ -x file ] |
-s |
文件大小是否大于0 | [ -s file ] |
-L |
是否是符号链接 | [ -L file ] |
-O |
文件是否属于当前用户 | [ -O file ] |
-G |
文件是否属于当前用户组 | [ -G file ] |
-nt |
文件1是否比文件2新 | [ file1 -nt file2 ] |
-ot |
文件1是否比文件2旧 | [ file1 -ot file2 ] |
-ef |
文件1和文件2是否是同一文件 | [ file1 -ef file2 ] |
#!/bin/bash
# 文件测试运算符示例
echo "=== 基本文件测试 ==="
file="test.txt"
dir="/tmp"
# 创建测试文件
echo "Hello World" > "$file"
if [ -e "$file" ]; then
echo "$file 存在"
else
echo "$file 不存在"
fi
if [ -f "$file" ]; then
echo "$file 是普通文件"
else
echo "$file 不是普通文件"
fi
if [ -d "$dir" ]; then
echo "$dir 是目录"
else
echo "$dir 不是目录"
fi
echo -e "\n=== 权限测试 ==="
if [ -r "$file" ]; then
echo "$file 可读"
else
echo "$file 不可读"
fi
if [ -w "$file" ]; then
echo "$file 可写"
else
echo "$file 不可写"
fi
if [ -x "$file" ]; then
echo "$file 可执行"
else
echo "$file 不可执行"
fi
echo -e "\n=== 文件属性测试 ==="
if [ -s "$file" ]; then
echo "$file 不为空"
else
echo "$file 为空"
fi
# 创建符号链接测试
ln -sf "$file" "link_to_test.txt"
if [ -L "link_to_test.txt" ]; then
echo "link_to_test.txt 是符号链接"
else
echo "link_to_test.txt 不是符号链接"
fi
if [ -O "$file" ]; then
echo "$file 属于当前用户"
else
echo "$file 不属于当前用户"
fi
if [ -G "$file" ]; then
echo "$file 属于当前用户组"
else
echo "$file 不属于当前用户组"
fi
echo -e "\n=== 文件比较 ==="
file1="file1.txt"
file2="file2.txt"
# 创建测试文件
echo "content1" > "$file1"
sleep 1 # 确保时间戳不同
echo "content2" > "$file2"
if [ "$file1" -nt "$file2" ]; then
echo "$file1 比 $file2 新"
else
echo "$file1 不比 $file2 新"
fi
if [ "$file1" -ot "$file2" ]; then
echo "$file1 比 $file2 旧"
else
echo "$file1 不比 $file2 旧"
fi
# 创建硬链接
ln "$file1" "hard_link.txt"
if [ "$file1" -ef "hard_link.txt" ]; then
echo "$file1 和 hard_link.txt 是同一文件"
else
echo "$file1 和 hard_link.txt 不是同一文件"
fi
echo -e "\n=== 综合文件检查函数 ==="
check_file() {
local file="$1"
echo "检查文件: $file"
if [ ! -e "$file" ]; then
echo " ❌ 文件不存在"
return 1
fi
[ -f "$file" ] && echo " ✅ 是普通文件"
[ -d "$file" ] && echo " ✅ 是目录"
[ -r "$file" ] && echo " ✅ 可读"
[ -w "$file" ] && echo " ✅ 可写"
[ -x "$file" ] && echo " ✅ 可执行"
[ -s "$file" ] && echo " ✅ 不为空"
[ -L "$file" ] && echo " ✅ 是符号链接"
[ -O "$file" ] && echo " ✅ 属于当前用户"
return 0
}
# 测试文件检查函数
check_file "$file"
check_file "$dir"
echo -e "\n=== 批量文件检查 ==="
files=("$file" "$file1" "$file2" "nonexistent.txt")
for f in "${files[@]}"; do
if [ -e "$f" ]; then
echo "✅ $f 存在"
if [ -s "$f" ]; then
size=$(wc -c < "$f")
echo " 大小: $size 字节"
fi
else
echo "❌ $f 不存在"
fi
done
# 清理测试文件
rm -f "$file" "$file1" "$file2" "link_to_test.txt" "hard_link.txt"
通过实际脚本示例展示Shell运算符的综合应用。
使用各种运算符实现一个简单的计算器。
#!/bin/bash
# 简单计算器脚本
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 显示菜单
show_menu() {
echo -e "${BLUE}=== 简单计算器 ===${NC}"
echo "1. 加法"
echo "2. 减法"
echo "3. 乘法"
echo "4. 除法"
echo "5. 取余"
echo "6. 幂运算"
echo "7. 比较两个数"
echo "8. 退出"
}
# 获取数字输入
get_number() {
local prompt="$1"
local number
while true; do
read -p "$prompt" number
if [[ "$number" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
echo "$number"
break
else
echo -e "${RED}错误: 请输入有效的数字${NC}"
fi
done
}
# 加法
add() {
local num1=$(get_number "请输入第一个数: ")
local num2=$(get_number "请输入第二个数: ")
local result=$(echo "$num1 + $num2" | bc)
echo -e "${GREEN}结果: $num1 + $num2 = $result${NC}"
}
# 减法
subtract() {
local num1=$(get_number "请输入第一个数: ")
local num2=$(get_number "请输入第二个数: ")
local result=$(echo "$num1 - $num2" | bc)
echo -e "${GREEN}结果: $num1 - $num2 = $result${NC}"
}
# 乘法
multiply() {
local num1=$(get_number "请输入第一个数: ")
local num2=$(get_number "请输入第二个数: ")
local result=$(echo "$num1 * $num2" | bc)
echo -e "${GREEN}结果: $num1 * $num2 = $result${NC}"
}
# 除法
divide() {
local num1=$(get_number "请输入第一个数: ")
local num2=$(get_number "请输入第二个数: ")
if (( $(echo "$num2 == 0" | bc) )); then
echo -e "${RED}错误: 除数不能为0${NC}"
return
fi
local result=$(echo "scale=4; $num1 / $num2" | bc)
echo -e "${GREEN}结果: $num1 / $num2 = $result${NC}"
}
# 取余
modulus() {
local num1=$(get_number "请输入第一个数(整数): ")
local num2=$(get_number "请输入第二个数(整数): ")
if (( $(echo "$num2 == 0" | bc) )); then
echo -e "${RED}错误: 除数不能为0${NC}"
return
fi
local result=$((num1 % num2))
echo -e "${GREEN}结果: $num1 % $num2 = $result${NC}"
}
# 幂运算
power() {
local num1=$(get_number "请输入底数: ")
local num2=$(get_number "请输入指数: ")
local result=$(echo "$num1 ^ $num2" | bc)
echo -e "${GREEN}结果: $num1 ^ $num2 = $result${NC}"
}
# 比较两个数
compare() {
local num1=$(get_number "请输入第一个数: ")
local num2=$(get_number "请输入第二个数: ")
echo -e "${BLUE}比较结果:${NC}"
if (( $(echo "$num1 == $num2" | bc) )); then
echo " $num1 = $num2"
elif (( $(echo "$num1 > $num2" | bc) )); then
echo " $num1 > $num2"
else
echo " $num1 < $num2"
fi
# 使用字符串比较显示差异
local diff=$(echo "$num1 - $num2" | bc)
if (( $(echo "$diff > 0" | bc) )); then
echo -e " 相差: +$diff"
else
echo -e " 相差: $diff"
fi
}
# 主循环
main() {
while true; do
show_menu
read -p "请选择操作 [1-8]: " choice
case $choice in
1) add ;;
2) subtract ;;
3) multiply ;;
4) divide ;;
5) modulus ;;
6) power ;;
7) compare ;;
8)
echo -e "${GREEN}感谢使用计算器,再见!${NC}"
break
;;
*)
echo -e "${RED}无效选择,请重新输入${NC}"
;;
esac
echo
read -p "按回车键继续..."
echo
done
}
# 脚本入口
echo -e "${GREEN}欢迎使用Shell计算器${NC}"
main
./calculator.sh[[ ]] 而不是 [ ] 进行条件测试$(( )) 进行算术运算[ ] 中使用 < 和 >expr 命令使用 set -x 开启调试模式,可以查看运算符的执行过程。使用 echo 输出中间结果进行调试。