3、UEFI Shell环境:Shell的启动方式、常用Shell命令、Shell脚本编写基础
UEFI Shell,说白了就是固件层面的一个命令行环境。你想想看,系统还没起来,硬盘还没认全,这时候你想调试、想部署、想修引导,怎么办?UEFI Shell就是干这个的。
我刚开始接触UEFI时,总觉得这玩意儿就是个黑框框,没啥用。直到有一次,客户的机器死活进不去系统,PE也起不来,最后靠Shell把引导修好了。嗯,从那以后,我再也不敢小看它了。
核心认知:UEFI Shell不是操作系统,它是UEFI固件提供的一个交互环境。它可以直接访问硬件、操作文件系统、加载驱动、执行EFI应用程序。说白了,它是你手里最后一把螺丝刀。
3.1 Shell的启动方式
启动Shell的方式有好几种,我按常用程度排个序:
- 固件内置Shell——有些主板直接把Shell编译进了固件,开机按某个热键(比如F2、Del)进BIOS设置,然后在启动菜单里选"UEFI Shell"就行。我个人习惯用这种方式,最省事。
- 从U盘启动Shell——把Shell.efi文件放到FAT32格式的U盘里,改名为
bootx64.efi,放到EFI\BOOT\目录下。然后从U盘启动,就进Shell了。我在项目中遇到过一台老服务器,固件里没内置Shell,就是用这招搞定的。 - 从Boot Manager手动加载——在BIOS设置里,找到"添加启动项"或类似选项,手动指定Shell.efi文件的路径。这种方式比较麻烦,但某些定制固件只能用这个办法。
- 通过启动项配置——如果你已经在系统里了,可以用
bcdedit或efibootmgr添加一个指向Shell.efi的启动项。下次开机就能选了。
我的建议:手边常备一个带Shell.efi的U盘,FAT32格式,目录结构就是EFI\BOOT\bootx64.efi。这玩意儿关键时刻能救命。
3.2 常用Shell命令
Shell命令不算多,但常用的就那么十几个。我挑最核心的说:
| 命令 | 作用 | 常用示例 |
|---|---|---|
map |
列出所有映射的设备 | map -r 重新扫描设备 |
fs0: |
切换到第一个文件系统 | fs0: 然后 ls |
ls |
列出目录内容 | ls -l 详细列表 |
cd |
切换目录 | cd EFI\BOOT |
edit |
文本编辑器 | edit startup.nsh |
load |
加载驱动 | load fs0:\drivers\nvme.efi |
reset |
重启系统 | reset -w 冷重启 |
help |
查看帮助 | help -b 分页显示 |
这里我要特别说一下map命令。你刚进Shell时,第一件事就是敲map -r。为什么?因为Shell不一定自动识别了所有设备。我遇到过好几次,插了U盘但map列表里没有,一加-r参数重新扫描,立马出来了。
注意:Shell里的路径分隔符是反斜杠\,不是正斜杠/。这是Windows的遗产,但UEFI Shell就是这么设计的。写脚本时千万别搞混了。
再说几个我常用的:
pci——查看PCI设备列表,调试硬件时很有用devtree——显示设备树,能看到整个硬件拓扑dmem——查看内存内容,调试时用bcfg——管理启动项,比如添加、删除、修改启动顺序
举个例子,你想把Shell加到启动菜单里:
Shell> bcfg boot add 0 fs0:\EFI\BOOT\bootx64.efi "UEFI Shell"
这条命令的意思是:在启动菜单第0位添加一个启动项,指向fs0:\EFI\BOOT\bootx64.efi,显示名称为"UEFI Shell"。下次开机就能直接选了。
3.3 Shell脚本编写基础
Shell脚本,说白了就是把一堆命令写进一个文件,然后让Shell自动执行。文件扩展名是.nsh,放在FAT32分区里就行。
我最早写Shell脚本是为了批量部署。几十台机器,每台都要进Shell敲一堆命令,太累了。写个脚本,U盘一插,自动跑完,省心省力。
3.3.1 脚本的基本结构
一个最简单的脚本长这样:
echo "Starting deployment..."
map -r
fs0:
cd EFI\BOOT
load nvme.efi
reset
保存为deploy.nsh,放到U盘根目录。然后在Shell里执行:
Shell> fs0:\deploy.nsh
或者,你把脚本命名为startup.nsh,放到U盘根目录。Shell启动时会自动执行它。嗯,这个技巧我经常用。
3.3.2 变量与参数
Shell脚本支持变量,用%包围:
set -v MyPath fs0:\EFI\BOOT
echo %MyPath%
ls %MyPath%
也支持命令行参数,%1、%2这样:
echo "First param: %1"
echo "Second param: %2"
调用时:
Shell> fs0:\test.nsh hello world
3.3.3 条件判断
Shell脚本的条件判断语法有点怪,但用熟了就好:
if %1% == "debug" then
echo "Debug mode enabled"
load fs0:\debug.efi
else
echo "Normal mode"
load fs0:\release.efi
endif
避坑指南:我曾经在条件判断里忘了加then,结果脚本直接报错退出。UEFI Shell的语法检查很严格,少一个关键字都不行。写完后最好用edit命令检查一遍。
3.3.4 循环
循环用得不多,但偶尔也用:
for %a run (1 2 3 4 5)
echo "Iteration %a"
endfor
或者遍历文件:
for %f in fs0:\*.efi
echo %f
endfor
3.3.5 一个完整的部署脚本示例
下面是我在实际项目中用过的脚本,稍微简化了一下:
echo "========================================"
echo " UEFI Deployment Script v1.0"
echo "========================================"
echo "Step 1: Rescan devices..."
map -r
echo "Step 2: Load NVMe driver..."
if exist fs0:\drivers\nvme.efi then
load fs0:\drivers\nvme.efi
echo "NVMe driver loaded."
else
echo "Warning: NVMe driver not found!"
endif
echo "Step 3: Copy boot files..."
if exist fs1: then
fs1:
mkdir EFI\BOOT
cp fs0:\bootx64.efi fs1:\EFI\BOOT\
echo "Boot files copied."
else
echo "Error: Target disk not found!"
goto end
endif
echo "Step 4: Add boot entry..."
bcfg boot add 0 fs1:\EFI\BOOT\bootx64.efi "My OS"
echo "Step 5: Done. Rebooting..."
reset
:end
echo "Script finished with errors."
这个脚本做了几件事:重新扫描设备、加载NVMe驱动、复制引导文件、添加启动项、最后重启。每一步都有检查,出错了会提示。我在项目中用过类似的脚本,部署了上百台机器,基本没出过问题。
3.4 Shell环境的知识体系
下面这张图把Shell环境的核心逻辑串起来了:
从这张图能看出来,Shell环境就三大块:怎么进去、进去后用什么命令、怎么把命令串成脚本。这三块搞定了,你就能在UEFI Shell里做很多事了。
总结一下:UEFI Shell是系统部署和故障恢复的利器。启动方式要记牢,常用命令要熟练,脚本编写能大幅提升效率。我个人建议,花半天时间把map、ls、edit、bcfg这几个命令练熟,再写几个简单的.nsh脚本试试手。嗯,用不了多久你就能体会到它的威力了。