I'm looking for a way to find 2 different lines in a file and only if those 2 line exist I need to perform a task. So far this is my code
$folderPath = c:\test
$files = Get-ChildItem $Folderpath -Filter *.txt
$find = 'stringA'
$find2 = 'StringB'
$replace = 'something to replace with string b'
if ($files.Length -gt 0 ) {
$files |
select -ExpandProperty fullname |
foreach {
If(Select-String -Path $_ -pattern $find , $find2 -quiet )
{
(Get-Content $_) |
ForEach-Object {$_ -replace $find2, $replace } |
Set-Content $_
write-host "File Changed : " $_
}
}
}
else {
write-host "no files changed"
}
Currently if I run it once it will change the files but if I run it again it will also notify me that it changed the same files instead of the output "no files changed"
Is there a simpler way to make it happen? Thanks
The Select-String
cmdlet selects lines matching any of the patterns supplied to it. This means that the following file contains a match:
PS> Get-Content file.txt
This file contains only stringA
PS> Select-String -Pattern 'stringA', 'stringB' -Path file.txt
file.txt:1:This file contains only stringA
Passing the -Quiet
flag to Select-String
will produce a boolean result instead of a list of matches. The result is $True
even though only one of the patterns is present.
PS> Get-Content file.txt
This file contains only stringA
PS> Select-String -Pattern 'stringA', 'stringB' -Path file.txt -Quiet
True
In your case, Select-String chooses all the files containing either 'stringA'
or 'stringB'
, then replaces all instances of 'stringB'
in those files. (Note that replacements are also performed in files you did not want to alter)
Even after the replacements, files containing only 'stringA'
still exist: these files are caught and reported by your script the second time you run it.
One solution is to have two separate conditions joined by the -and
operator:
If (
(Select-String -Path $_ -Pattern 'stringA' -Quiet) -and
(Select-String -Path $_ -Pattern 'stringB' -Quiet)
)
After this the script should work as intended, except that it won't report "no files changed"
correctly.
If you fix your indentation you'll realise that the final else clause actually checks if there are no .txt
files in the folder:
$files = Get-ChildItem $Folderpath -Filter *.txt
...
if ($files.length -gt 0) {
...
} else {
# will only display when there are no text files in the folder!
Write-Host "no files changed"
}
The way to resolve this would be to have a separate counter variable that increments every time you find a match. Then at the end, check if this counter is 0 and call Write-Host
accordingly.
$counter = 0
...
foreach {
if ((Select-String ...) ...) {
...
$counter += 1
}
}
if ($counter -eq 0) {
Write-Host "no files changed"
}