编写目的
在easyinstaller项目中使用。
思路
以下是/etc/NetworkManager/NetworkManager.conf的内容:
# Configuration file for NetworkManager.
#
# See "man 5 NetworkManager.conf" for details.
#
# The directory /etc/NetworkManager/conf.d/ can contain additional configuration
# snippets. Those snippets override the settings from this main file.
#
# The files within conf.d/ directory are read in asciibetical order.
#
# If two files define the same key, the one that is read afterwards will overwrite
# the previous one.
[main]
plugins=ifcfg-rh
[logging]
#level=DEBUG
#domains=ALL
先定义几个交流用的名词:
- 配置文件,上面的示例文件
- 配置块 (比如[main])
- kv值 (plugins=ifcfg-rh)
想象一下,哪些行为可以涵盖所有可能的操作呢?(欢迎补充)
- 在某个配置块下,(增加 | 修改 | uncomment)一个kv值。
- 在某个配置块下,(删除 | comment out) 一个kv值。
构建powershell对象
创建一个Custom PsObject,具有2个属性:
- 配置文件路径,filePath
- 以块名称为键值的hashtable, blockHt
- 块前面的前缀,prefix
再增加2个脚本方法:
- setKv(k,v,section),包括增加(没有的话),修改(直接修改,或uncomment之后修改)
- commentKv(k, v, section),如果有section的话,仅仅修改section内的kv,没有的话修改全部kv。
- writeBack(fn),如果提供了fn,写入到fn中,否则写入到构建对象时的FilePath
最终代码
function New-SectionKvFile {
Param
(
[parameter(Mandatory=$True)]
[String]
$FilePath,
[parameter(Mandatory=$False)]
[String]
$SectionPattern = "^\[(.*)\]$",
[parameter(Mandatory=$False)]
[String]
$commentPattern = "^#.*"
)
$prefix = @()
$blockHt = [ordered]@{}
$blockStart = $False
$currentBlock = $null
Get-Content $FilePath | ForEach-Object {
if ($blockStart) {
if ($_ -match $SectionPattern) {
$currentBlock = $Matches[0]
$blockHt[$currentBlock] = @()
} else {
$blockHt[$currentBlock] += $_
}
} else {
if ($_ -match $SectionPattern) {
$blockStart = $True
$currentBlock = $Matches[0]
$blockHt[$currentBlock] = @()
} else {
$prefix += $_
}
}
}
$skf = New-Object -TypeName PSObject -Property @{FilePath=$FilePath;blockHt=$blockHt;prefix=$prefix}
$addKv = {
param([String]$k, [String]$v, [String]$section)
$done = $False
$blockLines = $this.blockHt[$section] | ForEach-Object {
if ($done) {
$_
} else {
if ($_ -match "$k=") {
$done = $True
"$k=$v"
} else {
$_
}
}
}
if (!$done) {
$blockLines += "$k=$v"
}
$this.blockHt[$section] = $blockLines
}
$skf = $skf | Add-Member -MemberType ScriptMethod -Name addKv -Value $addKv -PassThru
$commentKv = {
param([String]$k, [String]$section)
$done = $False
$blockLines = $this.blockHt[$section] | ForEach-Object {
if ($done) {
$_
} else {
if ($_ -match "^$k=") {
$done = $True
"#$_"
} else {
$_
}
}
}
if ($done) { # if changed.
$this.blockHt[$section] = $blockLines
}
}
$skf = $skf | Add-Member -MemberType ScriptMethod -Name commentKv -Value $commentKv -PassThru
$writeToFile = {
param([parameter(Position=0,Mandatory=$False)][String]$fileToWrite)
if (!$fileToWrite) {
$fileToWrite = $this.FilePath
}
$lines = $this.prefix
([HashTable]$this.blockHt).GetEnumerator().ForEach({
$lines += $_.Key
$lines += $_.Value
})
Set-Content -Path $fileToWrite -Value $lines
}
$skf = $skf | Add-Member -MemberType ScriptMethod -Name writeToFile -Value $writeToFile -PassThru
return $skf
}
单元测试代码:
It "should addKv commentKv work" {
$kvf = New-SectionKvFile -FilePath (Join-Path $here -ChildPath "fixtures\NetworkManager.conf")
$ht = [HashTable]$kvf.blockHt;
$kvf.blockHt.Count | Should Be 2
$kvf.blockHt["[main]"] -contains "plugins=ifcfg-rh" | Should Be $True
$kvf.addKv("a", 1, "[main]")
$kvf.blockHt["[main]"] -contains "a=1" | Should Be $True
$kvf.blockHt["[main]"].Count | Should Be 3
$kvf.addKv("a", 1, "[main]")
$kvf.blockHt["[main]"].Count | Should Be 3
$kvf.commentKv("x", "[main]")
$kvf.blockHt["[main]"].Count | Should Be 3
$kvf.commentKv("a", "[main]")
$kvf.blockHt["[main]"] -contains "a=1" | Should Be $False
$kvf.blockHt["[main]"] -contains "#a=1" | Should Be $True
$kvf.addKv("a", 2, "[main]")
$kvf.blockHt["[main]"] -contains "a=1" | Should Be $False
$kvf.blockHt["[main]"] -contains "#a=1" | Should Be $False
$kvf.blockHt["[main]"] -contains "a=2" | Should Be $True
$kvf.blockHt["[main]"].Count | Should Be 3
}
It "should write to file work" {
$kvf = New-SectionKvFile -FilePath (Join-Path $here -ChildPath "fixtures\NetworkManager.conf")
$ht = [HashTable]$kvf.blockHt;
$ht.Keys | Should Be @("[main]", "[logging]")
([Array]$kvf.prefix).Count| Should Be 12
$tmpf = (New-TemporaryFile).ToString()
$kvf.writeToFile($tmpf)
$line2 = Get-Content $tmpf | Select-Object -First 2
$line2 | Should Be @("# Configuration file for NetworkManager.", "#")
Remove-Item -Path $tmpf
}