Shell数学计算全攻略:从整数到浮点,一文搞定脚本运算
早年间我刚开始正经写 shell 脚本的时候遇到过一件让我怀疑人生的事在终端里敲echo 1 2满心期待它给我输出个 3结果它原封不动打印了一行1 2。后来才明白shell 骨子里是个“字符串搬运工”它默认把所有内容都当成文本压根没打算替你做加法。从那一刻起我才意识到在 shell 里做数学计算第一课不是背命令而是搞清楚“怎么让 shell 知道你想算数”。这件事说起来简单但在实际脚本里计数器、耗时统计、阈值判断、批量序号生成、百分比计算……几乎每个自动化脚本都躲不开数学运算。用的方式不对轻则结果诡异重则整个脚本逻辑崩掉。这篇文章就从我踩过的坑出发把 shell 里做算术运算的几种常见姿势、适用场景和隐藏陷阱一次说清楚适合刚入门 shell 脚本的新手也适合写过一阵子但被各种奇怪现象折磨过的同学参考。1. 先把工具盘明白五种数学计算姿势对比shell 生态里做数学计算的方式其实不少但很多人翻来覆去只会用其中一两种遇到小数就直接抓瞎。这里把最常见的五种做法一次性盘清楚后面所有案例都建立在这些基础上。1.1$((...))日常最顺手的口袋计算器bash 里最推荐、也最常用的数学计算方式就是算术展开$(())。解释器看到这组符号会先把里面的内容当成数学表达式计算好再把结果替换到当前位置。前面那个echo 1 2只要改成echo $((1 2))就能老老实实输出 3 了。它有几个用起来非常舒服的细节变量在括号里面可以省略$符号a5; echo $((a 1))写起来干净得多支持 - * / %以及幂运算**bash 4.2 之后支持还支持括号改变优先级结果是纯整数echo $((1 / 3))输出的是 0不会给你小数未定义变量或者空变量在算术表达式里会被当成 0很多场景下能救命还支持类似 C 语言的赋值写法((cnt))、((total n))非常适合做循环计数。因为$((...))是 POSIX 标准规定的语法sh、dash、busybox 上的 sh 基本都支持跨环境兼容性最好。所以我的建议是凡是整数运算优先用它。1.2let命令老牌内置写法let是 bash 内置命令本质功能和$((...))差不多但它走的是“命令式”路线直接执行let aa1就能完成变量值的修改不用再把结果重新赋值回去。老脚本里经常看到这种风格读起来有点像 C 语言的语句。但let有两个特别容易踩的坑表达式两边不能有空格let a 1会直接报错必须写成let a1如果整个表达式的结果是 0let的退出状态码是 1不要把它当成“失败了”。假如你写let flag0紧接着用$?判断执行是否成功十有八九会被误导。新写的脚本里我很少单独用let但它能帮你读懂大量老项目所以还是建议搞明白。1.3expr兼容老系统的老黄牛expr是最传统的数学计算命令几乎出现在所有上古 shell 环境里。它是外部命令每执行一次都要启动一个独立进程性能在循环场景下非常难看但好处是“哪儿都有它”在极度精简的老系统上expr可能是你唯一的选择。用expr时有个容易被新手忽略的规则参数之间要有空格。expr 1 2里的1、、2必须分开乘号更要小心expr 3 * 2里的星号会被 shell 当成通配符展开必须写成expr 3 \* 2或者expr 3 * 2否则轻则报错重则把当前目录下的文件名全列出来当成参数。括号同理要用就得转义成\(和\)。1.4bc小数和精度问题的正路前面三种方式遇到除法就有点尴尬因为echo $((1 / 3))在 bash 里输出 0。要是计算 CPU 使用率、百分比、平均值这类需求整数结果根本不够用。最正统的小数计算工具是bc通过管道把表达式传进去echo scale2; 1 / 3 | bcscale是小数点后保留几位不给的话它默认按整数处理又会得到 0。bc还支持变量、函数和条件表达式配合bc -l开启数学库后可以用sqrt()、正弦余弦等数学函数处理浮点计算基本够用。1.5awk计算和格式化一步到位awk本身是一门完整的文本处理语言很多人只拿它取列其实它做数学计算也非常顺手。它可以在BEGIN模式里直接算不需要任何输入文件awk BEGIN{printf %.2f%%, 1/3*100}这条命令能直接输出带两位小数的百分比结果还带百分号。它比bc多了一个好处格式化输出能力极强printf想怎么控制格式都行不用算完之后再套一层printf。凡是“既要算数、又要按指定格式输出”的场景我基本都直接请awk上场。1.6 五种方式速查对比方式类型支持浮点性能适用场景$(( ))shell 内置否快整数计算、循环计数、条件判断letshell 内置否快老式脚本中的变量修改expr外部命令否慢老系统兼容bc外部命令是慢浮点数、数学函数awk外部命令是中等计算加格式化输出2. 场景选型什么时候该用哪一种工具都认识了接下来要解决的是“我到底该用哪个”的问题。这里不是简单的一句话推荐要把场景、性能、可移植性都考虑进去否则很容易在特定环境下翻车。2.1 循环内计数优先内置别频繁调外部命令写遍历脚本时最常见的需求是计数扫描一个网段里有多少台机器在线、统计一个日志文件里有几行满足条件的记录、计算批量处理完成了多少条。这种场景下循环里用cnt$((cnt 1))就够了纯粹靠 shell 内置计算不会额外创建进程。如果写成cnt$(expr $cnt 1)问题就来了每循环一次都要 fork 出一个expr进程循环 10000 次就是 10000 次进程创建和销毁。我在自己机器上简单对比过同样跑 10000 次累加$(( ))几乎是瞬间完成expr要好几秒。对批量脚本来说这个性能差距会直接决定你是等 5 秒还是等 5 分钟。2.2 要小数的时候别硬凑直接上 bc 或 awk算内存占用率、算超时比例、算任务成功百分比这些场景都会遇到“两个整数的除法结果不是整数”的问题。到底该用bc还是awk我的经验是只是简单算一两步两个都行如果后面还要对同一批数值做多次四则运算或者要做格式化输出优先awk因为它可以一次把计算和输出都搞定。还有一种思路是用整数放大法比如要计算 1/3 的百分比在不需要精确小数时可以直接算100 * 1 / 3得到整数 33凑合能用。这种做法的好处是省掉外部命令调用在循环里尤其有价值。2.3 可移植性陷阱sh、dash、busybox 环境差异如果脚本将来要丢到不同机器上跑不能默认所有环境都是完整 bash。$(( ))是 POSIX 标准大多数 sh 都支持可以放心用let不是 POSIX 标准语法dash 环境下根本没有这个命令bc在精简系统里不一定默认安装expr几乎一定存在awk基本都有但不同发行版可能用的是 mawk 或者 gawk数学函数支持的完整度有细微差别。我建议的保守组合是整数运算全部用$(( ))浮点运算优先awk需要极强兼容的老系统才考虑expr。2.4 面试官爱考的几个点很多 shell 面试题会围着数学计算打转常见的有三问第一为什么expr 3 * 2会报错这是在考察你有没有被通配符坑过第二在 bash 里echo $((1 / 3))输出什么这是在考你对整数除法的认知第三在 for 循环里做 1 到 100 的累加怎么写得又快又优雅大部分人会用expr硬算水平高低从这题能明显拉开差距。把这些原理弄懂其实面试题也只是纸老虎。3. 实操环节四个可以直接抄的脚本示例理论讲再多不如实际跑几个例子。下面这几个脚本都是我在日常工作里用过的、改造过的你可以直接拿来当模板改成自己的场景。3.1 批量重命名文件序号自动补零场景某台设备导出了一批照片文件名是乱七八糟的英文加数字想按顺序重命名成 001.jpg、002.jpg 这种风格方便排序和归档。#!/bin/bash n0 for f in *.jpg; do n$((n 1)) new$(printf %03d $n) mv $f ${new}.jpg done这里printf %03d负责补零$((n 1))负责递增序号。补零属于格式化序号生成属于数学计算两者配合极其常见。注意如果文件名带空格mv命令里的变量一定要加引号否则会按空格拆成多个参数。3.2 统计存活主机数量并计算比例场景内网有人报告某网段不稳定需要快速统计前 20 个 IP 里有多少台能 ping 通顺便算个存活百分比。#!/bin/bash ip192.168.1 ok0 total20 for i in $(seq 1 $total); do if ping -c 1 -W 1 $ip.$i /dev/null 21; then ok$((ok 1)) fi done echo 存活主机: $ok / $total if [ $ok -gt 0 ]; then rate$(awk BEGIN{printf \%.1f\, $ok/$total*100}) else rate0 fi echo 存活比例: ${rate}%这段代码里有两个值得注意的点。一是$((ok 1))可以用来做条件统计比用一堆 if 套字符串拼接干净得多二是计算百分比时用了awk直接从整数跨度到了小数输出避免了“1/20 直接得 0”的尴尬。这里把 awk 表达式放进双引号再传给-v的方式要注意转义实际生产环境建议用awk -v o$ok -v t$total BEGIN{...}的方式更安全也更易读。3.3 计算内存使用率场景写监控脚本时经常要输出当前内存使用占比free命令读到的值都是 KB 或 MB 级别的整数直接除会把小数点丢掉。#!/bin/bash total$(free -m | awk /Mem:/{print $2}) used$(free -m | awk /Mem:/{print $3}) awk -v t$total -v u$used BEGIN{printf 内存使用率: %.1f%%\n, u/t*100}这里先用free -m拿到内存总量和已使用量再通过-v把变量传给 awk 做浮点除法和格式化输出。这种写法的好处是逻辑分离得很清楚外部命令只负责取数数学计算交给专门的工具脚本可读性比在一堆管道里搅来搅去高多了。3.4 经典练手1 到 100 求和、九九乘法表、斐波那契这几个例子经常被拿来当 shell 教学题也是面试里出现频率很高的题型。1 到 100 求和最直接的循环写法#!/bin/bash sum0 for ((i 1; i 100; i)); do sum$((sum i)) done echo $sum更聪明的做法是直接用等差数列求和公式echo $((100 * (100 1) / 2))一步出结果。两种写法都能得到 5050但后者少了一层循环在数据量大时优势明显。九九乘法表#!/bin/bash for ((i 1; i 9; i)); do for ((j 1; j i; j)); do printf %dx%d%-2d $j $i $((i * j)) done echo done这里%-2d是让输出左对齐方便排版。$((i * j))在 ANSI-C for 循环里直接用简洁明了。斐波那契数列前 10 项#!/bin/bash a0 b1 for ((i 0; i 10; i)); do echo $a t$((a b)) a$b b$t done这三个例子本身不难但它们能帮你把“变量赋值”“循环结构”“数学表达式”这三块拼图完整地接起来。能把它们一遍写对说明你对 shell 数学计算的基本功已经过关了。4. 常见坑与排查技巧实录这部分是我最想写的因为 shell 数学计算报错时的诡异程度真的能让人怀疑是不是放错地方了。整理几个高频问题搭配一套排查方法至少能省你半天时间。4.1 高频坑位速查表错误表现根因正确姿势echo 1 2输出1 2shell 没有解释数学表达式改成echo $((1 2))let a 1报错let等号两侧不能有空格写成let a1expr 3 * 2报错或列出文件名星号被当成通配符展开写成expr 3 \* 2echo $((1 / 3))输出 0bash 整数除法截断改用bc或awk空变量参与计算时结果诡异变量未定义被当 0文件开头加set -ubc计算结果末尾少 0scale设置或输出格式问题用printf %.2f包一层echo $((2#1010))在新旧环境差异进制转换语法依赖 shell确认目标 shell 支持4.2 字符串和数字的边界很多坑的根源在于shell 变量本质是字符串数学计算只是在特定语法块里临时把它当数字用。所以会出现“明明赋值了一算就变成 0”的问题往往是因为变量的值根本不是一个干净的整数比如带了换行符、空格或者%.这样的格式化残留。排查这种问题时我习惯先echo [$var]看变量到底长什么样再用declare -p var查看类型和值。别嫌麻烦这一步能帮你避免 80% 的“为什么算出来是 0”类问题。4.3 用bash -x和set -u自检调试数学计算最有效的工具是bash -x它会把每一条命令实际执行的样子打印出来包括变量展开后的结果。你很快就能看到到底是a没赋值还是$((a 1))被展开成了$(( 1))。另一个强烈推荐的习惯是脚本第一行写上set -u开启未定义变量报错。默认情况下空变量在数学表达式里会被当成 0这个特性有时候是方便有时候会掩盖逻辑错误。开了set -u之后任何没定义就使用的变量都会直接报错逼你把问题在早期暴露出来。4.4 性能排查能用内置别乱 fork如果脚本跑得慢先怀疑循环里有没有反复调用expr、bc这些外部命令。我的经验是把循环内部的浮点运算挪到循环结束后统一处理或者在循环内用整数放大法性能能提升 10 倍以上。这也是为什么我在写大批量数据处理脚本时会刻意避免在循环里秀“花活”的原因。5. 几个进阶但很实用的写法掌握了基础和排坑再补充几个我平时用得很顺手的小技巧它们不算复杂但能明显提升脚本的健壮性和表达力。5.1 浮点数比较bash 的数字比较运算符-gt、-lt只支持整数如果直接[ 3.14 -gt 3.0 ]会报 invalid number。处理小数比较我一般用awkawk -v a3.14 -v b3.0 BEGIN{exit !(a b)} echo a 大这里awk的退出码拿来当判断条件逻辑清晰也不会被 bash 的整数限制卡住。5.2 进制转换处理网络配置或二进制掩码时偶尔要做进制转换。bash 支持前缀式写法echo $((2#1010)) # 二进制 1010 转十进制输出 10 echo $((16#FF)) # 十六进制 FF 转十进制输出 255 printf %x\n 255 # 十进制转十六进制输出 ff注意$((2#...))这类写法依赖 shell 是否启用 ARITH_FOR 等扩展功能不是所有 POSIX sh 都支持跨环境使用前先确认目标环境。5.3 随机数生成shell 里生成随机数bash 有内置变量$RANDOMecho $((RANDOM % 100)) # 0 到 99 之间的随机数 echo $((RANDOM % 50 1)) # 1 到 50 之间的随机数这在测试脚本、模拟请求分发时很常用。注意$RANDOM不是加密级随机不能用于安全相关场景。5.4 超时判断脚本里经常要判断“某个操作是否超时”可以结合date %s和数学计算start$(date %s) # 执行某些操作 sleep 3 end$(date %s) if (( end - start 2 )); then echo 超时了 fidate %s取出的是 Unix 时间戳两者相减就是实际耗时秒数属于最经典的数学计算应用。从我这些年的实际操作体会来看shell 数学计算的难点从来不在“语法记不住”而在于“你在哪种场景下选了哪种工具”。能熟练在整数和浮点之间切换、能在循环内外做性能取舍很多奇怪的问题根本不会出现。最后再分享一个小技巧接手别人的脚本时先全局搜一下expr和bc把它们全部改成$(( ))或awk脚本的可维护性会立刻上一个台阶排查问题时也能少绕很多弯路。

相关新闻

Java AIO实现MQTT百万连接:从架构设计到落地避坑

Java AIO实现MQTT百万连接:从架构设计到落地避坑

简介:基于 Java AIO 实现的低延迟、高性能百万级 MQTT 客户端组件与 Broker 服务,定位为物联网、边缘计算场景中需要自建消息服务器的 Java 开发者,解决多协议接入、高并发连接和集群扩展等核心问题。压缩包共 282 个文件,文件类型…

2026/10/4 20:55:08 阅读更多 →
本地 AI 自动化 OpenClaw 下载‑安装‑测试全套流程:把 settings 改到 TaoToken

本地 AI 自动化 OpenClaw 下载‑安装‑测试全套流程:把 settings 改到 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 20:55:08 阅读更多 →
Qwen3.8-27B 源码拆解 + 保姆级本地部署 + 模型横评:270 亿参数如何用混合线性注意力打穿长序列天花板

Qwen3.8-27B 源码拆解 + 保姆级本地部署 + 模型横评:270 亿参数如何用混合线性注意力打穿长序列天花板

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 20:55:08 阅读更多 →

最新新闻

K3这波操作,AI圈沸腾了,华尔街失眠了:MoE+KDA开源模型实测与TaoToken接入

K3这波操作,AI圈沸腾了,华尔街失眠了:MoE+KDA开源模型实测与TaoToken接入

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 21:36:48 阅读更多 →
ChatGPT Codex试用心得:从github PR到dotnet项目的TaoToken接入实录

ChatGPT Codex试用心得:从github PR到dotnet项目的TaoToken接入实录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 21:36:48 阅读更多 →
别再背八股了!Java面试官真正想听的是这些

别再背八股了!Java面试官真正想听的是这些

面试了上百个Java后端,我发现一个扎心的现象:很多人能把八股文背得滚瓜烂熟,可一旦追问“为什么”,立刻哑火。问他HashMap,他能背出负载因子0.75、红黑树阈值8,可问他“为什么是0.75不是0.8”,答…

2026/10/4 21:36:48 阅读更多 →
基于Node.js与React构建本地AI智能体:paperclip架构与OpenClaw部署实践

基于Node.js与React构建本地AI智能体:paperclip架构与OpenClaw部署实践

1. 项目缘起与核心定位第一次看到 "paperclip" 这个标题,加上关联的 Node.js、React、AI agents、OpenClaw 这几个词,我脑子里第一反应是:这大概率是一个用 Node.js 做运行时、React 做交互层、面向 AI 智能体(AI agent…

2026/10/4 21:35:47 阅读更多 →
Codex CLI 接入 MCP Server 实战:用 Ace Data Cloud 统一管理多个 AI 工具

Codex CLI 接入 MCP Server 实战:用 Ace Data Cloud 统一管理多个 AI 工具

1. 为什么我要折腾这个:从“能聊天的终端”到“真的能干活的工作台”Codex CLI 装好之后,最大的感受是:这家伙本质上是一个跑在终端里的 AI 助手,不是玩具。别管你用的什么模型,它能读你的仓库、能执行命令、能改代码、…

2026/10/4 21:35:47 阅读更多 →
从CRUD到架构师,后端进阶路线全解析

从CRUD到架构师,后端进阶路线全解析

很多后端开发者的日常,是写接口、改字段、修Bug,日复一日地CRUD。有人三年成为团队核心,有人五年仍在原地踏步。差距不在加班时长,而在于是否完成了从“功能实现者”到“系统设计者”的思维跃迁。从CRUD到架构师,需要跨…

2026/10/4 21:35:47 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 11:40:45 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 20:14:29 阅读更多 →