Search code examples
powershellreplacesplitline

How does one update a file by line number in powershell?


all,

I am trying to update a line in config file with powershell and I can't seem get it working properly...

I have this file: config.ini

version.clients.latest.location=.\
version.clients.latest.classpath=My.jar
version.clients.latest.mainclass=gui.Launch

version.clients.latest.jreLocation=C:\home\user\agent\install_master\Deploy\Admin\..\1AOpenJRE\jre1.8.0_342\jre8
version.clients.latest.jre.options=-Xmx256m -Dcrow.library.path=.\crow\crow-native -Dcrow.runtime.path=.\crow\cre\ -Denv.global.clp.enable=false
#version.clients.latest.xre.isUsed=true
#version.clients.latest.xre.version=2.2
#version.clients.latest.xre.location=L:\Cross-Projects\2.2\2.5\xre
version.client.latest.xre.isUsed=true
version.client.latest.xre.version=2.2
version.client.latest.xre.location=./xre
version.client.latest.xulWrapper.dllFile=./xre/XULWrapper.dll
version.client.latest.xre.cache.isEnabled=true
version.client.latest.xre.cache.isShared=true
version.client.latest.jreOptions=-Dsun.java2d.ddoffscreen=false

and I want to update this line

version.clients.latest.jreLocation=C:\home\user\agent\install_master\Deploy\Admin\..\1AOpenJRE\jre1.8.0_342\jre8

with a new path... Like so...

version.clients.latest.jreLocation=..\..\..\1AOpenJRE\jre1.8.0_342\jre8

and I made this script

# Config
$CfgFile = ".\cfg\cfg.ini"
$SearchString = "version.clients.latest.jreLocation="

$ConfigContent = Get-Content "$CfgFile"
$ConfigSelectLine =  Get-Content -Path "$CfgFile" | Select-String $SearchString
$LineNum = $ConfigSelectLine.LineNumber

$key = $ConfigSelectLine -Split("=")[0]
$filePath = $ConfigSelectLine -Split("..")[1]

$ImportantPath = $filePath -Split("..")[1]
$NewLine = 'version.clients.latest.jreLocation=..\..\..' + $ImportantPath

$ConfigContent[$LineNum] = "$NewLine" | Set-Content ".\cfg\cfg-test.ini"

and when I run it, I a output file with one line ...

version.clients.latest.jreLocation=..\..\..\      

How do we split a string on ".."? and why is the entire file wiped out when I try to update the single line in the file by line number?

Cheers,

X


Solution

  • The following line doesn't do what you intend:

    $ConfigContent[$LineNum] = "$NewLine" | Set-Content ".\cfg\cfg-test.ini"

    This saves the value of $NewLine only to file .\cfg\cfg-test.ini, and then stores the output from the Set-Content call in the element with index $LineNum of array $ConfigContent (and, unless you use -PassThru, `Set-Content produces no output).

    While ($ConfigContent[$LineNum] = "$NewLine") | Set-Content ".\cfg\cfg-test.ini" would - thanks to (...) - first update the array element and then pass the updated value through, you'd still only save that element to the file.

    The problem with the splitting by .. is simply due to calling .Split('..')[1] twice.
    Replace:

    $filePath = $ConfigSelectLine -Split("..")[1]
    
    $ImportantPath = $filePath -Split("..")[1]
    

    with:

    $ImportantPath = $ConfigSelectLine -Split("..")[1]
    

    Also, you should add the -SimpleMatch switch to your Select-String call to ensure that your search term is used literally.


    The immediate fix for the single-line-only result is to first update the array element and then send the updated array as a whole to the output file:

    $ConfigContent[$LineNum] = $NewLine
    Set-Content ".\cfg\cfg-test.ini" -Value $ConfigContent
    

    Note the use of the -Value parameter rather than the pipeline, which speeds up the operation.


    However, your code can be greatly streamlined, using a switch statement:

    $CfgFile = ".\cfg\cfg.ini"
    $SearchString = "version.clients.latest.jreLocation="
    
    $(
      switch -WildCard -File $CfgFile {
        "$SearchString*" {
           $prop, $value = $_ -split '=', 2
           "$prop=" + '..\..\..' + $value.Split('..')[1]
         }
        Default { $_ } # pass through
      }
    ) | Set-Content .\cfg\cfg-test.ini