在配置文件 powershell 或批处理文件中查找和替换
Find and replace in config file powershell or batch file
假设我有一个如下所示的配置文件:
{name1}
Settinga=1
settingb=2
settingc=3
{name2}
Settinga=1
settingb=2
settingc=3
{name3}
Settinga=1
settingb=2
settingc=3
我希望能够将 {name3} 下的行 settingb=2 更改为另一个值,例如 settingb=4
它将是 Windows OS 上的文件存储,因此理想情况下它会在 PowerShell 或批处理命令下完成。
任何人有任何想法或者这是否可能?
谢谢
您可以使用 Get-Content
, and store the section content under each name in a nested hash table, where the name lines are outer keys, and the settings lines are split into keys and values of an inner hash table. To maintain order of keys found from the original file, we can make use of System.Collections.Specialized.OrderedDictionary
. To create one, simply add the [ordered]
attribute to a hashtable @{}
. You can find out more at about_Hash_Tables
读取您的配置文件。
我们也可以使用System.String.Split
将行分割成=
,这样会根据长度来判断该行是名称还是设置。长度为 1 是名称,长度为 2 是设置。
# Read lines from config file
$config = Get-Content -Path .\config.txt
# Use an ordered hashtable to remember order of keys inserted
$sections = [ordered]@{}
# Keep a key which indicates the current name being added
$currentKey = $null
# Go through each line in the config file
foreach ($line in $config) {
# Split each line by '='
$items = $line.Split("=")
# If splitted items is only one value, we found a new name
# Set the new name and create an inner settings dictionary
if ($items.Length -eq 1) {
$currentKey = $line
$sections[$currentKey] = [ordered]@{}
}
# Otherwise we found a normal line
else {
# Only add the setting if the current name is not null
if ($null -ne $currentKey) {
$sections[$currentKey][$items[0]] = $items[1]
}
}
}
这将给出如下所示的散列 table $sections
:
Name Value
---- -----
{name1} {Settinga, settingb, settingc}
{name2} {Settinga, settingb, settingc}
{name3} {Settinga, settingb, settingc}
然后你可以像这样设置一个值(或多个值):
$sections["{name3}"].settingb = 4
并使用 Out-File
. To iterate the outer and inner hash tables, we need to iterate their key value pairs with System.Collections.Hashtable.GetEnumerator
.
将更新后的散列 table 写入输出文件
& {
# Output each outer key first, where the names are stored
foreach ($outerKvp in $sections.GetEnumerator()) {
$outerKvp.Key
# Then output each setting and value
foreach ($innerKvp in $outerKvp.Value.GetEnumerator()) {
"$($innerKvp.Key)=$($innerKvp.Value)"
}
}
# Pipe output from script block to output file
} | Out-File -FilePath .\output.txt
上面将 foreach
循环包装在 Call Operator &
to run the script block and pipe the output to Out-File
. You can have a a look at about_Pipelines
and about_Script_Blocks
中以获取更多信息。
既然我提到了管道和脚本块,我们也可以使用 Foreach-Object
to pass input down the pipeline. From some initial testing it seems this is slightly slower than the above solution(will need to investigate further with larger inputs). You can have a look at this Runtime of Foreach-Object vs Foreach loop 问题来了解这两种方法之间的区别。
$sections.GetEnumerator() | ForEach-Object {
$_.Key
$_.Value.GetEnumerator() | ForEach-Object {
"$($_.Key)=$($_.Value)"
}
} | Out-File -FilePath .\output.txt
最后是下面新建的输出文件。
output.txt
{name1}
Settinga=1
settingb=2
settingc=3
{name2}
Settinga=1
settingb=2
settingc=3
{name3}
Settinga=1
settingb=4
settingc=3
{name3}
的 settingb
已从 2
更新为 4
。
假设我有一个如下所示的配置文件:
{name1}
Settinga=1
settingb=2
settingc=3
{name2}
Settinga=1
settingb=2
settingc=3
{name3}
Settinga=1
settingb=2
settingc=3
我希望能够将 {name3} 下的行 settingb=2 更改为另一个值,例如 settingb=4
它将是 Windows OS 上的文件存储,因此理想情况下它会在 PowerShell 或批处理命令下完成。
任何人有任何想法或者这是否可能?
谢谢
您可以使用 Get-Content
, and store the section content under each name in a nested hash table, where the name lines are outer keys, and the settings lines are split into keys and values of an inner hash table. To maintain order of keys found from the original file, we can make use of System.Collections.Specialized.OrderedDictionary
. To create one, simply add the [ordered]
attribute to a hashtable @{}
. You can find out more at about_Hash_Tables
读取您的配置文件。
我们也可以使用System.String.Split
将行分割成=
,这样会根据长度来判断该行是名称还是设置。长度为 1 是名称,长度为 2 是设置。
# Read lines from config file
$config = Get-Content -Path .\config.txt
# Use an ordered hashtable to remember order of keys inserted
$sections = [ordered]@{}
# Keep a key which indicates the current name being added
$currentKey = $null
# Go through each line in the config file
foreach ($line in $config) {
# Split each line by '='
$items = $line.Split("=")
# If splitted items is only one value, we found a new name
# Set the new name and create an inner settings dictionary
if ($items.Length -eq 1) {
$currentKey = $line
$sections[$currentKey] = [ordered]@{}
}
# Otherwise we found a normal line
else {
# Only add the setting if the current name is not null
if ($null -ne $currentKey) {
$sections[$currentKey][$items[0]] = $items[1]
}
}
}
这将给出如下所示的散列 table $sections
:
Name Value
---- -----
{name1} {Settinga, settingb, settingc}
{name2} {Settinga, settingb, settingc}
{name3} {Settinga, settingb, settingc}
然后你可以像这样设置一个值(或多个值):
$sections["{name3}"].settingb = 4
并使用 Out-File
. To iterate the outer and inner hash tables, we need to iterate their key value pairs with System.Collections.Hashtable.GetEnumerator
.
& {
# Output each outer key first, where the names are stored
foreach ($outerKvp in $sections.GetEnumerator()) {
$outerKvp.Key
# Then output each setting and value
foreach ($innerKvp in $outerKvp.Value.GetEnumerator()) {
"$($innerKvp.Key)=$($innerKvp.Value)"
}
}
# Pipe output from script block to output file
} | Out-File -FilePath .\output.txt
上面将 foreach
循环包装在 Call Operator &
to run the script block and pipe the output to Out-File
. You can have a a look at about_Pipelines
and about_Script_Blocks
中以获取更多信息。
既然我提到了管道和脚本块,我们也可以使用 Foreach-Object
to pass input down the pipeline. From some initial testing it seems this is slightly slower than the above solution(will need to investigate further with larger inputs). You can have a look at this Runtime of Foreach-Object vs Foreach loop 问题来了解这两种方法之间的区别。
$sections.GetEnumerator() | ForEach-Object {
$_.Key
$_.Value.GetEnumerator() | ForEach-Object {
"$($_.Key)=$($_.Value)"
}
} | Out-File -FilePath .\output.txt
最后是下面新建的输出文件。
output.txt
{name1}
Settinga=1
settingb=2
settingc=3
{name2}
Settinga=1
settingb=2
settingc=3
{name3}
Settinga=1
settingb=4
settingc=3
{name3}
的 settingb
已从 2
更新为 4
。