Home > OS >  Powershell search within file and add if not exist
Powershell search within file and add if not exist

Time:02-18

I have a file that I want Powershell to search for Include1 and if it does, see if the rest of the line contains XYZ. If it does, fine, but if it does not, see if Include2 exists and repeat. But once Powershell gets to a point where IncludeX does not exist and we have not found XYZ yet, at the end of the file, write the next available IncludeX with XYX and then remove any double blank lines \r\n\r\n. I can find plenty of examples of Powershell looking for a string in a file, a few that can write a set line if the string does not exist, but not one that can increment the IncludeX as part of the new line. Please help!

The file is ccleaner.ini, if you have CCleaner, it is easy to reference. The Include1 and subsequent Include<n> lines are for setting file paths to include in the cleaning that is not in their default lists. I have a script that will modify some settings in the file, but I can't figure it out.

Example from my file:

Include1=PATH|C:\ProgramData\Quest\KACE\downloads\|*.*|RECURSE|0|0|24
Include2=PATH|D:\Temp\|*.*|RECURSE|0|0|24

I want to make sure the computers that I will run this script on has the =PATH|C:\ProgramData\Quest\KACE\downloads\|*.*|RECURSE|0|0|24, no matter what Include number it is.

CodePudding user response:

If I understand what you are trying to do, then I recently worked on something that was similar. So I rewrote it to produce this.

To be honest, I don't understand what you are trying to do with the double blank lines, so I didn't try to address that. If your source file has blank lines, and you want to remove them, probably need to do that before searching for include lines and appending new include lines.

If you are only working on one file, use the commented out "$Files = ..." statement and give it the exact name of the file you want. And don't forget to set the path in the Get-ChildItem to the correct folder.

When done, the $Files array should have the same length as the $Results array and you should be able to find out what happened to each file my checking the matching result in $Results. A result of 0 means the value was found and nothing was done. A result above 0 means the a new line was added with the result is the include number it was given.

There are some Write-host statements used in testing that you many want to remove.

function Process-File {
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$WhatToLookFor,
        [Parameter(ValueFromPipeline)]
        $FilePath
    )
    begin {
        $LookingFor = $WhatToLookFor.Trim()
    }
    Process {
        [int]$Return = 0
        [bool]$FoundIt = $false
        $FileFullName = $FilePath.FullName
        Write-Host
        Write-Host "Working on $FileFullName"
        switch -Regex -File $FileFullName {
            '(?i)^\s*Include(?<Number>[0-9] )\s*=\s*(?<Value>. ?)\s*$' { 
                [int]$Number = [int]($Matches.Number)
                if($Number -gt $Return) { $Return=$Number}
                $Value = $Matches.Value
                if($Value -eq $LookingFor) {$FoundIt = $true}
                Write-Host "$Number=[$Value]"
                continue
            }
            default { continue }
        }
        $Return  
        if( $FoundIt) {
            return 0
        } else {
            "Include$Return=$LookingFor" | Add-Content $FileFullName
            return $Return
        }
    }
}
#$Files = Get-ChildItem -Path 'D:\Temp\StackOverflow\71160426' -Filter 'TextIn.txt'
$Files = Get-ChildItem -Path 'D:\Temp\StackOverflow\71160426' -Filter '*.txt' -Recurse 
$Results = $Files | Process-File 'NPR'

foreach ($Result in $Results) {
    Write-Host
    if($Result -gt 0) {
        Write-Host "Saved as Include$Result."
    } else {
        Write-Host "Item was found and nothing added to file."
    }
}

Tested it on 2 files, TextIn1.txt and TextIn2.txt, each file had includes 1 through 8 already defined. TextIn1.txt also had the include9 with the value "NPR". The following is the results of running the script on these 2 files. And both files had the same "Include9=NPR" at the end when done.

The last 2 statements in the output below explain the action taken to TextIn1.txt and then TextIn2.txt. I didn't take the time to add the filenames in the statements, but you get the idea what just happened.

This was NOT strongly tested against files of diverse content. So you may have to do some modify to deal with your real world source file(s).


Working on D:\Temp\StackOverflow\71160426\TextIn1.txt
1=[ABC]
2=[DEF]
3=[GHI]
4=[JKL]
5=[MNO]
6=[PQR]
7=[STU]
8=[XYZ]
9=[NPR]

Working on D:\Temp\StackOverflow\71160426\TextIn2.txt
1=[ABC]
2=[DEF]
3=[GHI]
4=[JKL]
5=[MNO]
6=[PQR]
7=[STU]
8=[XYZ]

Item was found and nothing added to file.

Saved as Include9.

CodePudding user response:

You can use a switch statement:

$file = 'ccleaner.ini' # Path to the file of interest.
$(
  $num = 0; $found = $false
  $requiredValue = 'PATH|C:\ProgramData\Quest\KACE\downloads\|*.*|RECURSE|0|0|24'
  switch -Regex -File $file {
    '^\s*Include(\d )=(.*)' {
      $_ # Pass line through
      if ($found) { continue }
      [int] $num = $Matches[1]
      $found = $Matches[2] -eq $requiredValue
    }
    default {      
      $_ # Pass other lines through.
    }
  }
  # If the value of interest wasn't found, add it now
  # with the next sequence number.
  if (-not $found) {
    "Include$($num 1)=$requiredValue"
  }
) | Set-Content $file -WhatIf

Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.

To also fold multiple consecutive newlines into one in order to remove empty lines (... represents parts of the solution above):

$(...) -join "`r`n" -replace '(\r\n){2,}', "`r`n" | Set-Content ...
  • Related