How to replace a string (with exceptions) in multiple text-files


  1. Posts : 7
    Win10
       #1

    How to replace a string (with exceptions) in multiple text-files


    I would like to process all *.bat files in a directory to replace all instances of my use of the command "echo " to "echo/", but I would like to exclude the "@echo off" command from being changed.

    How do I do that?

    I have used the past couple of hours searching Google, but I am not finding what I am looking for.

    I have tried to look into regex several times, but I simply cannot comprehend it.

    I am open to having it be in regex if it works in grepWin, Or if grepWin has a way of excluding strings without regex which I just haven't caught on to yet, that would be great.

    Or if someone can explain how to it in PowerShell, that would be wonderful also.

    I hope somebody can help me.

    Thank you.
      My Computer


  2. Posts : 745
    Windows 10/11
       #2

    PowerShell script created with the help of ChatGPT:
    Code:
    param (
        [string]$folderPath = (Get-Location)
    )
    
    $fileList = Get-ChildItem -Path $folderPath -File | Where-Object { $_.Extension -eq ".bat" -or $_.Extension -eq ".cmd" }
    
    foreach ($file in $fileList) {
        $content = Get-Content -Path $file.FullName -Raw
        $content = $content -replace "echo`r`n", "echo/`r`n"
        $content | Set-Content -Path $file.FullName -Force
    }
    
    Write-Host "Processing complete."
    I have made the assumption that you only want to change "echo" if it's on a line by itself. If you want to change "echo hello" to "echo/hello" that would require a modification to the script. Please clarify.
      My Computer


  3. Posts : 7
    Win10
    Thread Starter
       #3

    LesFerch said:
    PowerShell script created with the help of ChatGPT:

    I have made the assumption that you only want to change "echo" if it's on a line by itself. If you want to change "echo hello" to "echo/ hello" that would require a modification to the script. Please clarify.
    Thank you! I am not sure I understand your question. Looking at the code, it SHOULD change "echo hello" to "echo/hello", no?

    But when I run the code in a PowerShell window, no .bat files are changed.

    Also, in your code, I cannot see an exception for "@echo off"?
      My Computer


  4. Posts : 745
    Windows 10/11
       #4

    The previous code only changes lines that have "echo" only on the line. I think this might be what you're looking for:
    Code:
    param (
        [string]$folderPath = (Get-Location)
    )
    
    $fileList = Get-ChildItem -Path $folderPath -File | Where-Object { $_.Extension -eq ".bat" -or $_.Extension -eq ".cmd" }
    
    foreach ($file in $fileList) {
        $content = Get-Content -Path $file.FullName -Raw
        $content = $content -replace '(?<!@echo\s)echo\s', 'echo/'
        $content = $content -replace "echo`r`n", "echo/`r`n"
        $content | Set-Content -Path $file.FullName -Force
    }
    
    Write-Host "Processing complete."
      My Computer


  5. Posts : 7
    Win10
    Thread Starter
       #5

    LesFerch said:
    The previous code only changes lines that have "echo" only on the line. I think this might be what you're looking for:
    Thanks you for your help!

    This changed the files, but it also replaced "@echo off" with "@echo/off".

    I have just tried to ask ChatGPT as you wrote you had. I only have access to the free one, v. 3.5. It came up with this, which works, but it also replaced "@echo off" with "@echo/off".

    So I had to add the '@' manually, and then it worked!

    I just wish I could grasp PowerShell. It's really confusing and difficult to understand what is happening and when. It doesn't seem like C program, easy to grasp the sequence of execution.

    Code:
    # Specify the directory containing .bat files
    $directory = "C:\Path\To\Directory"
    
    # Get all .bat files in the directory
    $batFiles = Get-ChildItem -Path $directory -Filter *.bat
    
    # Loop through each .bat file
    foreach ($file in $batFiles) {
        # Read the content of the file
        $content = Get-Content -Path $file.FullName
        
        # Process each line in the file
        for ($i = 0; $i -lt $content.Count; $i++) {
            $line = $content[$i].Trim()
            
            # Check if the line contains "echo" but not "echo off"
            if ($line -match 'echo ' -and $line -notlike '@echo off*') {
                # Replace "echo " with "echo/"
                $content[$i] = $line -replace 'echo ', 'echo/'
            }
        }
        
        # Write the modified content back to the file
        $content | Set-Content -Path $file.FullName
    }
    
    Write-Host "Processing complete."
      My Computer


  6. Posts : 745
    Windows 10/11
       #6

    Subhuti said:
    This changed the files, but it also replaced "@echo off" with "@echo/off".

    I have just tried to ask ChatGPT as you wrote you had. I only have access to the free one, v. 3.5. It came up with this, which works, but it also replaced "@echo off" with "@echo/off".

    So I had to add the '@' manually, and then it worked!

    I just wish I could grasp PowerShell. It's really confusing and difficult to understand what is happening and when. It doesn't seem like C program, easy to grasp the sequence of execution.
    Yes, sorry about that. I didn't check carefully enough before posting. Glad to hear you got it sorted.

    I also just use the free version of ChatGPT. I had the paid version 4.0 for a while, but the extra accuracy was not usually worth the slower reply speed for me. Someday I'll have to give Copilot in Visual Studio a whirl.

    With ChatGPT, it can often take a few tries and a bit of tweaking to get the right results, but it's still a time saver for me. I do find that ChatGPT makes more mistakes with PowerShell than C#. I'm sure there are many factors that account for those accuracy differences. It's often best to keep the requirements as simple as possible with the first pass then test and ask the AI to add features and test as you go.
      My Computer


  7. Posts : 745
    Windows 10/11
       #7

    Sometimes a bit of lateral thinking leads to a simpler solution. For example, in the code below, rather than trying to implement an exception, I just put the "@echo off" back to the way it was after doing the initial search and replace. This makes for a very simple script:
    Code:
    param (
        [string]$folderPath = (Get-Location)
    )
    
    $fileList = Get-ChildItem -Path $folderPath -File | Where-Object { $_.Extension -eq ".bat" -or $_.Extension -eq ".cmd" }
    
    foreach ($file in $fileList) {
        $content = Get-Content -Path $file.FullName -Raw
        $content = $content -replace "echo ", "echo/"
        $content = $content -replace "@echo/", "@echo "
        $content = $content -replace "echo`r`n", "echo/`r`n"
        $content | Set-Content -Path $file.FullName -Force
    }
    
    Write-Host "Processing complete."
      My Computer


  8. Posts : 779
    Windows 7
       #8

    Regular expressions allow for lookahead, where you can specify an optional pattern that occurs before your desired match. Using conditional logic, you can exclude unwanted matches with that lookahead.
    Code:
    '(?optional_pattern)wanted_match'
    Replace every "echo ", except those proceeded by "@".
    Code:
    '(?<!excluded_pattern)wanted_match'
    
    $string -replace '(?<!@)echo ','echo/ '

    Your next problem is sloppy constraints. What happens if the word "echo" isn't part of a command, but inside a line being echoed?
    Code:
    @echo off
    
    if exist filename.txt (
        echo File exists
    ) else (
        echo File missing
    )
    
    echo Don't echo an echo command
    Code:
    @echo off
    
    if exist filename.txt (
        echo/ File exists
    ) else (
        echo/ File missing
    )
    
    echo/ Don't echo/ an echo/ command

    To solve this condition, we need to convert Get-Content into an array of individual lines, and then perform a "[regex] .Replace(old_string,new_string,count)" on each line where the count is 1 (only replace the first match).

    We should also skip files where there's nothing to replace, instead of overwriting all read files.
    Code:
    param (
        [string]$folderPath = (Get-Location)
    )
    
    $regex = [regex]'(?<!@)echo '
    
    $fileList = Get-ChildItem -Path $folderPath -File | where { $_.Extension -in '.bat','.cmd' }
    
    foreach ($file in $fileList.FullName) {
        $content = Get-Content -Path $file -Raw
    
        if (($content -match $regex) -eq $true) {
            $newContent = @()
            
            foreach ($line in @($content -split "`n")) {
                $newContent += $regex.Replace($line,'echo/ ',1)
            }
    
            $newContent -join "`n" | Set-Content -NoNewline $file -Force
            Write-Host Updated $file
        }
    }

    Code:
    @echo off
    
    if exist filename.txt (
        echo/ File exists
    ) else (
        echo/ File missing
    )
    
    echo/ Don't echo an echo command
    Last edited by garlin; 13 Mar 2024 at 23:10.
      My Computer


 

  Related Discussions
Our Sites
Site Links
About Us
Windows 10 Forums is an independent web site and has not been authorized, sponsored, or otherwise approved by Microsoft Corporation. "Windows 10" and related materials are trademarks of Microsoft Corp.

© Designer Media Ltd
All times are GMT -5. The time now is 22:34.
Find Us




Windows 10 Forums