Powershell和Regex:如何替换命名节中的INI名称、值对?
我正在PowerShell v.2.0中使用INI文件 我想更改混凝土截面中现有的name=value对。举例:我想为[Sky]部分设置color=Blue,而不更改[Home]部分中的相同名称、值对 我有正则表达式来获取我想要更改的完整部分(“天空”): 我还有另一个表达式可以工作,但前提是颜色是节的名字和值对Powershell和Regex:如何替换命名节中的INI名称、值对?,regex,powershell,file-io,Regex,Powershell,File Io,我正在PowerShell v.2.0中使用INI文件 我想更改混凝土截面中现有的name=value对。举例:我想为[Sky]部分设置color=Blue,而不更改[Home]部分中的相同名称、值对 我有正则表达式来获取我想要更改的完整部分(“天空”): 我还有另一个表达式可以工作,但前提是颜色是节的名字和值对 \[[ \t]*Sky[ \t]*\][.\s]*Color[ \t]*=[ \t]*(.*) 这是示例ini文件: [Sky] Size=Big Color=white [Ho
\[[ \t]*Sky[ \t]*\][.\s]*Color[ \t]*=[ \t]*(.*)
这是示例ini文件:
[Sky]
Size=Big
Color=white
[Home]
Color=Black
PS:这是我现在用来替换ini文件中name=value的所有实例的代码,我想用新的表达式进行更新,只替换给定部分中的
$Name=Color
$Value=Blue
$regmatch= $("^($Name\s*=\s*)(.*)")
$regreplace=$('${1}'+$Value)
if ((Get-Content $Path) -match $regmatch)
{
(Get-Content -Path $Path) | ForEach-Object { $_ -replace $regmatch,$regreplace } | Set-Content $Path
}
编辑: @TheMadTechnician解决方案非常完美:) 以下是要点中的代码: @Matt解决方案是另一种方法:
以下是完整的代码:
我正在寻找一个基于正则表达式的解决方案,但是,@mark z建议非常有效,是Windows PowerShell API调用的一个很好的例子。下面是经过一些小调整的完整代码:好的,让我们来看看这个
@"
#This line have a white line after and before
[Sky]
Size=Big
Color=White
[Home]
Color=Black
#Last line
"@ | Set-content c:\temp\test.ini
只需使用该代码创建文件。现在让我们做一些改变
$edits = (Get-Content c:\temp\test.ini) -join "`r`n" -split '\s(?=\[.+?\])' | ForEach-Object{
If($_ -match '\[sky\]'){
$_ -replace '(color=)\w+', '$1Blue'
} Else {
$_
}
}
$edits | Set-Content c:\temp\test.ini
使用获取内容
读入ini文件,然后将其加入一个大字符串中。我们使用-split
将文件分解为[部分]
。我们循环每个部分,寻找包含[sky]
的部分。当我们找到该部分时,只需将后面的单词“color”替换为蓝色
这种方法有一些警告,但如果文件足够简单,它应该可以工作。如果代码中有[word]格式的值,显然会导致解析无法按预期工作,但结果应该仍然相同
测试中的间距问题
直到胡安·安东尼奥·图比奥(Juan Antonio Tubío)让我意识到随后的代码传递会产生额外的空白,我才意识到这一点。这就是我如何分解文件进行解析的原因。我通过在输出文件之前将数组合并成一个字符串来修复这个问题。为了使后续测试更容易执行,我还制作了一个函数
function Set-INIKey{
param(
[string]$path,
[string]$section,
[string]$key,
[string]$value
)
$edits = (Get-Content $path) -join "`r`n" -split '\s(?=\[.+?\])' | ForEach-Object{
If($_ -match "\[$section\]"){
$_ -replace "($key=)\w+", "`$1$value"
} Else {
$_
}
}
-join $edits | Set-Content $path
}
所以当我在几个测试中使用它时,多余的空白就不再存在了
Set-INIKey "c:\temp\test.ini" "Sky" "Color" "Blue"
我要做的是:将整个文件作为单个字符串读取,然后更改正则表达式匹配,使其成为多行匹配,然后对[Sky]执行反向查找,然后捕获除“[”之外的任何内容,直到您要更改的选项为止,在第二个捕获组中捕获等号另一侧的所有内容,并根据需要进行替换
$Path="C:\Temp\prueba.ini"
@"
[Sky]
Size=Big
Color=White
[Home]
Color=Black
"@ | Set-content $Path
$Section="Sky"
$Name="Color"
$Value="Blue"
(Get-Content $Path) -join "`n" -replace "(?m)(?<=\[$Section\])([^\[]*$Name=)(.+?)$","`$1$Value" | Set-Content $Path
$Path=“C:\Temp\prueba.ini”
@"
[天空]
大小=大
颜色=白色
[主页]
颜色=黑色
“@|设置内容$Path
$Section=“天空”
$Name=“Color”
$Value=“蓝色”
(获取内容$Path)-加入“`n”-替换”(?m)(?也许另一种解决方案根本不使用正则表达式,而是调用Windows API函数WritePrivateProfileString
。这是为了编辑ini文件而设计的。但是,从PowerShell执行PInvoke有点棘手,您必须编译一些带有Add类型的C#
$source = @"
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
public static class IniFile
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool WritePrivateProfileString(string lpAppName,
string lpKeyName, string lpString, string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
static extern uint GetPrivateProfileString(
string lpAppName,
string lpKeyName,
string lpDefault,
StringBuilder lpReturnedString,
uint nSize,
string lpFileName);
public static void WriteValue(string filePath, string section, string key, string value)
{
string fullPath = Path.GetFullPath(filePath);
bool result = WritePrivateProfileString(section, key, value, fullPath);
}
public static string GetValue(string filePath, string section, string key, string defaultValue)
{
string fullPath = Path.GetFullPath(filePath);
var sb = new StringBuilder(500);
GetPrivateProfileString(section, key, defaultValue, sb, (uint)sb.Capacity, fullPath);
return sb.ToString();
}
}
"@
Add-Type -TypeDefinition $source
function Set-IniValue {
param (
[string]$path,
[string]$section,
[string]$key,
[string]$value
)
$fullPath = [System.IO.Path]::GetFullPath($(Join-Path $pwd $path))
[IniFile]::WriteValue($fullPath, $section, $key, $value)
}
但是,一旦完成此操作,编辑ini键值对就很容易了:
Set-IniValue -Path "test.ini" -Section Sky -Key Color -Value Blue
有关用法的几点说明。设置null值将删除该键。设置null键将删除整个部分
\[[ \t]*Sky[ \t]*\][.\s]*Color[ \t]*=[ \t]*(.*)
注意,有一个对应的GetPrivateProfileString用于从ini文件中读取值。我已经为此编写了C部分,我将把它作为一个练习留给读者来实现它的PowerShell函数。(Get Content$path-raw)-join“`n”-replace”(?mi)(?谢谢@TheMadTechnician,我已经测试了你的和RegEx101的工作,但没有测试poweshell:(完整代码的要点:这是我的错,我错误地记得-ReadCount 0
是如何工作的,并且认为它像-Raw
一样工作。我已经对它进行了更新和测试。它现在可以通过添加一个-join”
-join“
n”来工作了。)`语句。还删除了-ReadCount 0
,因为它毫无意义。谢谢@Matt。你的代码可以工作,但在每个部分前都添加了一行新行,我不知道如何修复它。@juanantonitubío哦……好的,应该可以添加一个trim
。更新了答案中的正则表达式。再试一次。谢谢@Matt,现在是puttig wh每段末端的ite线;)如果你想测试他,我已经更新了我的gist代码。@Juanantonitubío这些空白存在于你的代码示例中。如果你想删除所有空白,这应该不难。而且你没有显示如何将我的代码导出到文件中。PowerShell在某些情况下会在文件末尾添加一行新行。添加了一些修整器。我可以将你的代码放入其中正如我用来测试:。在这里,我放置了ini.file的第一个版本,运行了3次后得到的结果:。正如您所看到的,每次读取和写入文件时都会放置白线。感谢您的修剪更新,它可以工作,但我不想更改目标文件上的线结构。谢谢@mike,我正在寻找基于正则表达式的解决方案,但您的工作正常这非常完美,是来自Windows PowerShell的API调用的一个很好的例子
(Get-Content $path -raw) -join "`n" -replace "(?mi)(?<=\[$Section\])([^\[]*$Name=)(.*?)(\r?)$","`$1$Value`$3" | Set-Content $path -NoNewline