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 知识体系总览

下面这张图,把四个命令的关系和流转路径画清楚了。你可以把它当作操作指南:

环境变量操作命令知识体系 存储设备(Flash/MMC) 内存环境变量(运行时) 启动时加载 saveenv 持久化 printenv 查看变量 setenv 修改/删除变量 saveenv 保存到存储 env default -f 恢复出厂默认 读取 写入 持久化 覆盖 操作流程建议 1. 修改前:printenv 备份当前配置 2. 修改时:setenv 谨慎操作,注意引号 3. 修改后:saveenv 立即持久化 4. 出问题时:env default -f -a 恢复出厂设置

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 环境变量这块你就拿下了。