Search code examples
powershellpowershell-2.0powershell-3.0

Powershell : Update properties file with key = value


My requirement is, I have a properties file say C:\google\configuration\backup\configuration.properties with content shown below

backup.path = C:\\ProgramData\\google\\backup
backup.volume.guid = \\\\?\\Volume{49e5d325-8065-49f4-bf0d-r4be94cc1feb}\\
backup.max.count = 10

I have a method that takes key and value as input.

function Script:change_or_replace_value([string]$key, [string]$value) {

    $origional_file_content = Get-Content $CONF_FILE_LOCATION
    $key_value_map = ConvertFrom-StringData($origional_file_content -join [Environment]::NewLine)
    $old_value = $key_value_map.$key
    $Old_file_pattern = "$key = $old_value"
    $new_file_pattern = "$key = $value"

    $origional_file_content | ForEach-Object {$_ -Replace $Old_file_pattern, $new_file_pattern} | Set-Content $NEW_FILE_LOCATION

}
  1. If key is "backup.volume.guid" and value is "\\?\Volume{111111-222-222-444-r4be94cc1feb}\" method should replace the text
backup.path = C:\\ProgramData\\google\\backup
backup.volume.guid = \\\\?\\Volume{111111-222-222-444-r4be94cc1feb}\\
backup.max.count = 10
  1. If key is "backup.volume.guid" and value is "" method should remove the line
backup.path = C:\\ProgramData\\google\\backup
backup.max.count = 10

If the value is empty delete the line else replace the text for the given key.

  • It contains special character like \ or other characters
  • How to delete the content if the key exists and value is an empty string

Solution

  • Your current approach has two problems, based on your attempt to update the properties by string manipulation via the file content as a single string:

    • In the ForEach-Object script block you'd need a different command to eliminate a line, because the -replace operator always returns something: if the regex pattern doesn't match the input, the input string is passed through.

    • You're missing an additional string-replacement step: ConvertFrom-StringData considers \ an escape character, so any pair of \\ in the input file turns into a single \ in the resulting hashtable. Therefore, you'll also have to double the \\ in $oldvalue and $value in order for the string replacement on the original file content to work.

    • Also, -replace, because it expects regex (regular expression) as the search operand, requires metacharachters such as \ to be escaped by \-escaping them; you could do that with [regex]::Escape($Old_file_pattern).


    I suggest a different approach that avoids these problems, namely:

    • Directly modify the hashtable that ConvertFrom-StringData returns.

    • Then serialize the updated hashtable to the output file, using string formatting.

      • As part of the string formatting, ouble the \ in the values again by using the [string] type's .Replace() method, which operates on literal strings and is simpler (and faster) in this case; however, you could also use the somewhat counter-intuitive -replace '\\', '\\'
    # Assign your real path here.
    $OCUM_CONF_FILE_LOCATION = 'in.properties'
    
    # Only for demonstration here: create a sample input file.
    @'
    backup.path = C:\\ProgramData\\google\\backup
    backup.volume.guid = \\\\?\\Volume{49e5d325-8065-49f4-bf0d-r4be94cc1feb}\\
    backup.max.count = 10
    '@ > $OCUM_CONF_FILE_LOCATION
    
    # Function which either updates, adds, or removes an entry.
    # NOTE: 
    #   * This function updates input file $OCUM_CONF_FILE_LOCATION *in place*.
    #     To be safe, be sure to have a backup copy before you try this.
    #   * Set-Content's default character encoding is used to save the updated file.
    #     Use the -Encoding parameter as needed.
    function Update-PropertiesFile ([string]$key, [string]$value) {
      $ht = ConvertFrom-StringData (Get-Content -Raw $OCUM_CONF_FILE_LOCATION)
      if ($ht.Contains($key)) { # update or delete existing entry
        if ('' -eq $value) { $ht.Remove($key) }
        else               { $ht[$key] = $value }
      } elseif ('' -eq $value) { # entry to remove not found
        Write-Warning "No entry with key '$key' found; nothing to remove."
        return
      } else { # new entry 
        $ht[$key] = $value
      }
      # Serialize the updated hashtable back to the input file.
      Set-Content $OCUM_CONF_FILE_LOCATION -Value $( 
        foreach ($key in $ht.Keys) {
         '{0} = {1}' -f $key, $ht[$key].Replace('\', '\\')
        }
      )
    }