1

So for this script I'm having an issue if I run the script multiple times it overwrites the second occurrence of "start-sleep -s 10" with the replaced text "start-sleep -s 30" that I need to remain the same ie. "start-sleep -s 10". When I tested this it works great when you run it once, but what if the script is ran multiple times? During testing, when the script is ran more than once, the second instance is then changed to "start-sleep -s 30" when I need it to remain "start-sleep -s 10" every time the script is ran as well as not altering the rest of the file. Is there a way in PS to prevent this? Possibly just have the script search lines 0-115 for example? because the second instance of start-sleep -s 10 is located at line 145. What I essentially need is for the script to find the first instance of "start-sleep -s 10" and replace with "start-sleep -s 30", leave the second instance of "start-sleep -s 10"alone every time I run this script. I posted an original question for this before and can add it at the end of this post.

$ScriptPath = "C:\ScriptsFolder\powershell.ps1"
$newContent = switch -Wildcard -File $ScriptPath {
    # if this line contains `Start-Sleep -s 10`
    '*Start-Sleep -s 10*' {
        # if we previously matched this
        if($skip) {
            # output this line
            $_
            # and skip below logic
            continue
        }
        # if we didn't match this before (if `$skip` doesn't exist)
        # replace from this line `Start-Sleep -s 10` for `Start-Sleep -s 30`
        $_.Replace('Start-Sleep -s 10', 'Start-Sleep -s 30')
        # and set this variable to `$true` for future matches
        $skip = $true
    }
    # if above condition was not met
    Default {
        # output this line as-is, don't change anything
        $_
    }
}
$newContent | Set-Content $ScriptPath

Original Question: I need to find and replace one occurrence of a string in a PowerShell script (script is about 250 lines long). There are two occurrences where the string "start-sleep -s 10" appears within the script. I need to edit and only change the first occurrence to "start-sleep -s 30 and keep the second occurrence "start-sleep -s 10". The issue I'm having is that there are a couple variants of the script I have to edit so my approach was locate the range of lines where the first occurrence is located, make the change, then save the script and keep everything else as is. New to PowerShell so not sure how to go about this. I keep seeing articles online of how to use Get-Content and Set-Content to find and replace text in a file but I need to change the first occurrence of "start-sleep -s 10" and keep the second one as is.

4
  • 2
    Change the case label to '*Start-Sleep -s *' so it still matches after the first run Commented May 18, 2022 at 19:39
  • 1
    See, I knew there were easier ways to do it @MathiasR.Jessen 🤦‍♂️ Commented May 18, 2022 at 19:41
  • 3
    Why not pass the sleep time as a parameter and pick right value whenever you invoke the script? Commented May 18, 2022 at 19:50
  • You can use the Select-String cmdlet and Select-Object, like this: Get-Content C:\test.ps1 | Select-String -Pattern 'Start-Sleep -s 10' | Select -First 1 Commented May 18, 2022 at 21:23

1 Answer 1

2

To replace only a certain number of matches you can use the Regex class Replace() method which allows you to specify the number of replacements

$scriptPath = '.\sleep_text.txt'
$scriptContent = Get-Content $scriptPath -Raw
$find = 'start-sleep -s 10'
$replacement = 'start-sleep -s 30'
$numberOfReplacements = 1

$regex = [regex]$find
$regex.Replace( $scriptContent, $replacement, $numberOfReplacements ) | Set-Content -Path $scriptPath

Not processing the same file twice

I agree with Theo's comment that if you do not want to process the same file twice then just save the file to a different location or to a different name that you can filter out during pre-processing. If you don't like this method another method you could use is to provide your script a different way of knowing which files have been processed already.

Here is an example of one way. Here we append a comment to the bottom of the script during processing. The script looks for this comment in an if statement and if found just displays a warning on the screen that the file has already been processed rather then processing again.

$scriptPath = '.\sleep_text.txt'
$scriptContent = Get-Content $scriptPath -Raw
if ($scriptContent -notmatch '# processed') {
    $find = 'start-sleep -s 10'
    $replacement = 'start-sleep -s 30'
    $numberOfReplacements = 1

    $regex = [regex]$find
    ($regex.Replace( $scriptContent, $replacement, $numberOfReplacements )) + "`n# processed" | Set-Content -Path $scriptPath
}
else {
    Write-Warning "$scriptPath already processed"
}
WARNING: .\sleep_text.txt already processed
Sign up to request clarification or add additional context in comments.

4 Comments

Nice, I didn't know about this overload! Just in case, you might want to consider $regex = [regex]::new('start-sleep -s 10', 'IgnoreCase') or '(?i)start-sleep -s 10'
So this solution works great if you run it once and I get the desired result, but when I run the script a second time+ it changes the second instance of start sleep 10 to 30 which i dont want it to do. Is there a way for the script to read the file and stop before line 140 for example?
@AverageJoe I suggest you save the (once) updated file to a different location or under a new name so you don't process it more that once
I agree with Theo. If you don't want to process the file twice then save the files to a different location or rename them to something that can be identified as being processed already. If you don't like this method I've updated my answer with another way you can handle this.

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.