3. 环境变量操作命令:printenv、setenv、saveenv、env default -f 的详细用法与实战
环境变量操作,是 U-Boot 里最基础也最核心的活儿。说白了,你玩 U-Boot 就是在跟这些变量打交道。我见过不少新手,上来就乱改变量,结果板子起不来,然后一脸懵地来找我。嗯,今天咱们就把这几个命令彻底讲透。
核心认知:U-Boot 的环境变量存储在特定的存储区域(如 SPI Flash、EMMC、NAND 等),系统启动时加载到内存中运行。你对变量的所有修改,默认只在内存中生效。想永久保存?必须显式调用保存命令。
3.1 printenv —— 查看环境变量的“眼睛”
这个命令最简单,但也最容易被忽视。我习惯在每次修改环境变量前后,都先跑一遍 printenv,确认当前状态。
基本用法
# 打印所有环境变量
U-Boot> printenv
# 打印指定变量
U-Boot> printenv bootargs
bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rw
# 打印多个变量
U-Boot> printenv bootcmd bootargs
bootcmd=mmc read 0x40000000 0x800 0x4000; bootm 0x40000000
bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rw
你想想看,printenv 不加参数时,会把所有变量都列出来。如果变量特别多,输出会很长。这时候我一般会用 printenv | grep xxx 来过滤,但前提是你的 U-Boot 编译时开启了管道支持。
小技巧:如果你只想看某个变量是否存在,可以用 printenv 变量名。如果变量不存在,U-Boot 会返回空,不会报错。这在脚本里做条件判断时很有用。
实战场景:排查启动失败
有一次,客户说板子起不来。我远程连上去,第一件事就是 printenv bootargs。结果发现 root 参数指向了一个不存在的分区。这就是典型的“环境变量被意外修改”案例。所以,遇到启动问题,先看环境变量,别急着怀疑硬件。
3.2 setenv —— 修改变量的“手术刀”
setenv 是使用频率最高的命令。它的语法很灵活,但坑也不少。
基本语法
# 设置/修改变量
U-Boot> setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rw'
# 删除变量(赋空值)
U-Boot> setenv bootdelay
# 设置变量为空字符串
U-Boot> setenv myvar ''
注意看,删除变量和设置空字符串是两码事。删除后,printenv myvar 什么都看不到;设置空字符串后,变量还在,只是值为空。我在项目中遇到过因为混淆这两者导致的脚本 bug,排查了半天。
警告:setenv 修改变量后,不会自动保存到存储设备。如果你直接断电,修改就丢了。必须执行 saveenv 才能持久化。
引号的使用规则
这个点特别容易出错。我总结了一个表格,你照着用就行:
| 场景 | 写法 | 说明 |
|---|---|---|
| 值包含空格 | setenv var 'a b c' |
必须用引号包裹 |
| 值包含特殊字符 | setenv var 'root=/dev/mmcblk0p2' |
单引号防止 Shell 解释 |
| 值包含单引号 | setenv var "it's ok" |
外层用双引号 |
| 值包含变量引用 | setenv var '${othervar}' |
单引号阻止立即展开 |
| 多行值 | setenv var 'line1\nline2' |
用 \n 换行 |
3.3 saveenv —— 持久化的“保险柜”
这个命令没有参数,执行就完事了。但它的背后,藏着不少门道。
U-Boot> setenv serverip 192.168.1.100
U-Boot> saveenv
Saving Environment to SPI Flash...
Erasing SPI flash... done
Writing to SPI flash... done
看到 "Erasing" 和 "Writing" 了吗?这说明环境变量存储区在 SPI Flash 上。saveenv 会先擦除整个环境变量分区,再重新写入。如果这个过程中断电,环境变量就全丢了。嗯,这就是为什么我总说“改完变量先 saveenv,再断电”。
经验之谈:在产品量产阶段,我习惯在烧录完系统后,执行一次 saveenv 来固化默认配置。这样即使后续操作失误,也能通过 env default -f 恢复。
不同存储介质的差异
| 存储介质 | 特点 | 注意事项 |
|---|---|---|
| SPI Flash | 擦写次数有限(约10万次) | 频繁 saveenv 会缩短寿命 |
| NAND Flash | 可能有坏块,需要 ECC | saveenv 失败时检查坏块 |
| EMMC | 速度快,寿命长 | 注意分区表不要被覆盖 |
| FAT/NTFS 文件 | 以文件形式存储 | 文件系统损坏会导致丢失 |
3.4 env default -f —— 恢复出厂设置的“后悔药”
这个命令我用的不多,但每次用都是救命时刻。比如你把 bootcmd 改错了,板子起不来,这时候就需要它。
用法详解
# 恢复所有变量到默认值(不保存)
U-Boot> env default -a
# 恢复所有变量到默认值并强制保存
U-Boot> env default -f -a
# 恢复单个变量到默认值
U-Boot> env default bootargs
-f 参数是关键。没有它,env default 只是把默认值加载到内存,不会写入存储。加上 -f,它会直接擦除存储区并写入默认值。我曾经在调试时忘了加 -f,结果重启后变量还是旧的,折腾了半天才发现问题。
注意:env default -f -a 会清空所有自定义变量。如果你在板子上配置了 MAC 地址、序列号等唯一标识,执行这个命令后它们就没了。所以,量产后的板子,慎用!
实战:从变砖边缘救回来
有一次,我在调试网络启动时,把 bootcmd 改成了一个死循环脚本。保存后重启,板子一直在循环里出不来,串口都来不及打断。怎么办?
我按住复位键,上电后快速在串口敲回车,侥幸进入了 U-Boot 命令行。然后立刻执行:
U-Boot> env default -f -a
U-Boot> saveenv
U-Boot> reset
板子活了。这就是 env default -f 的威力。当然,如果你连串口都进不去,那就只能上烧录器了。所以,我建议你在改关键变量前,先用 printenv 把当前配置备份到文本文件里。
3.5 知识体系总览
下面这张图,把四个命令的关系和流转路径画清楚了。你可以把它当作操作指南:
3.6 避坑指南与最佳实践
最后,分享几个我这些年踩过的坑,你遇到了能少走弯路:
- 改完变量不保存就断电 —— 这是最常见的错误。我见过有人改了一堆变量,断电重启后全没了,然后骂 U-Boot 有 bug。其实只是忘了 saveenv。
- env default 不加 -f —— 只恢复内存,不写存储。重启后变量还是旧的。我曾经在产线上因为这个被折腾了一下午。
- 变量值里混了不可见字符 —— 比如从 Windows 复制过来的换行符。用
printenv看不出来,但脚本执行就是不对。我一般用echo $变量名来检查。 - 环境变量空间溢出 —— 每个 U-Boot 对环境变量总长度有限制(通常是 4KB~16KB)。变量太多或值太长,saveenv 会失败。这时候需要删掉一些不用的变量。
我的习惯:在开发阶段,我会在 U-Boot 命令行里执行 setenv saveenv 来测试保存功能是否正常。如果 saveenv 报错,先检查存储分区是否被写保护了。
好了,这四个命令的用法就讲到这里。记住:printenv 是眼睛,setenv 是手术刀,saveenv 是保险柜,env default 是后悔药。用好它们,U-Boot 环境变量这块你就拿下了。