-match and Editing a text file

This topic contains 9 replies, has 2 voices, and was last updated by  Chris 3 years, 10 months ago.

  • Author
    Posts
  • #10784

    Chris
    Participant

    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

  • #10785

    Don Jones
    Keymaster

    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.

  • #10786

    Chris
    Participant

    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.

  • #10787

    Don Jones
    Keymaster

    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.

  • #10788

    Chris
    Participant

    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.

  • #10791

    Don Jones
    Keymaster

    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.

  • #10797

    Chris
    Participant

    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.

  • #10798

    Chris
    Participant

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

  • #10799

    Don Jones
    Keymaster

    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.

  • #10800

    Chris
    Participant

    That seems easy enough, Thanks again!

You must be logged in to reply to this topic.