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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.