3、UEFI Shell环境:Shell的启动方式、常用Shell命令、Shell脚本编写基础

UEFI Shell,说白了就是固件层面的一个命令行环境。你想想看,系统还没起来,硬盘还没认全,这时候你想调试、想部署、想修引导,怎么办?UEFI Shell就是干这个的。

我刚开始接触UEFI时,总觉得这玩意儿就是个黑框框,没啥用。直到有一次,客户的机器死活进不去系统,PE也起不来,最后靠Shell把引导修好了。嗯,从那以后,我再也不敢小看它了。

核心认知:UEFI Shell不是操作系统,它是UEFI固件提供的一个交互环境。它可以直接访问硬件、操作文件系统、加载驱动、执行EFI应用程序。说白了,它是你手里最后一把螺丝刀。

3.1 Shell的启动方式

启动Shell的方式有好几种,我按常用程度排个序:

  1. 固件内置Shell——有些主板直接把Shell编译进了固件,开机按某个热键(比如F2、Del)进BIOS设置,然后在启动菜单里选"UEFI Shell"就行。我个人习惯用这种方式,最省事。
  2. 从U盘启动Shell——把Shell.efi文件放到FAT32格式的U盘里,改名为bootx64.efi,放到EFI\BOOT\目录下。然后从U盘启动,就进Shell了。我在项目中遇到过一台老服务器,固件里没内置Shell,就是用这招搞定的。
  3. 从Boot Manager手动加载——在BIOS设置里,找到"添加启动项"或类似选项,手动指定Shell.efi文件的路径。这种方式比较麻烦,但某些定制固件只能用这个办法。
  4. 通过启动项配置——如果你已经在系统里了,可以用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环境的核心逻辑串起来了:

UEFI Shell 环境知识体系 UEFI Shell 环境 启动方式 固件内置 Shell U 盘启动 Shell Boot Manager 手动加载 启动项配置加载 常用命令 map / ls / cd / edit load / reset / help pci / devtree / dmem bcfg 管理启动项 脚本编写 .nsh 文件 / startup.nsh 变量 %var% / 参数 %1 if / else / endif 条件 for / endfor 循环 典型应用场景 系统部署 引导修复 硬件调试

从这张图能看出来,Shell环境就三大块:怎么进去、进去后用什么命令、怎么把命令串成脚本。这三块搞定了,你就能在UEFI Shell里做很多事了。

总结一下:UEFI Shell是系统部署和故障恢复的利器。启动方式要记牢,常用命令要熟练,脚本编写能大幅提升效率。我个人建议,花半天时间把map、ls、edit、bcfg这几个命令练熟,再写几个简单的.nsh脚本试试手。嗯,用不了多久你就能体会到它的威力了。