-match and Editing a text file

OK, so I’m hoping someone can help with this. I’m trying to edit a number of text files used as configuration files. I’m currently having two issues with the code:

First, the

If ($oFile -match “$sString” )
check fails; I’ve tried -match, -like, -contains; with and without quotes. It’s in there, but it won’t see it and I don’t know why.

Second, When I’m re-writing the file I’m destroying the formatting (I’ve tried using both Set-Content and Out-file). This has no functionality effect but if/when a person has to look at the file it destroys the readability.

	$aSearchFolders = "$sSysDr\Loc1", "$sPF\Loc2"
	$aOldStrings = 'Name=(Settings)','Name=(Settings)','Name=Setting'
	$aNewStrings = 'Name=(NewSettings)','Name=(NewSettings)','Name=NewSetting'
	ForEach ($sFolder in $aSearchFolders) {
		If (Test-Path "$sFolder") {
			Set-Location $sFolder
			$aSessions = Get-ChildItem -Path $sFolder -Filter *session*.txt -Recurse -Force
			ForEach ($oSession in $aSessions){
				#Write-Host "Session: $oSession"
				ForEach ($sString in $aOldStrings){
					#Write-Host "Search String: $sString"
					$oFile = (Get-Content $oSession)
					#Write-Host $oFile
					If ($oFile -match "$sString" ) {
						Write-Host "Inside the IF!"
						$iNdx = [array]::IndexOf($aOldStrings,$sString)
						$oFile.replace($sString, $aNewStrings[$iNdx])
						Set-Content -Value $oFile -Path $oSession.FullName -Force					
						#$oFile -replace $sString, $aNewStrings[$iNdx] | Out-Null
					}
					If ($sString -eq "Name=Setting") {$oFile = ((Get-Content $oSession) -join "`n") -replace "DiffName=DiffSetting", "DiffName=DiffSetting`nNewName=NewSetting"}
					Set-Content -Value $oFile -Path $oSession.FullName -Force
				}	
			}
		}
	}

So where’s my mis-step? And thanks in advance…

Also, if it helps, the text files are formatted like this:
[Region0]
Name=(Settings)
Name=(Settings)
Name=Setting

[Region1]
Name=(Settings)
Name=(Settings)
Name=Setting

Well, -match and -like are different; one does regular expression and the other is simple wildcard matching. -Contains has nothing to do with string matches whatsoever - it checks to see if a collection contains a specific object or not.

You don’t need the double quotes around $sString. You’ve done that in a few places, and it isn’t necessary - that’s not how variables work. In fact, in some cases it’ll mess you up, because putting a variable inside double quotes force-casts it into a single string.

Understand that Get-Content does not read a text file as a single string; it reads each line as a string, which means it returns a collection of objects. You can’t execute -match against a collection that way.

So if myfile.txt contains multiple lines…

$contents = Get-Content myfile.txt
foreach ($line in $contents) {
  if ($line -match 'regex') { 
    #whatever
  }
}

Is more what I’d expect to see - checking each LINE in the file, one at a time. Now, if your goal is to just check the entire contents of the file…

$contents - Get-Content myfile.txt | Out-String

Might be a way to force the collection of strings to coalesce into a single string object, which -match can deal with.

But in order to help more, I need to know what it is you’re trying to compare to what. I’m not sure -match is what you should be using - for a simple string match, -like MIGHT be much better. But you need to understand a bit about how both -match and -like operate.

$test = "This is a test string"
$regex = "test"
$simple = "test"
$test -match $regex  # TRUE
$test -like $simple # FALSE

You see, -like doesn’t do a “floating match” like a regex would. And -contains has nothing to do with what you’re doing.

Yea, I tried contains more of a last-ditch attempt. The quotes were added when it wasn’t working without them; but you’re right, I have them in a couple places they’re unrequired.

My understanding was that by placing Get-Contents within parentheses it would load the entire file, instead of just a collection of lines; is that incorrect?

On the other side of that however, if I’m loosing the formatting when I re-write the file; then maybe I need to be going through line by line.

The actual lines I’m trying to find & replace are these; find the old, replace with the new:

	$aOldStrings = 'RemoteHostAddressList=(ibmhost,IBM-3278,23,YES,1,NO,NO)','RemoteHostAddressList=(ibmhost,IBM-3278,1423,YES,1,NO,NO)','SSLEncryptionStrength=-1' 	$aNewStrings = 'RemoteHostAddressList=(ibmhost,IBM-3278,23,YES,11,NO,NO)','RemoteHostAddressList=(ibmhost,IBM-3278,1423,YES,11,NO,NO)','SSLEncryptionStrength=256'

And the file (oSession) contents looks like this:

[Connection] SameModel=NO ResourceName= DeviceType= Associate=NO DevModel= HostDelay=3 RemoteHostAddress= ConnectionRetryCount=0 DestinationPort= CertificateLabel= RemoteHostAddressList=(ibmhost,IBM-3278,23,YES,1,NO,NO) CPType= IDMgrUseIP=NO IDMgrAddrPool= AssociateFileName=

Hopefully that helps.

Parentheses do not have that effect, no. They work just like they do in math, specifying an order of execution or evaluation. They don’t change what the cmdlet puts into the pipeline.

So if you’re trying to do a replace, we should probably be looking at the -replace operator, no?

foreach ($line in $file) {
  for($i=0, $i -lt ($newstrings.count), $i++) {
    $revisedline = $line -replace $oldstrings[$i],$newstrings[$i]
  }
}

Wouldn’t that work? It goes through each line, and replaces each old string (in an array) with its corresponding new string (which has the same index in its array)?

Most of PowerShell’s operators don’t work with arrays. For example, if PowerShell were PHP, you could just do “$line -replace $oldstrings,$newstrings” and it’d work. PowerShell won’t do that - you’ve got to use single strings with the -replace operator, as with -like or -match.

Actually, that’s not COMPLETELY true - the -like operator will accept an array in the first operand (array -like object), just not in the second. When you do that, it doesn’t return true/false, though - it returns the matched array element(s). Anyway, I think the example above might do what you’re after.

Oh, ok… well that clears up at least that misunderstanding.

And that’s good to know about -like.

So I ran this:

$aOldStrings = 'RemoteHostAddressList=(ibmhost,IBM-3278,23,YES,1,NO,NO)','RemoteHostAddressList=(ibmhost,IBM-3278,1423,YES,1,NO,NO)','SSLEncryptionStrength=-1' $aNewStrings = 'RemoteHostAddressList=(ibmhost,IBM-3278,23,YES,11,NO,NO)','RemoteHostAddressList=(ibmhost,IBM-3278,1423,YES,11,NO,NO)','SSLEncryptionStrength=256' ForEach ($sFolder in $aSearchFolders) { If (Test-Path $sFolder) { Set-Location $sFolder $aSessions = Get-ChildItem -Path $sFolder -Filter *session*.edp -Recurse -Force ForEach ($oSession in $aSessions){ Write-Host "Session: $oSession" #ForEach ($sString in $aOldStrings){ #Write-Host "Search String: $sString" $File = Get-Content $oSession #Write-Host $oFile foreach ($line in $File) { for($i=0, $i -lt ($newstrings.count), $i++) { $revisedline = $line -replace $oldstrings[$i],$newstrings[$i] } } Set-Content -Value $File -Path $oSession.FullName -Force #} } } }

But still no dice.

You’re just copying and pasting what I have you without integrating it into your script. For example, you used $aOldStrings for a variable. I didn’t. My example used $oldstrings. The pseudo-Hungarian-notation thing isn’t fashionable anymore, so I don’t tend to prefix my variables with “a” and “s” like you did. You’re welcome to do so, obviously, but you’re going to have to work the code into your script, not just paste it.

I put the revised string into $revisedstring, but you’re not using that anywhere. Your Set-Content command is just writing the original $file variable, which hasn’t been modified.

Go through the logic on this. I wasn’t trying to provide you with a drop-in fix for your code - I was trying to provide an illustration of what you needed to change in your approach. You’re going to have to make sure you understand what it’s doing, and then integrate that into your script.

Doh… I missed the name changes.

Ok, so assuming I have $line = $line -replace $aOldstrings[$i],$aNewstrings[$i]

How would I re-integrate that back into the $file? Sorry if that’s a stupid question; I haven’t done a lot with text files.

Also thank you for your help, it’s much appreciated, even if i’m a bit slow on the uptake.

Well, $revisedline (from my example) has the revised line. Just pipe it out to a file.

$revisedline | out-file

If the line didn’t need any replacement, it’ll be the same, so you’d out it anyway. You’re basically recreating the file one line at a time.

That seems easy enough, Thanks again!