在配置文件 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