6

I'm working on a powershell script that modifies config files. I have files like this:

##################################################### # comment about logentrytimeout ##################################################### Logentrytimeout= 1800 

who should look like this:

##################################################### # comment about logentrytimeout ##################################################### Logentrytimeout= 180 disablepostprocessing = 1 segmentstarttimeout = 180 

If there is a key set(Logentrytimeout), just update it to the given value. Ignore comments, where the key is mentioned(lines that start with #). The Key is case insensitive.

If the key is not set(disablepostprocessing and segmentstarttimeout), append key and value to the file. My function so far goes like this:

function setConfig( $file, $key, $value ) { (Get-Content $file) | Foreach-Object {$_ -replace "^"+$key+".=.+$", $key + " = " + $value } | Set-Content $file } setConfig divider.conf "Logentrytimeout" "180" setConfig divider.conf "disablepostprocessing" "1" setConfig divider.conf "segmentstarttimeout" "180" 
  • What is the correct regex?
  • How do I check if there was a replacement?
  • If there was no replacement: How can I append $key+" = "+$value to the file then?

6 Answers 6

14

Assuming the $key you want to replace is always at the beginning of a line, and that it contains no special regex characters

function setConfig( $file, $key, $value ) { $content = Get-Content $file if ( $content -match "^$key\s*=" ) { $content -replace "^$key\s*=.*", "$key = $value" | Set-Content $file } else { Add-Content $file "$key = $value" } } setConfig "divider.conf" "Logentrytimeout" "180" 

If there is no replacement $key = $value will be appended to the file.

Sign up to request clarification or add additional context in comments.

Comments

3

Updated version of the functions above with some parametrisation and verbose output if required.

 Function Set-FileConfigurationValue() { [CmdletBinding(PositionalBinding=$false)] param( [Parameter(Mandatory)][string][ValidateScript({Test-Path $_})] $Path, [Parameter(Mandatory)][string][ValidateNotNullOrEmpty()] $Key, [Parameter(Mandatory)][string][ValidateNotNullOrEmpty()] $Value, [Switch] $ReplaceExistingValue, [Switch] $ReplaceOnly ) $content = Get-Content -Path $Path $regreplace = $("(?<=$Key).*?=.*") $regValue = $("=" + $Value) if (([regex]::Match((Get-Content $Path),$regreplace)).success) { If ($ReplaceExistingValue) { Write-Verbose "Replacing configuration Key ""$Key"" in configuration file ""$Path"" with Value ""$Value""" (Get-Content -Path $Path) | Foreach-Object { [regex]::Replace($_,$regreplace,$regvalue) } | Set-Content $Path } else { Write-Warning "Key ""$Key"" found in configuration file ""$Path"". To replace this Value specify parameter ""ReplaceExistingValue""" } } elseif (-not $ReplaceOnly) { Write-Verbose "Adding configuration Key ""$Key"" to configuration file ""$Path"" using Value ""$Value""" Add-Content -Path $Path -Value $("`n" + $Key + "=" + $Value) } else { Write-Warning "Key ""$Key"" not found in configuration file ""$Path"" and parameter ""ReplaceOnly"" has been specified therefore no work done" } } 

1 Comment

Question, if a key/value exists but happens to be commented out, how do I either uncomment it, or throw an exception? i am using this standalone, but how do i use it as an actual function with the parameters as strings, so i can use ..("./foo.cfg", "foo", "bar, baz")
2

I'd do this:

function setConfig( $file, $key, $value ) { $regex = '^' + [regex]::escape($key) + '\s*=.+' $replace = "$key = $value" $old = get-content $file $new = $old -replace $regex,$replace if (compare-object $old $new) { Write-Host (compare-object $old $new | ft -auto | out-string) -ForegroundColor Yellow $new | set-content $file } else { $replace | add-content $file Write-Host "$replace added to $file" -ForegroundColor Cyan } } 

Edit: added a replacement bell, and a not match whistle.

Comments

1

Change the function to this:

function Set-Config( $file, $key, $value ) { $regreplace = $("(?<=$key).*?=.*") $regvalue = $(" = " + $value) if (([regex]::Match((Get-Content $file),$regreplace)).success) { (Get-Content $file) ` |Foreach-Object { [regex]::Replace($_,$regreplace,$regvalue) } | Set-Content $file } else { Add-Content -Path $file -Value $("`n" + $key + " = " + $value) } } 

Then when you call the function, use this format:

Set-Config -file "divider.conf" -key "Logentrytimeout" -value "180" 

Edit: I forgot your requirement of adding the line if it doesn't exist. This will check for the $key, if it exists it will set its value to $value. If it doesn't exist it will add $key = $value to the end of the file. I also renamed the function to be more consistent with power shell naming conventions.

Comments

0

@CarlR Function it's for PowerShell Version 3. This it's the same adapted to PowerShell Version 2.

EDIT: Changed regular expression to fix two bugs on Set-FileConfigurationValue:

  1. If you have one line like this:

    ; This is a Black line

    And you try to do:

    Set-FileConfigurationValue $configFile "Black" 20 -ReplaceExistingValue

    You get one message about "Replacing" but nothing happens.

  2. If you have two lines like these:

    filesTmp=50
    Tmp=50

    And you try to do:

    Set-FileConfigurationValue $configFile "Tmp" 20 -ReplaceExistingValue

    You get the two lines changed!

    filesTmp=20 Tmp=20

This is the final version:

Function Set-FileConfigurationValue() { [CmdletBinding()] param( [Parameter(Mandatory=$True)] [ValidateScript({Test-Path $_})] [string] $Path, [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [string] $Key, [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [string]$Value, [Switch] $ReplaceExistingValue, [Switch] $ReplaceOnly ) $regmatch= $("^($Key\s*=\s*)(.*)") $regreplace=$('${1}'+$Value) if ((Get-Content $Path) -match $regmatch) { If ($ReplaceExistingValue) { Write-Verbose "Replacing configuration Key ""$Key"" in configuration file ""$Path"" with Value ""$Value""" (Get-Content -Path $Path) | ForEach-Object { $_ -replace $regmatch,$regreplace } | Set-Content $Path } else { Write-Warning "Key ""$Key"" found in configuration file ""$Path"". To replace this Value specify parameter ""ReplaceExistingValue""" } } elseif (-not $ReplaceOnly) { Write-Verbose "Adding configuration Key ""$Key"" to configuration file ""$Path"" using Value ""$Value""" Add-Content -Path $Path -Value $("`n" + $Key + "=" + $Value) } else { Write-Warning "Key ""$Key"" not found in configuration file ""$Path"" and parameter ""ReplaceOnly"" has been specified therefore no work done" } } 

I've also added a function to read from the config file

Function Get-FileConfigurationValue() { [CmdletBinding()] param( [Parameter(Mandatory=$True)] [ValidateScript({Test-Path $_})] [string] $Path, [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [string] $Key, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [string]$Default="" ) # Don't have spaces before key. # To allow spaces, use "$Key\s*=\s*(.*)" $regKey = $("^$Key\s*=\s*(.*)") # Get only last time $Value = Get-Content -Path $Path | Where {$_ -match $regKey} | Select-Object -last 1 | ForEach-Object { $matches[1] } if(!$Value) { $Value=$Default } Return $Value } 

Comments

0
function sed($filespec, $search, $replace) { foreach ($file in gci -Recurse $filespec | ? { Select-String $search $_ -Quiet } ) { (gc $file) | ForEach-Object {$_ -replace $search, $replace } | Set-Content $file } } 

Usage:

sed ".\*.config" "intranet-" "intranetsvcs-" 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.