2

I have never worked with powershell before, and Google didn't give me answers for exactly what I need. I hope you guys can help me. I need to find all the files in the directory(including sub-directories) that have 2 lines:

aaa aaa
bbb bbb

and replace the second line with ccc

Replacement shouldn't happen if line bbb bbb doesn't follow right after aaa aaa.

Here's what I have so far:

$line3 = "aaa[ ]*aaa[ ]*bbb[ ]*bbb"
Get-ChildItem -exclude *.bak -recurse *.txt | Where-Object {$_.Attributes -ne "Directory"} |
    ForEach-Object { 
        Copy-Item $_ "$($_).bak"; 
        $wholeFile = [IO.File]::ReadAllText("$_");
        $wholeFile = $wholeFile.replace("`n", " ");
        $wholeFile = $wholeFile.replace("`r", " ");
        $temp3 = $wholeFile | Select-String -pattern $line3 -quiet;
        if(($temp3 -eq "True")){
            (Get-Content $_)  -replace "bbb[ ]*bbb", "ccc" | Set-Content -path $_;
        }
        else{
            echo failed
        }
    }

I think this code almost work except if a file has 2 occurrences of "bbb bbb": one that follows right after "aaa aaa" and another one without "aaa aaa". both lines would get replaced, and I need only the first one to be replaced.Is there any way to fix it? Can someone with more experience tell me if I missed something? Is there any simpler/shorter solutions for what I need? I've seen so many neat one-line solutions in powershell. Is there anything like that for my case? Thanks

3 Answers 3

2

I think this will do what you want. It's not a one-liner and I'm sure it could be simplified. I was more worried on getting something functional, though.

I tested your use case, too. This will only replace the 'bbb bbb' line when it directly follows the 'aaa aaa' line. All other locations in the file with that line will not be overwritten.

Just modify the variables at the top with your information for testing.

I've always assumed that you will:

  1. only be processing .txt files
  2. query/replace entire rows of data
#script variables
$filePath     = 'C:\Temp\SO\'
$processFiles = Get-ChildItem -Exclude *.bak -Filter *.txt -Recurse -Path $filePath

$query        = 'aaa aaa'
$query2       = 'bbb bbb'
$replace      = 'ccc'

#script begin
foreach ( $file in $processFiles ) {
    #Pre-Processing | backup the file for archival
    $file | Copy-Item -Destination "$file.bak"

    $arrayData = Get-Content $file
    for ($i=0; $i -lt $arrayData.Count; $i++)
    {
        if ( $arrayData[$i] -match $query ) {
            if ( $arrayData[$i+1] -match $query2 ) {
                $arrayData[$i+1] = $arrayData[$i+1].Replace($query2,$replace)
            }
            else {
                #Catch if the second query isn't matched.
            }
        }
        else {
            #Catch if the first query isn't matched.
        }
    }

    #Overwrites the original text file that was being processed
    $arrayData | Out-File $file -Force
}
Sign up to request clarification or add additional context in comments.

Comments

2

Here is a batch solution :)

@echo off
setlocal enabledelayedexpansion
cd C:\folder
set aaa=false
for /r C:\folder %%a in (*) do (
for /f "tokens=1,2 delims= " %%x in (%%a) do (
if "!aaa!"=="true" (
if "%%x"=="bbb" if "%%y"=="bbb" (
echo ccc ccc >>temp.txt
del %%a /f /q
ren temp.txt %%~nxa
set aaa=false
)
) else (
if "%%x"=="aaa" if "%%y"=="aaa" set aaa=true
echo %%x %%y >>temp.txt
)
)
)

Comments

0

Here's a one-liner solution:

$a="aaa aaa";$b="bbb bbb";$c="ccc";ls *.txt -File -Recurse|foreach{$l=0;cp $_ "$($_).bak";(cat $_)|foreach{if($_ -match $a){$l=1;$_}else{if($_ -match $b){if($l -eq 1){$_ -replace($b,$c)}else{$_};$l=2}else{$l=0;$_}}}|sc($_) -Force}

It's a bit obscure and I wouldn't call it "neat" but it works.

Comments

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.