以编程方式重启计算机并启动到闪存驱动器

以编程方式重启计算机并启动到闪存驱动器

我最初在 Stack Overflow 上发布了这个问题,但有人告诉我这里可能是发布这个问题的更好的地方,所以就这样了;

我创建了一个小脚本,它将自动重新启动计算机进入其恢复序列(Windows 恢复环境)。这是我的代码:

$testpath = test-path "bcdedit.txt"
if ($testpath -eq $true){
    Remove-item "bcdedit.txt"
}
bcdedit | Out-File "bcdedit.txt"
foreach($line in Get-Content .\bcdedit.txt) {
    if($line -match $regex){
        if($line -like 'recoverysequence*') {
            $variable = "{" + $line.Split('{')[-1]
        }
    }
}
bcdedit /bootsequence $variable
Remove-item "bcdedit.txt"
shutdown /r /t 1

此 Powershell 脚本从我的 Electron(基于 Node.js 的框架)应用程序运行。此应用程序每天在数百种不同的计算机型号上运行,收集它们的硬件信息并将其与数据库进行比较,以在生产环境中检索设备的 UPC。

但是,我之所以有这个功能,是因为我不知道该怎么做。我希望这个脚本能够真正重启计算机,并启动 USB 驱动器的 .EFI 文件。重新启动到 Windows 恢复环境是我实施的一种解决方法,因为它提供了一个用于启动到 USB 闪存驱动器的按钮。我在这里发布是因为我真的想省去这个步骤,直接启动到 USB 闪存驱动器。运行该程序的 USB 闪存驱动器还将包含一个可启动的 .EFI 文件。

现在的问题是:我如何编写代码(在 PowerShell、Electron 或 Node.js 中)来重新启动计算机并使其从 USB 闪存驱动器启动?

我曾尝试研究如何使用来做到这一点bcdedit,但什么也找不到,而且我真的不知道任何其他搜索词来尝试找到我需要的东西,我在这里茫然无措。

答案1

忽略这个听起来像 XY 问题的事实,因为当您已经拥有正在运行的操作系统的管理访问权限时,我看不出需要启动到外部驱动器来“收集硬件信息”的解释...

让 Windows 启动到插入的 USB 驱动器非常简单。使用 为驱动器创建启动项bcdedit create,然后告诉 Windows 启动到该驱动器。创建启动项是微软在此处记录

根据文档,复制现有条目可能会更容易:

bcdedit /copy {current} /d "My Boot Entry Description"

bcdedit 将返回您需要提取的新启动项的 GUID:

The entry was successfully copied to {some-autogenerated-guid-here}.

然后设置新条目的选项以指向您的 USB 设备。您需要更改的最少属性是设备、操作系统设备和路径。根据您要启动的内容,您可能需要修改其他值 - 只需查看现有 Windows 加载程序和/或 PE/PXE 加载程序的 bcdedit 输出作为示例。

bcdedit /set {your-new-boot-entry-guid} device partition=D:
bcdedit /set {your-new-boot-entry-guid} osdevice partition=D:
bcdedit /set {your-new-boot-entry-guid} path \Location\Of\Your\Bootloader.efi

然后告诉 Windows 启动它,就像您已经在做的那样:

bcdedit /bootsequence {your-new-boot-entry-guid}

显然,替换My Boot Entry Description为一些有用的内容,例如一个唯一标识符,以便将来帮助您找到该条目,以便重新使用或删除它(如果您每天都运行该脚本)。替换D:为您尝试启动的 USB 设备的驱动器号;如果您在异构系统上执行此操作,则可能必须以编程方式确定这一点,因为驱动器号会因系统而异。然后替换\Location\Of\Your\Bootloader.efi为您尝试启动的 .EFI 文件的相对路径。

答案2

我知道这已经是两年多前的事了,但本周我不得不做类似的事情,可能偶然发现了一些有用的东西。与其查看启动管理器,不如查看固件启动管理器,它甚至在启动管理器之前就运行了。在您的系统上,以“bcdedit /enum FIRMWARE”开头,它应该显示 {fwbootmgr} 以及任何可启动设备分区的条目。您的 USB 设备(假设为 F:)应该有一个条目,其中包含以下行:

identifier              {9f9738d7-c2c0-11ea-89ac-806e6f6e6963}
device                  partition=F:

使用 %Your_ID% 中的设备标识符运行:

bcdedit /set {fwbootmgr} bootsequence %Your_ID%

bootsequence 参数将指示固件启动管理器仅在下次启动时启动到该设备/分区。此后,它将恢复到其当前的“显示顺序”,该顺序可能首先是 {bootmgr}。

以下是 Windows 批处理文件片段,可能有助于从 USB 设备的驱动器号中查找 {id}:

setlocal ENABLEDELAYEDEXPANSION

set DEVICE=partition=F:
for /F "usebackq tokens=1-3" %%a in ( `bcdedit /enum FIRMWARE` ) DO (
    if /I "%%a" == "identifier"    set ID=%%b
    if /I "%%b" == "%DEVICE%"      set F_ID=!ID!
)
echo Found ID  %F_ID%
bcdedit /set {fwbootmgr} bootsequence %F_ID%

我希望这能有所帮助。

答案3

又过了两年,但我最近做了类似的事情,我认为这可能也适用于你的情况。

我们不需要依靠 Windows 启动管理器,甚至不需要依靠固件启动管理器,而是直接修改 EFI,间接修改 BIOS 启动顺序,而无需真正修改 BIOS。

基本上,我在不同的驱动器上有一个 Windows 分区和一个 Linux(Ubuntu)分区,每个驱动器上都有单独的引导加载程序。它们可以使用单独的 EFI 分区,甚至可以共享相同的 EFI 分区。

简单来说,BIOS 在启动时会查找所有驱动器上的 EFI 分区,然后在这些分区上查找 .EFI 文件,并从找到的任何 Bootloader 生成启动条目。这些条目可以是每个 EFI 分区一个,也可以是多个。通常它们将安装在单独的文件夹中。

我所做的就是编写一个脚本,将当前活动操作系统的所有 .EFI 文件重命名为不同的文件扩展名(如 .BAK),对于我想要引导到的其他操作系统,反之亦然。这会导致 BIOS 在重新启动时仅发现其他操作系统的启动管理器并引导到该操作系统,就好像它是系统中唯一的驱动器一样。要返回原始操作系统,您需要在另一个操作系统上使用第二个脚本来反向执行相同的操作。

在 Linux 上,您可以直接在 /boot/efi 修改当前 EFI 分区(如果您是 root 用户),如果您有第二个 EFI 分区,您可以像任何 fat32 分区一样手动挂载它。

在 Windows 上,您可以使用 diskpart 脚本以编程方式挂载 EFI 分区。

下面的示例脚本使用共享的 EFI 分区,但它应该也可以与两个 EFI 分区一起工作,您只需挂载它们两个。

从 Windows 切换到 Ubuntu 的批处理脚本示例:

@echo off
echo Mounting EFI Partition...
diskpart /s C:\Users\permissionBRICK\source\script\diskpart_boot_linux.txt
echo Enabling Linux Bootmgr...

move M:\EFI\ubuntu\grubx64.bak M:\EFI\ubuntu\grubx64.efi
move M:\EFI\ubuntu\mmx64.bak M:\EFI\ubuntu\mmx64.efi
move M:\EFI\ubuntu\shimx64.bak M:\EFI\ubuntu\shimx64.efi
echo Disabling Windows Bootmgr...
move M:\EFI\Microsoft\Boot\memtest.efi M:\EFI\Microsoft\Boot\memtest.bak
move M:\EFI\Microsoft\Boot\bootmgfw.efi M:\EFI\Microsoft\Boot\bootmgfw.bak
move M:\EFI\Microsoft\Boot\bootmgr.efi M:\EFI\Microsoft\Boot\bootmgr.bak

echo Unmounting EFI Partition...
diskpart /s C:\Users\permissionBRICK\source\script\diskpart_boot_linux_done.txt
echo Done. Rebooting in 10 sec.
shutdown -r -t 1

示例 diskpart_boot_linux.txt:

sel disk 2
sel part 1
assign letter=M

示例 diskpart_boot_linux_done.txt:

sel disk 2
sel part 1
remove

通过在控制台中运行 diskpart 并使用来找出磁盘和分区编号列出磁盘列表部分 请注意,如果向系统添加更多驱动器,这些可能会发生变化。

从 Ubuntu 启动到 Windows 的示例 bash 脚本:

sudo mv /boot/efi/EFI/Microsoft/memtest.bak /boot/efi/EFI/Microsoft/memtest.efi
sudo mv /boot/efi/EFI/Microsoft/bootmgfw.bak /boot/efi/EFI/Microsoft/bootmgfw.efi
sudo mv /boot/efi/EFI/Microsoft/bootmgr.bak /boot/efi/EFI/Microsoft/bootmgr.efi
sudo mv /boot/efi/EFI/ubuntu/grubx64.EFI /boot/efi/EFI/ubuntu/grubx64.bak
sudo mv /boot/efi/EFI/ubuntu/mmx64.EFI /boot/efi/EFI/ubuntu/mmx64.bak
sudo mv /boot/efi/EFI/ubuntu/shimx64.EFI /boot/efi/EFI/ubuntu/shimx64.bak
sudo reboot

这种方法的缺点是,除非您使用脚本,否则您将只能启动到当前操作系统,除非您运行系统修复,甚至无法通过 BIOS 手动启动设备选择。此外,这仅在您的启动顺序中没有其他启动设备(如网络启动等)时才有效,因为某些 BIOS 会在您每次运行脚本时改变启动顺序,因为从技术上讲每次都是一个新的启动选项,因此要使其无缝运行,请确保两个启动驱动器是启动顺序中唯一可用的启动选项,因此当前选定的操作系统将始终是唯一可用的启动选项。

使用它的好处是,它甚至可以在启用 SecureBoot 和 BitLocker 的情况下工作,您甚至不需要暂停 BitLocker 来设置双启动,只要第一个启动设备是 Windows 启动管理器,TPM 就可以正常工作,如果您启动 Linux,Bitlocker 无论如何都不会处于活动状态。

相关内容