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.

3
  • 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. Commented Dec 13, 2018 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? Commented Dec 13, 2018 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. Commented Dec 13, 2018 at 14:29

1 Answer 1

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 }
Sign up to request clarification or add additional context in comments.

3 Comments

Does it need to be manually specified vs being based off of the location of the script when executed?
@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
Theo, I suggest adding a process block to Update-FileContent, as originally suggested by Matt, so you don't need the ForEach-Object workaround.

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.