我正在使用PowerShell v.2.0中的INI文件
我想在具体部分中更改现有的 name = value 对。例如:我想为 [Sky] 部分设置 color = Blue ,而不更改 [Home] 部分内的相同名称,值对。
我有正则表达式来获得我想改变的完整部分('天空'):
[[ \t]*Sky[ \t]*\](?:\r?\n(?:[^[\r\n].*)?)*
我还有另一个有效的表达式,但前提是Color是该部分的名字,值对。
\[[ \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 解决方案非常完美:) 这里是Gist中的代码:ReplaceIniValue_Sample2.ps1
@Matt 解决方案是另一种方法:
以下是完整的代码:ReplaceIniValue_Sample1.ps1
我正在寻找基于正则表达式的解决方案,但 @mark z 提案运行良好,是Windows PowerShell API调用的一个很好的示例。以下是完整的代码,只需稍作调整即可:ReplaceIniValue_Sample3.ps1
答案 0 :(得分:3)
也许另一种解决方案根本就不使用正则表达式并调用Windows API函数WritePrivateProfileString
。这是为了编辑ini文件。但是从PowerShell执行PInvoke有点棘手,你必须使用Add-Type编译一些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
关于用法的一些注意事项。设置空值将删除该键。设置空键将删除整个部分。
注意,有一个相应的GetPrivateProfileString用于从ini文件中读取值。我已经为此编写了C#部分,我将把它留给读者来练习PowerShell函数。
答案 1 :(得分:2)
以下是我要做的事情:将整个文件作为单个字符串读取,然后更改您的RegEx匹配以使其成为多行匹配,然后对[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
修改:几乎忘记了强制性的RegEx101链接来解释我的RegEx:https://regex101.com/r/uC0cC3/1
Edit2 :为了兼容v2,将-Raw
更改为-ReadCount 0
。
编辑3:好的,我显然错误地记得。我认为-ReadCount 0
与-Raw
的工作方式相同,但事实并非如此。更新了代码以解决此问题。现在它将字符串数组与新行字符连接,基本上将其转换为here-string,现在-replace
按预期工作。
答案 2 :(得分:1)
好的,我们来看看吧。
@"
#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
使用Get-Content
读取ini文件,然后将-join
读入一个大字符串。我们使用-split
将文件分解为[parts]
。我们循环每个部分寻找包含[sky]
的部分。当我们发现该部分正在寻找replace
之后带有蓝色的“颜色”字样。
这种方法有一些警告,但如果文件足够简单,它应该可以工作。具有[word]格式的代码中的值显然会导致解析不按预期工作,但结果应该仍然相同。
测试中的间距问题
直到JuanAntonioTubí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"
答案 3 :(得分:0)
(Get-Content $path -raw) -join "`n" -replace "(?mi)(?<=\[$Section\])([^\[]*$Name=)(.*?)(\r?)$","`$1$Value`$3" | Set-Content $path -NoNewline
TheMadTechnician的回答稍有改进,可以保留回车符(如果存在)(并且不添加其他换行符)。