news 2026/11/5 4:43:33

Linux Shell 内建命令 let 详解:用法、原理与脚本实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux Shell 内建命令 let 详解:用法、原理与脚本实战

直接写,不需要客套话。咱们来看看 Linux 下这个总是被人忽略、但在脚本里又特别好用的内建命令:let。很多刚接触 shell 编程的朋友,在做算术运算的时候,第一反应是去装 bc,要么就是用 expr,等到别人拿出 let 三下五除二解决了问题,才发现自己绕了远路。这篇就把 let 命令从用法到原理、再到实际脚本里的典型场景,一次讲透。

1. 先搞清楚 let 到底是什么、为什么需要它

1.1 它不是外部程序,而是 shell 自带的计算器

let 是 bash、zsh、ksh 这类 shell 的内建命令,不是一个独立的二进制文件。这一点特别关键,因为内建命令意味着它不需要 fork 一个子进程来执行,在脚本里大量使用的时候,性能开销比外部命令小得多。你可以做个简单验证:

type -a let

在 bash 里会输出类似let is a shell builtin的结果。如果你用的是 dash,比如 Ubuntu 系统默认的 /bin/sh 在某些版本里就是 dash,直接执行 let 会告诉你 command not found。这是后面要专门说的兼容性问题,先在心里记一笔。

let 从功能上讲,就是一个整数算术表达式求值器,支持变量赋值、自增自减、复合赋值、位运算、进制转换、逻辑运算等等。它不处理浮点数,这是它的边界,也是后续选型时要考虑的点。

1.2 用计算器的类比理解它的工作方式

你可以把 let 想象成一个历史悠久的计算器,但它的按键规则很特别:输入表达式时不允许有空格,除非你把整个表达式用引号包起来。

let a=5+3 echo $a # 8

如果你写成let a = 5 + 3,不加引号,shell 会把参数拆成 a、=、5、+、3 五个部分,let 拿到第一个参数 a 去求值,结果就是变量 a 原本的值,后面的参数被忽略了,而且整个过程不会报错。这是我当年踩过的第一个坑,后面会详细展开。

let 能干的事远不止加减乘除,它还能直接给变量赋值。比如:

let b=10 echo $b # 10

这里不需要在 b 前面加$符号,let 会自动识别变量名并赋值,如果变量之前不存在,它会直接创建。这个特性和很多语言完全不一样,刚接触会觉得有点反直觉,但真正理解了之后会发现非常顺手。

1.3 let 和 $(()) 双括号的关系

let 还有一个好兄弟:算术扩展$(())和复合命令(())。它们底层用的是同一套算术求值机制,区别在于使用形式。

let "x = 5 + 3" y=$((5 + 3)) ((z = 5 + 3))

三个写法结果一样,但细节不同。$(())会返回表达式的结果值,可以直接赋值给变量;(())是命令形式,不输出结果,但它的退出状态码表示表达式是否非零;let 本身不输出结果,要么通过变量赋值体现,要么通过退出状态码判断。理解了三者的关系,你在写脚本时就能根据场景自由选择,不需要死记硬背。

2. let 的语法规则与底层原理

2.1 空格问题:为什么我写的 let 总是不按预期执行

这是 let 命令最容易踩的坑。let 的参数可以是多个,每个参数都会被当作表达式的一部分,但关键是加号、减号、乘号、除号这些符号在没有引号的普通参数里,会被 shell 解析器拆分成独立的参数。

let a=5 + 3

这里的5和+是两个参数,let 只会对第一个参数5做求值,结果就变成了a=5,后面的+和3被忽略。没有任何错误提示,但结果就是不对,这就是 shell 脚本里最瘆人的隐形 bug。排查起来还特别费劲,因为你不看参数解析过程根本发现不了。

解决办法很简单,就两个方向:

  1. 整个表达式加引号:let "a = 5 + 3"
  2. 不加引号但代码紧凑:let a=5+3

我个人的习惯是只要表达式稍微长一点,就一律用双引号包起来。好处有三个:允许在等号两边加空格提升可读性;可以安全地在表达式中使用*、(、)这些在命令行里容易被通配符展开的符号;如果不小心写了 shell 关键字,比如let "a = index",引号也能避免被词法解析器干扰。

特别提醒一下*的使用。在 shell 中,裸的*是文件通配符,如果你写let a=5*3,而当前目录下有文件叫3,那5*3会被展开成文件名的形式,结果就是语法错误或者计算错误。在脚本里必须写成let "a = 5 * 3"或者let a=5\*3转义。很多新手在这个问题上折腾半天,其实是没搞清楚 shell 的通配符展开优先级。

2.2 变量引用:什么时候加 $ ,什么时候不加

let 命令有个很有意思的设计:表达式里的变量,加不加$都行。比如:

x=10 let y=x+5 echo $y # 15 let y=$x+5 echo $y # 15

两种写法结果一样。为什么?因为 let 的底层会先对表达式做变量名解析,没加$的纯变量名会被当作变量引用,加了$的会被 shell 展开成值,然后参与算术运算。

但在某些场景下,两者是有区别的。看个例子:

str="3+4" let a=str+1 echo $a # 错误:str 不是整数,找不到变量 str let b=$str+1 echo $b # 8

str这个变量名本身不存在,所以let a=str+1报错;但$str展开成值3+4,然后得到3+4+1=8。反过来,如果变量名存在但值是字符串,情况又不一样。这里不展开太深,核心结论是:当你拿不准这个变量到底存不存在、值是什么时,老老实实加$更稳妥,至少语义和 shell 其他位置一致。

另外还有一层:let 可以直接给变量赋值,也就是把这个变量当作左值。这个场景下变量名不加$是必须的,因为加$之后展开成值,就变成let 10=5+5这种非法语法了。

let "result = (a + b) * c"

这种写法在脚本里非常常用,尤其是一连串计算之后把结果保存在一个新变量里。它本质上把 let 当成了 shell 里的一个迷你赋值语言。

2.3 返回值状态码:一个不能忽视的细节

shell 命令的退出状态码 0 表示成功,非 0 表示失败,let 的规则有点特殊:它根据最后一个算术表达式的结果来判断。如果结果是非 0 的,返回 0(成功);如果结果是 0,返回 1(失败)。

let "a = 0" echo $? # 1,因为表达式结果是 0 let "b = 10" echo $? # 0,因为表达式结果是 10 let "c = -1" echo $? # 0,非 0 即成功

这个特性容易被忽略,但在条件判断里特别有用。比如你写:

let "remainder = n % 2" if [ $remainder -eq 0 ]; then echo "偶数" fi

这是常规写法,还可以利用 let 的状态码直接简写成:

if ((n % 2 == 0)); then echo "偶数" fi

或者更直接的:

if let "n % 2 == 0"; then echo "偶数" fi

let 表达式求值结果为真(非 0)则 if 成立。这就能写出非常紧凑的脚本。但要注意,表达式结果恰好为 0 时,if 分支不会走进去,别把逻辑搞反了。我见过不少人在这里踩坑,写判断条件时没注意到表达式结果为 0 的语义,导致条件恒真或恒假。

2.4 进制与位运算支持

let 支持多种进制表示,这一点很多人在排查 bug 时才会注意到。

  • 十进制:let a=10
  • 八进制:以 0 开头的数字。let a=077,值是 63
  • 十六进制:以 0x 开头。let a=0x1F,值是 31
  • 任意进制:base#number的写法,比如let a=2#1011是二进制,值是 11

这个base#number语法非常强大,在处理二进制、八进制、十六进制输入时,不需要额外调用外部命令。比如写一个脚本把二进制字符串转十进制:

bin="101101" let "dec = 2#$bin" echo $dec # 45

这里变量 $bin 展开成 101101,然后2#101101按二进制解析成十进制 45。这个特性在处理网络权限、文件权限、位掩码时特别有用。比如解析一个八进制的文件权限:

perm="755" let "value = 8#$perm" echo $value # 493

这在写一些处理 chmod 数据的辅助脚本时,能节省大量字符串解析工作。

let 也支持全套位运算:左移右移、按位与或异或、按位取反。位运算在一些高性能脚本里作用很大,比如做权限掩码判断:

mask=0b111000 let "p = 0b101000" let "r = p & mask" echo $r # 40

let 对二进制的0b前缀在较高版本的 bash 里也支持,不过为了老版本系统兼容,我一般用2#写法更稳妥。

3. 实际场景里 let 的高频用法

3.1 计数器:循环里最常见的应用模式

let 在循环计数器里的地位几乎是不可撼动的。最典型的场景:

count=0 while [ $count -lt 5 ]; do echo "第 $count 次循环" let count=count+1 done

后面这句let count=count+1是标准的自增写法,也可以简写成:

let count++ let count+=1 let count=count+1

三种写法等价。let count++是后置自增,表达式的结果是加 1 之前的值,但变量确实加了 1;如果你需要先加后用,可以写let ++count或者直接let count+=1。

更现代的脚本通常用 for 循环:

for ((i=0; i<5; i++)); do echo "第 $i 次循环" done

这里的(( ))和 let 完全等价,可以理解为let "i=0; i<5; i++"的便捷形式。for 循环的三个表达式之间用分号分隔,这种写法在 C 语言里很常见,shell 用户看到会觉得格外亲切。

计数器场景有一个实际经验:在循环里频繁执行 let 时性能相差无几,但如果循环规模特别大,比如上百万次,外部命令 expr 会明显慢,因为每次 expr 都要 fork 一个子进程。let 是内建命令,没有这个开销。我之前跑一个 100 万次的循环,用 expr 要几十秒,换 let 一瞬间跑完,完全不在一个量级。

3.2 复合赋值:可以让脚本更简洁

let 支持+=、-=、*=、/=、%=、**=一整套复合赋值运算符,这几乎照搬了 C 语言的运算符体系。比如:

let "x = 10" let "x += 5" echo $x # 15 let "x *= 2" echo $x # 30 let "x %= 7" echo $x # 2

注意**=是幂运算,比如let "x = 2",然后let "x **= 10",得到 1024。shell 里幂运算支持得不多,let 有这个能力算是意外之喜。在脚本里写计算里平方、立方时,不需要调外部命令。

复合赋值在处理累加累乘时非常有用。比如计算一个目录下所有文件的总行数:

total=0 for file in *.txt; do lines=$(wc -l < "$file") let "total += lines" done echo "总行数: $total"

累加逻辑就一行,清晰直白。如果把let "total += lines"换成total=$((total + lines))也完全等价,看个人习惯。我写代码时更倾向 let 的复合赋值形式,因为可读性好,一眼就能看出是在累加。

3.3 多表达式的逗号运算符

let 支持在同一行执行多个算术表达式,用逗号分隔。这在命令行里操作临时变量时特别方便。

let "i = 1, j = 2, k = i + j" echo $i $j $k # 1 2 3

逗号运算符的特性是:表达式从左到右依次求值,最后一个表达式的值作为整体结果,影响退出状态码。这个特性在一次给多个变量赋值的场景下非常好用。

还有一个实用场景:在单行循环里同时更新多个变量。比如斐波那契数列:

a=0 b=1 for ((n=0; n<10; n++)); do echo "$a" let "tmp = a, a = b, b = tmp + b" done

这里let "tmp = a, a = b, b = tmp + b"一行完成了三个变量的更新,而且因为逗号按顺序求值,不会出现变量被提前覆盖的问题。这个写法精简且高效。

但要注意一点:逗号运算符不是所有 shell 版本的 let 都支持完全一致的行为,在 bash 里很可靠,zsh 里也行,但有些精简过的 shell 可能不支持。写项目脚本时如果只是针对 bash,放心用;如果要移植到别的 shell,先做个兼容性测试。

3.4 配合脚本参数与退出码

let 经常被用来处理命令行传进来的数值参数,比如:

calc() { let "result = $1 * $2" echo $result } calc 6 7 # 42

这里$1和$2是外部传入的参数,展开后参与运算。如果参数不是一个合法的数值,shell 会报语法错误,所以在交互式脚本里最好先做参数校验:

if ! [[ $1 =~ ^[0-9]+$ ]]; then echo "参数必须是数字" >&2 exit 1 fi

另外,可以用 let 的退出码来做一个数值条件的判断分支。比如检查一个数是否在某个区间:

n=15 if let "n > 10 && n < 20"; then echo "n 在 10 到 20 之间" fi

&&和||逻辑运算在 let 里正常支持,表达式求值结果符合预期,退出码也对应真假。这个写法比[ "$n" -gt 10 -a "$n" -lt 20 ]看着舒服多了。

4. let 与其他算术方法的对比:选型指南

4.1 一张表看懂差异

工具类型浮点支持性能可移植性适合场景
letshell 内建不支持极高bash/zsh/ksh,dash 不支持脚本内循环、计数、变量赋值
$(())算术扩展不支持极高bash/zsh,POSIX 可用需要取得计算结果时
expr外部命令不支持(写法繁琐)低几乎所有 Unix老脚本兼容
bc外部命令支持中低几乎所有 Unix浮点数、高精度运算
declare -ishell 内建不支持高bash需要自动整数化的变量

这张表基本覆盖了我在实际项目里见过的所有场景。连 POSIX 都没有要求 shell 必须支持 let,所以 let 只能“能用但别指望所有环境都有”。

4.2 场景化的工具选择建议

在 for 循环或 while 循环里,我几乎不用 expr,原因前面说了,性能差太多。如果是简单的整数算术并需要拿到结果,推荐$(( )):

total=$((count + 10))

如果表达式不需要返回值,只想修改变量或者做条件判断,let 和(())都好。写成let "x += 1"和((x += 1))是等价的,后者在某些人看来更像 shell 原生的写法。

如果需要浮点数运算,比如计算平均分、折扣价、百分比,let 干不了,直接上 bc:

result=$(echo "scale=2; 5 / 3" | bc) echo $result # 1.66

如果你有变量已经是数字字符串,又想省去类型转换,declare -i也是一种思路。但它的使用范围窄,我这里只是提醒有这个东西,不推荐过度使用。

4.3 一个场景两种实现:谁优谁劣

拿一个具体需求来比较:统计 1 加到 100 的总和。

用 expr 的写法:

sum=0 i=1 while [ $i -le 100 ]; do sum=$(expr $sum + $i) i=$(expr $i + 1) done echo $sum

用 let 的写法:

sum=0 i=1 while let "i <= 100"; do let "sum += i" let "i++" done echo $sum

这是两个很有代表性的写法。第一个里面,expr 每次算出结果还要通过命令替换拿回来,每个循环迭代至少 fork 两个子进程;第二个全程内建命令,不产生任何额外进程。规模小的时候没感觉,循环次数到一万以上差距就开始明显了,到百万级几乎就是十秒级和毫秒级的差异。写脚本的人要注意这种细节,尤其日志分析类脚本经常要处理大量文本。

5. 实际踩坑记录与问题排查

5.1 诡异结果不报错的空参数问题

有一次帮同事排查一个 flaky 的部署脚本,现象是每次计算的并发数都是 0,但逻辑上明明传了参数。最后定位到他那行写的:

let threads=MAX_THREADS

而 MAX_THREADS 是一个字符串变量,值是"16"带引号吗?并不是,是环境变量没有正确导出,结果这个变量是空的。let 拿到一个空变量名,直接给它赋了个空值,输出就成了 0,没有任何报错。

这里的核心陷阱是:let 对未定义变量的处理不像其他语言会抛异常,它默认当成 0。所以如果你在脚本里依赖某个外部传入的变量,先做一次变量存在性检查:

if [ -z "$MAX_THREADS" ]; then MAX_THREADS=4 fi let "threads = MAX_THREADS"

这种问题最恶心的地方是不报错,只在结果里体现。排查时要善用set -u(未定义变量时报错)和set -x(打印执行过程),能极大缩短定位时间。

5.2 浮点数进 let:一个报错的例子

let 只支持整型。你输入:

let "a = 3.14"

bash 会直接报语法错误:syntax error: invalid arithmetic operator。这不算 bug,是设计边界。

但更隐蔽的是变量里的浮点数:

pi=3.14 let "r = pi * 2"

这里 pi 展开成 3.14,let 尝试解析3.14 * 2,同样报语法错误。如果你在写一个通用的脚本,需要同时处理整数和浮点数,建议提前判断:整型运算走 let,浮点运算走 bc。或者在脚本入口统一转换成整数,比如把兆字节换算成字节数这种场景,用整数乘法就够了。

专门做单位换算时,let 其实很合适。比如:

let "bytes = size_mb * 1024 * 1024"

因为是整数乘法,完全不涉及浮点问题,而且计算超快。

5.3 bash 之外的 shell:dash 不支持 let

如果你在写一个以#!/bin/sh开头的脚本,而这个 sh 在 Ubuntu 或者 Debian 上指向 dash,那么 let 命令直接不存在。执行会报let: not found。

解决方案有三个:

  1. 哈希行改#!/bin/bash,显式声明用 bash
  2. 不用 let,全部用$(( ))替代
  3. 用一个函数封装兼容逻辑

我自己的经验是:如果你不确定目标机器上有没有 bash,用$(( ))是最保险的,它属于 POSIX 标准语法,几乎所有现代系统都支持。如果你确认系统有 bash,那 let 写起来确实舒服,两者不冲突。

这里有个令人哭笑不得的历史细节:bash 的作者在早期写 let 的时候,是从 C shell 的@命令借鉴来的思路,后来 bash 又自己加了(( )),结果(( ))后来居上成了主流,let 反而被一些人遗忘了。但这不代表 let 过时,很多老练的运维脚本里依然大量用 let,尤其是处理大量循环计算的场景。

5.4 引号里面再引号的问题

let 表达式里如果嵌套了字符串变量,比如从一个文本文件里读取一个数,这个数周围有空白字符,let 解析时可能出错。

num=$(cat number.txt) # 假设内容是 " 42 ",带了空格 let "result = num * 2"

这里 num 展开后是42,有前后的空格,let 解析42 * 2时到 42 后面没直接接运算符,可能报语法错误。稳妥的做法是先把变量转成整数:

num=$(cat number.txt) num=$((num)) # 利用算术扩展的强转能力 let "result = num * 2"

$((num))会对值做一次算术解析,如果 num 是纯整数字符串带空白,扩展后还是能正常解析;但如果 num 本身包含非数字字符,会直接报错。另一个办法是直接用num=${num//[^0-9]/}把非数字字符剥掉,再交给 let。

这类问题的根源在于 shell 的变量展开发生在 let 解析之前,所以你不能假设 let 能像其他语言编译器那样做类型推断。写健壮脚本的要点是:凡是来自外部输入的数,先清洗、再参与运算。

5.5 条件判断表达式与关键字冲突

let 表达式里如果出现 shell 关键字,比如let "echo = 1",这种在没有引号的时候会先被 shell 解析,把 echo 当命令执行,非常危险。所以养成好习惯:let 表达式一律引号包裹。万一变量名真的叫 time、type 之类,引号也是必须的。

另外,如果脚本里定义了函数与 let 交互,注意变量作用域。函数内用 let 定义的变量默认是全局的,除非你用local关键字声明。这个坑比较容易出现在写库函数时:

my_func() { local count let "count = 10" echo $count }

这里的local count只是声明了变量,let 里赋值才能确保函数内局部。如果不写 local,函数外部如果有同名变量,let 会直接修改它,带来隐形的副作用。

5.6 调试技巧:用 set -vx 观察表达式解析

如果你实在找不到 let 表达式哪里出了问题,最直接的办法是开启 shell 的调试模式:

set -vx let "r = a + b * (c - d)" set +vx

运行后,bash 会显示命令展开过程、参数拆分结果以及每一步的计算值。这能看到*是否被通配符展开、变量展开后的值是什么、最终退出码是多少。在脚本里这种调试方式比到处加 echo 高效得多,我基本每次都靠它定位空格类诡异问题。

6. 几个可以直接拿去用的实战脚本片段

6.1 下载进度计算

模拟一个下载进度条,百分比是整数计算:

total=1000 current=320 let "percent = current * 100 / total" echo "已下载 $current / $total,进度 $percent%"

注意current * 100可能超过整数范围?在 bash 里默认整数是 64 位,一般不用担心溢出。但如果你在 32 位机器上跑老系统,乘法结果超过 21 亿就可能出问题,这种情况建议先除后乘,牺牲精度换取范围。

6.2 批量文件重命名

把一堆文件名里的数字加 1 再重命名:

for file in file_*.txt; do num=$(echo "$file" | grep -o '[0-9]*') let "newnum = num + 1" mv "$file" "file_${newnum}.txt" done

这里的核心就是let "newnum = num + 1",一行完成类型转换和加法。其他地方都是文本处理。

6.3 计算质数的朴素筛

用 let 写一个 100 以内的质数判断:

is_prime() { local n=$1 if let "n < 2"; then return 1 fi local i=2 while let "i * i <= n"; do if let "n % i == 0"; then return 1 fi let "i++" done return 0 } for ((num=2; num<=100; num++)); do if is_prime $num; then echo -n "$num " fi done echo

这个脚本用到了 let 的双重身份:一面是条件判断,一面是计数更新。读起来其实有点像 C 语言的语句,习惯了之后写 shell 脚本的效率很高。

6.4 模拟 dice 骰子游戏

一个简单但有意思的例子。利用时间戳模拟随机数:

max=6 min=1 let "range = max - min + 1" let "rand = ($(date +%s) % range) + min" echo "骰子点数: $rand"

这种写法不适合生产环境,但用来演示 let 的表达式组合能力非常直观。重点是let "rand = ($(date +%s) % range) + min",命令替换、算术取模、变量赋值一层层套进去,let 都能处理。

7. 我对 let 的个人经验总结

用 let 写脚本这么多年,我很少在项目里把 let 当作唯一工具,它更像一把趁手的扳手:单线程场景很顺手,但你别指望用它拧所有的螺丝。比较合理的策略是:

  • 循环计数器、累加器、简单整数逻辑直接 let
  • 需要把计算结果嵌入到更长的字符串里时用$(( ))更直观
  • 浮点运算上 bc
  • 大数字高精度计算上 python 或者 awk

let 最大的优点就是快、简洁、符合嵌入式脚本的极简精神。它最大的坑则是语法暗坑多,不报错的诡异行为容易让新手困惑。我的习惯是:把所有 let 表达式统一都用双引号包起来,在脚本开头声明#!/bin/bash,然后每次修改完脚本先用bash -n做语法检查,再跑一行set -euo pipefail提升整体防御性。这样踩到暗坑的概率大幅下降。

如果手头有老旧的 bash 版本,比如 CentOS 6 默认的 bash 4.1,也别担心,let 的绝大部分能力在那个年代都已经具备了,除了个别 bitwise 写法在极端老版本里可能有细微差异。总的来说,只要规避了空格、变量空值、引号展开这几个硬伤,let 是非常可靠的一个内建命令。

最后分享一个我经常用的排查小技巧:写完任意一条 let 表达式,立刻接着写一行echo $?或者在一个变量后面打印它的值。利用 let 退出码判断执行结果,同时看变量值是否符合预期,双保险,能省掉大量后期调试时间。这个习惯帮我处理过不少线上脚本的诡异问题,算是最值得推荐的个人经验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 23:02:42

工业串口服务器选型避坑指南:从参数验证到现场部署的实战经验

工业现场做数据采集和联网改造&#xff0c;绕不开的一个环节就是串口设备怎么接入以太网。很多项目在实验室里跑得挺顺&#xff0c;一到车间就出问题——丢包、断连、数据错位&#xff0c;排查半天发现是选型阶段埋的雷。工业串口服务器这东西看着不起眼&#xff0c;但选错了&a…

作者头像 李华
网站建设 2026/11/5 1:57:36

Koa2实战指南:从中间件洋葱模型到PM2部署全解析

做Node.js后端的朋友&#xff0c;迟早要跟koa打交道。如果你一直用Express写接口&#xff0c;大概能理解那种感觉&#xff1a;回调地狱虽然被Promise缓解了&#xff0c;但中间件体系还是不够顺手&#xff0c;每个业务里都夹着一堆模板代码。koa从2015年前后进入大家视野&#x…

作者头像 李华
网站建设 2026/10/26 17:53:00

CKKS同态加密实战指南:从原理到参数调优

1. 为什么是 CKKS&#xff1a;它解决了同态加密的哪块短板1.1 从整数到浮点&#xff1a;BFV/BGV 的天然局限很多刚接触全同态加密的人&#xff0c;第一次跑通的是 BFV 或 BGV 方案&#xff0c;因为它们逻辑直白&#xff1a;把一个整数当作多项式系数加密&#xff0c;加法和乘法…

作者头像 李华
网站建设 2026/10/23 8:08:37

回文链表最优解:快慢指针+反转链表,O(1)空间判定

回文链表这道题&#xff0c;在LeetCode上是编号234的经典题&#xff0c;但凡是准备面试刷题的人&#xff0c;几乎都会把它放进必刷清单。我当年第一次看到这道题&#xff0c;内心是有点不屑的——回文不就是正着读和反着读一样吗&#xff1f;数组的话直接双指针从两头往中间走就…

作者头像 李华
网站建设 2026/10/23 4:09:35

纯前端三件套实现激光射击游戏:Canvas动画与碰撞检测实战

前一阵子有人问我&#xff0c;用纯 htmlcssjavascript 做一个小游戏到底能做到什么程度。我当时正好在整理一个激光射击游戏的项目&#xff0c;就顺手把整个实现过程重新过了一遍。这个项目最吸引我的地方在于&#xff1a;它不需要任何框架和构建工具&#xff0c;一个 .html …

作者头像 李华
网站建设 2026/11/1 21:43:29

Linux运维实战:不背命令,从原理建立认知地图

刚看到这标题的时候我愣了一下&#xff0c; Liunx ——好家伙&#xff0c;又见这个经典拼写。十多年里我在各种论坛和技术群里见到不下几百次&#xff0c;从刚入门的菜鸟到偶尔撸脚本的老手&#xff0c;总有人把 Linux 打成 Liunx 。说实话这不算什么大事儿&#xff0c;毕…

作者头像 李华