2、环境变量管理(上):printenv、setenv、saveenv 详解

环境变量,说白了就是 U-Boot 的“记忆”。板子一上电,U-Boot 怎么知道从哪里启动?IP 地址配多少?内核用什么参数?全靠这些变量撑着。

我刚开始接触 U-Boot 时,总觉得环境变量就是个配置文件。后来调一块新板子,发现启动参数写错了,板子死活起不来。从那以后,我对这三个命令就特别上心。

核心认知:环境变量是 U-Boot 运行时的键值对数据库。printenv 负责查看,setenv 负责修改,saveenv 负责持久化。缺一个,你的配置就可能在下次重启后丢失。

环境变量管理核心流程 printenv 查看当前环境变量 setenv 修改变量或新建变量 saveenv 保存到持久存储 关键逻辑: setenv 只修改内存中的值 → 必须 saveenv 才能写入 Flash/EEPROM → 下次重启才生效 重启后环境变量持久化生效

2.1 printenv —— 把家底翻出来看看

printenv 是最常用的命令之一。不加参数,它会打印所有环境变量。我调试时第一件事就是敲它,看看当前板子的“状态快照”。

U-Boot> printenv
baudrate=115200
bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rw
bootcmd=mmc dev 0; fatload mmc 0:1 0x82000000 uImage; bootm 0x82000000
bootdelay=2
ethaddr=00:11:22:33:44:55
ipaddr=192.168.1.100
serverip=192.168.1.10
stderr=serial
stdin=serial
stdout=serial

Environment size: 412/16380 bytes

最后一行很关键——它告诉你当前环境变量占了多少空间,总容量多大。如果接近上限,你就要小心了。

我的习惯:每次拿到一块新板子,第一件事就是 printenv 并把输出保存到文本文件。这样万一改乱了,还能对照着恢复。我曾经因为手滑删了 bootcmd,板子变砖,幸亏有备份才救回来。

printenv 也支持查看单个变量:

U-Boot> printenv bootargs
bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rw

如果变量不存在,U-Boot 会告诉你:

U-Boot> printenv nonexist
## Error: "nonexist" not defined

2.2 setenv —— 改变量,就这么简单

setenv 的语法很直白:

setenv 变量名 变量值

举个例子:

U-Boot> setenv ipaddr 192.168.1.200
U-Boot> printenv ipaddr
ipaddr=192.168.1.200

注意,这时候修改只存在于内存中。你重启板子,ipaddr 又会变回原来的值。想永久保存?往下看 saveenv。

setenv 还有一个重要用法——删除变量:

U-Boot> setenv myvar
U-Boot> printenv myvar
## Error: "myvar" not defined

不给值,就是删除。这个操作很危险,尤其是误删了 bootcmd 或 bootargs。我见过有人调试时想清一个临时变量,结果手快把 bootcmd 删了,板子再也起不来。

警告:删除变量前,先用 printenv 确认变量名。特别是 bootcmd、bootargs、ethaddr 这些关键变量。删错了,板子可能无法启动。

setenv 也支持带空格的变量值,用引号括起来就行:

U-Boot> setenv myvar "hello world"
U-Boot> printenv myvar
myvar=hello world

你想想看,如果不用引号,U-Boot 会把 world 当成下一个参数,结果就变成了两个变量。

2.3 saveenv —— 把修改写进“永久记忆”

saveenv 没有参数,直接敲就行:

U-Boot> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done

它会将当前内存中的所有环境变量,写入持久存储设备。这个设备可能是 SPI Flash、NAND、MMC 或 EEPROM,取决于你的板子配置。

存储介质 特点 常见场景
SPI Flash 速度慢,容量小,可靠 小容量嵌入式板
NAND Flash 容量大,有坏块问题 高密度存储设备
MMC/SD 速度快,分区管理 树莓派、开发板
EEPROM 字节级读写,寿命长 网络设备、服务器

saveenv 执行后,下次上电或重启,U-Boot 会从存储介质中重新加载环境变量。你之前用 setenv 做的修改,才会真正生效。

我的建议:养成“改完就存”的习惯。我见过太多人改了变量,然后直接断电或重启,结果发现修改没生效,又回来查半天。setenv 之后立刻 saveenv,形成肌肉记忆。

2.4 三个命令的协作关系

这三个命令,说白了就是一套“读-改-写”流程:

  1. printenv:读取当前环境变量(从内存中)
  2. setenv:修改内存中的变量值
  3. saveenv:将内存中的变量写入持久存储

重启后,U-Boot 从持久存储加载环境变量到内存。所以如果你只 setenv 不 saveenv,重启后一切归零。

记住这个口诀:printenv 看现状,setenv 改内存,saveenv 写持久。三步缺一不可。

2.5 避坑指南

我这些年踩过的坑,总结成几条:

  • 变量名大小写敏感:bootargs 和 Bootargs 是两个不同的变量。U-Boot 习惯用小写,别搞混。
  • 值里不要有多余空格:setenv ipaddr 192.168.1.100 (末尾多了一个空格),printenv 看不出来,但实际值多了个空格,可能导致网络配置异常。
  • 环境变量空间有限:printenv 最后一行会显示已用/总大小。如果满了,saveenv 会失败。我曾经在调试时塞了一大堆临时变量,结果 saveenv 报错,排查了半天。
  • ethaddr 不要乱改:MAC 地址通常是出厂烧录的,改错了可能导致网络不通。如果你非要改,先记下原始值。

我曾经犯过的错:有一次调试网络,我改了 ethaddr,然后 saveenv。结果板子连不上交换机,查了半天才发现 MAC 地址冲突。最后只能重新烧录环境变量分区才恢复。所以,改关键变量前,一定先 printenv 备份。