0

I've been wanting an easy to use script that will allow me to replace multiple strings from multiple files for a while. So far I've got this code:

$replacements = @{
'bCompressDiffuseLocalPlayerCharacterTextures=True' = 'bCompressDiffuseLocalPlayerCharacterTextures=False'
'bCompressDiffuseLocalPlayerVehicleTextures=True'   = 'bCompressDiffuseLocalPlayerVehicleTextures=False'
'bCompressDiffuseOtherPlayerCharacterTextures=True' = 'bCompressDiffuseOtherPlayerCharacterTextures=False'
'bCompressDiffuseOtherPlayerVehicleTextures=True'   = 'bCompressDiffuseOtherPlayerVehicleTextures=False'
'bCompressNormalTextures=True'                      = 'bCompressNormalTextures=False'
'bDisablePhysXHardwareSupport=True'                 = 'bDisablePhysXHardwareSupport=False'
'bEnableMouseSmoothing=True'                        = 'bEnableMouseSmoothing=False'
'bInitializeShadersOnDemand=True'                   = 'bInitializeShadersOnDemand=False'
'MaxChannels=32'                                    = 'MaxChannels=64'
'MotionBlur=True'                                   = 'MotionBlur=False'
'm_bCalculateOnServer=True'                         = 'm_bCalculateOnServer=False'
'OneFrameThreadLag=True'                            = 'OneFrameThreadLag=False'
'PoolSize=140'                                      = 'PoolSize=1024'
'UseMinimalNVIDIADriverShaderOptimization=True'     = 'UseMinimalNVIDIADriverShaderOptimization=False'
'UseTextureFileCache=False'                         = 'UseTextureFileCache=True'
}

function Update-FileContent {

[cmdletbinding()]
param(
    [Parameter(ValueFromPipeline=$true,
                ValueFromPipelineByPropertyName=$true,
                Mandatory=$true,
                Position=0)]
    [Alias('PsPath')]
    $Path
)

$lines = Get-Content $Path
$lines | ForEach-Object {

    foreach($rep in $replacements.Keys)
    {
        $_ = $_ -replace $rep, $replacements[$rep]
    }

    $_
} | Set-Content $Path
}

Get-ChildItem -Recurse *.ini | Update-FileContent

It works but only if a file is 1 directory deep.

  • 1
    You want it to work in the pipe? Well then you need to add a `process` block to your cmdlet. Else it will only work on the first item in the pipe. – Matt Dec 13 '18 at 13:35
  • `It works but only if a file is 1 directory deep.` Does this men that it doesn't find no other ini-files, or does it mean that the script doesn't work at all if there are more directory levels? What have you tried so far? What happens if your run `gci -Recurse *.ini | Write-Host`? Have you tried to change the binding of the function and call it with a path to a "deeper" INI file? – harper Dec 13 '18 at 13:38
  • Good point, @Matt, though it's the _last_ object in the pipeline that is the only one processed, because the absence of a `process` block is an implicit `end` block. – mklement0 Dec 13 '18 at 14:29

1 Answers1

0

I'd do something like this:

$replacements = @{
    'bCompressDiffuseLocalPlayerCharacterTextures=True' = 'bCompressDiffuseLocalPlayerCharacterTextures=False'
    'bCompressDiffuseLocalPlayerVehicleTextures=True'   = 'bCompressDiffuseLocalPlayerVehicleTextures=False'
    'bCompressDiffuseOtherPlayerCharacterTextures=True' = 'bCompressDiffuseOtherPlayerCharacterTextures=False'
    'bCompressDiffuseOtherPlayerVehicleTextures=True'   = 'bCompressDiffuseOtherPlayerVehicleTextures=False'
    'bCompressNormalTextures=True'                      = 'bCompressNormalTextures=False'
    'bDisablePhysXHardwareSupport=True'                 = 'bDisablePhysXHardwareSupport=False'
    'bEnableMouseSmoothing=True'                        = 'bEnableMouseSmoothing=False'
    'bInitializeShadersOnDemand=True'                   = 'bInitializeShadersOnDemand=False'
    'MaxChannels=32'                                    = 'MaxChannels=64'
    'MotionBlur=True'                                   = 'MotionBlur=False'
    'm_bCalculateOnServer=True'                         = 'm_bCalculateOnServer=False'
    'OneFrameThreadLag=True'                            = 'OneFrameThreadLag=False'
    'PoolSize=140'                                      = 'PoolSize=1024'
    'UseMinimalNVIDIADriverShaderOptimization=True'     = 'UseMinimalNVIDIADriverShaderOptimization=False'
    'UseTextureFileCache=False'                         = 'UseTextureFileCache=True'
}

function Update-FileContent {
    [cmdletbinding()]
    param(
        [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, Position=0)]
        [Alias('PsPath')]
        $Path
    )

    # read the ini file in as one single string
    $content = Get-Content $Path -Raw
    # copy that string so we can compare at the end of the loop if anything has changed at all
    $newContent = $content
    foreach($rep in $replacements.Keys) {
        $newContent = $newContent -replace $rep, $replacements[$rep]
    }
    # only replace the contents of the ini file if changes are made
    if ($newContent -ne $content) {
        Write-Host "Replacing content of file '$Path'"
        Set-Content $Path -Value $content
    }
}

$rootPath = '<PATH OF THE ROOTFOLDER TO RECURSE WHERE THE INI FILES ARE LOCATED>'
Get-ChildItem -Path $rootPath -Filter '*.ini' -Recurse | ForEach-Object { Update-FileContent $_.FullName }
Theo
  • 57,719
  • 8
  • 24
  • 41
  • Does it need to be manually specified vs being based off of the location of the script when executed? – Triksterism Dec 13 '18 at 13:34
  • @Triksterism I recommend ALWAYS to give it a full path. If you don't, the current working directory ($pwd) is used. If you want it to be the path the script itself is running from, have a look at [$PSScriptRoot](https://stackoverflow.com/questions/3667238/how-can-i-get-the-file-system-location-of-a-powershell-script/3667376#3667376) – Theo Dec 13 '18 at 14:26
  • Theo, I suggest adding a `process` block to `Update-FileContent`, as originally suggested by Matt, so you don't need the `ForEach-Object` workaround. – mklement0 Dec 13 '18 at 14:35