我正在创建一个分析启动配置数据(BCD)中条目的应用程序。
我已经尝试使用PowerShell,但是似乎它没有提供任何cmdlet来处理它。因此,我回到了.NET,尤其是C#。
我想让某些东西获得像这样的BCD条目
var entries = bcd.GetEntries();
条目为IList<BcdEntry>
class BcdEntry
{
public string Name {get; set; }
IDictionary<string, IList<string>> Properties { get; set; }
}
问题是我不知道如何获取条目。调用BCDEdit是可能的,但是它需要解析命令的输出,这是一个繁琐的任务。
希望您能为我的问题找到解决方案。
答案 0 :(得分:3)
一种PSv4 +解决方案,可将bcdedit.exe /enum
输出解析为自定义对象列表:
# IMPORTANT: bcdedit /enum requires an ELEVATED session.
$bcdOutput = (bcdedit /enum) -join "`n" # collect bcdedit's output as a *single* string
# Initialize the output list.
$entries = New-Object System.Collections.Generic.List[pscustomobject]]
# Parse bcdedit's output.
($bcdOutput -split '(?m)^(.+\n-)-+\n' -ne '').ForEach({
if ($_.EndsWith("`n-")) { # entry header
$entries.Add([pscustomobject] @{ Name = ($_ -split '\n')[0]; Properties = [ordered] @{} })
} else { # block of property-value lines
($_ -split '\n' -ne '').ForEach({
$propAndVal = $_ -split '\s+', 2 # split line into property name and value
if ($propAndVal[0] -ne '') { # [start of] new property; initialize list of values
$currProp = $propAndVal[0]
$entries[-1].Properties[$currProp] = New-Object Collections.Generic.List[string]
}
$entries[-1].Properties[$currProp].Add($propAndVal[1]) # add the value
})
}
})
# Output a quick visualization of the resulting list via Format-Custom
$entries | Format-Custom
注意:
正如LotPing所言,
bcdedit.exe
的输出是部分本地化的;具体来说,以下各项:
Windows Boot Manager
在西班牙语中是Administrador de arranque de Windows
)identifier
的属性名称(例如西班牙语中的Identificador
)。为简洁起见,该代码未尝试将本地化名称映射到其美国英语名称,但可以这样做。
此外,示例bcdedit
输出和this ServerFault question(重复)表明,可能存在很长的属性名称,以至于它们碰到了它们的值,而没有空格和空白截断。
如果这不仅仅是发布的产物,那么将需要更多的工作来处理这种情况; this article包含属性名称列表。
[pscustomobject]
实例而不是自定义BcdEntry
类的实例;在PSv5 +中,您可以直接在PowerShell中创建这样的自定义类。
所有属性值均捕获为 string 值,并收集在[List[string]]
列表中(即使只有1个值);需要进行额外的工作才能将它们解释为特定类型;
例如,[int] $entries[1].Properties['allowedinmemorysettings'][0]
将字符串'0x15000075'
转换为整数。
示例输入/输出:
给出bcdedit.exe /enum
这样的输出...
Windows Boot Manager
--------------------
identifier {bootmgr}
device partition=C:
displayorder {current}
{e37fc869-68b0-11e8-b4cf-806e6f6e6963}
description Windows Boot Manager
locale en-US
inherit {globalsettings}
default {current}
resumeobject {9f3d8468-592f-11e8-a07d-e91e7e2fad8b}
toolsdisplayorder {memdiag}
timeout 0
Windows Boot Loader
-------------------
identifier {current}
device partition=C:
path \WINDOWS\system32\winload.exe
description Windows 10
locale en-US
inherit {bootloadersettings}
recoverysequence {53f531de-590e-11e8-b758-8854872f7fe5}
displaymessageoverride Recovery
recoveryenabled Yes
allowedinmemorysettings 0x15000075
osdevice partition=C:
systemroot \WINDOWS
resumeobject {9f3d8468-592f-11e8-a07d-e91e7e2fad8b}
nx OptIn
bootmenupolicy Standard
...上面的命令产生了这个结果:
class PSCustomObject
{
Name = Windows Boot Manager
Properties =
[
class DictionaryEntry
{
Key = identifier
Value =
[
{bootmgr}
]
Name = identifier
}
class DictionaryEntry
{
Key = device
Value =
[
partition=C:
]
Name = device
}
class DictionaryEntry
{
Key = displayorder
Value =
[
{current}
{e37fc869-68b0-11e8-b4cf-806e6f6e6963}
]
Name = displayorder
}
class DictionaryEntry
{
Key = description
Value =
[
Windows Boot Manager
]
Name = description
}
...
]
}
class PSCustomObject
{
Name = Windows Boot Loader
Properties =
[
class DictionaryEntry
{
Key = identifier
Value =
[
{current}
]
Name = identifier
}
class DictionaryEntry
{
Key = device
Value =
[
partition=C:
]
Name = device
}
class DictionaryEntry
{
Key = path
Value =
[
\WINDOWS\system32\winload.exe
]
Name = path
}
class DictionaryEntry
{
Key = description
Value =
[
Windows 10
]
Name = description
}
...
]
}
要以编程方式处理条目:
foreach($entry in $entries) {
# Get the name.
$name = $entry.Name
# Get a specific property's value.
$prop = 'device'
$val = $entry.Properties[$prop] # $val is a *list*; e.g., use $val[0] to get the 1st item
}
注意:$entries | ForEach-Object { <# work with entry $_ #> }
,即也可以使用管道,但是如果条目列表已经在内存中,则foreach
循环会更快。
答案 1 :(得分:2)
我对@ mklement0脚本进行了一些更改,以至于无法添加注释。
我想知道为什么为什么只有4个词典条目
输出直到找到$FormatEnumerationLimit
的默认值
是4
为避免输出中出现换行符,脚本使用Out-String -Width 4096
## Q:\Test\2018\06\20\SO_50946956.ps1
# IMPORTANT: bcdedit /enu, requires an ELEVATED session.
#requires -RunAsAdministrator
## the following line imports the file posted by SupenJMN for testing
$bcdOutput = (gc ".\BCDEdit_ES.txt") -join "`n" -replace '\}\n\s+\{','},{'
## for a live "bcdedit /enum all" uncomment the following line
# $bcdOutput = (bcdedit /enum all) -join "`n" -replace '\}\n\s+\{','},{'
# Create the output list.
$entries = New-Object System.Collections.Generic.List[pscustomobject]]
# Parse bcdedit's output into entry blocks and construct a hashtable of
# property-value pairs for each.
($bcdOutput -split '(?m)^([a-z].+)\n-{10,100}\n').ForEach({
if ($_ -notmatch ' +') {
$entries.Add([pscustomobject] @{ Name = $_; Properties = [ordered] @{} })
} else {
($_ -split '\n' -ne '').ForEach({
$keyValue = $_ -split '\s+', 2
$entries[-1].Properties[$keyValue[0]] = $keyValue[1]
})
}
})
# Output a quick visualization of the resulting list via Format-Custom
$FormatEnumerationLimit = 20
$entries | Format-Custom | Out-String -Width 4096 | Set-Content BCDEdit_ES_Prop.txt
缩短了脚本的示例输出(约700行)
class PSCustomObject
{
Name =
Properties =
[
]
}
class PSCustomObject
{
Name = Administrador de arranque de firmware
Properties =
[
class DictionaryEntry
{
Key = Identificador
Value = {fwbootmgr}
Name = Identificador
}
class DictionaryEntry
{
Key = displayorder
Value = {bootmgr},{e37fc869-68b0-11e8-b4cf-806e6f6e6963},{05d4f193-712c-11e8-b4ea-806e6f6e6963},{05d4f194-712c-11e8-b4ea-806e6f6e6963},{cb6d5609-712f-11e8-b4eb-806e6f6e6963},{cb6d560a-712f-11e8-b4eb-806e6f6e6963},{cb6d560b-712f-11e8-b4eb-806e6f6e6963}
Name = displayorder
}
class DictionaryEntry
{
Key = timeout
Value = 1
Name = timeout
}
]
}
答案 2 :(得分:0)
我的方法看起来像这样:
(bcdedit /enum | Out-String) -split '(?<=\r\n)\r\n' | ForEach-Object {
$name, $data = $_ -split '\r\n---+\r\n'
$props = [ordered]@{
'name' = $name.Trim()
}
$data | Select-String '(?m)^(\S+)\s\s+(.*)' -AllMatches |
Select-Object -Expand Matches |
ForEach-Object { $props[$_.Groups[1].Value] = $_.Groups[2].Value.Trim() }
[PSCustomObject]$props
}
上面的代码基本上从与其他答案一样将bcdedit
输出合并为单个字符串开始,然后将该字符串拆分为启动配置数据块。然后,再次拆分这些块中的每个块,以将标题与实际数据分开。将标题作为boot config部分的名称添加到哈希表中,然后使用正则表达式解析键/值对的数据块。这些将附加到哈希表中,该哈希表最终将转换为自定义对象。
由于使用ordered
和PSCustomObject
类型的加速器,因此代码至少需要PowerShell v3。
当然,您可以对上述基本示例代码进行各种优化。例如,不同的引导配置节可能具有不同的属性。引导管理器部分具有toolsdisplayorder
和timeout
之类的属性,它们在引导加载器部分中不存在,而引导加载器部分具有诸如osdevice
和systemroot
之类的属性,它们不存在在启动管理器部分中。如果您希望所有生成的对象具有一致的属性集,则可以通过Select-Object
将它们通过管道传递给您,并列出您希望对象具有的属性,例如:
... | Select-Object 'name', 'identifier', 'default', 'osdevice' 'systemroot'
列表中不存在的属性将从对象中删除,而对象中不存在的属性将添加一个空值。
此外,除了将所有值创建为字符串外,您还可以将它们转换为更合适的类型,或者仅修改值,例如从字符串中删除大括号。
... | ForEach-Object {
$key = $_.Groups[1].Value
$val = $_.Groups[2].Value.Trim()
$val = $val -replace '^\{(.*)\}$', '$1'
if ($val -match '^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$') {
$val = [guid]$val
} elseif ($val -eq 'yes' -or $val -eq 'true') {
$val = $true
} elseif ($val -eq 'no' -or $val -eq 'false') {
$val = $false
} elseif ($key -eq 'locale') {
$val = [Globalization.CultureInfo]$val
}
$props[$key] = $val
}