1

I am trying to retrieve the unknown file path of a known filename and then do a Get-Content on that filename so that I can modify it. I know that the file will be in C:\Users\$env:UserName\ but thats as far as I can go (the end user may have it in further in, like Documents or Desktop or somewhere else).

It seems to can do either a Get-ChildItem or a Get-Content but for some reason I can't do both in the same script.

Overall goal: Pipe the results of a Get-ChildItem to the Get-Contents function so that I can modify the file.

Here's my code:

(Get-ChildItem -Path C:\Users\$env:UserName -Filter QA11test.hep) | 
    Foreach-Object {
        $_ # send the current line to output
        if($_ -match "SSL/TLS User Cipher Suites=DEFAULT"){
            "IP Port=2023"
            "Security Option=2"
            $Message = "Your host explorer session has been updated."
            Msg $env:UserName $Message
            exit
        } else {
            $Message = "Script Failed."
            Msg $env:UserName $Message
            exit
        }
    } | Set-Content C:\Users\$env:UserName\Telnet\QA11TestSecure.hep

Need help! Thank you!

techguy1029
  • 743
  • 10
  • 29
Jim Thunder
  • 11
  • 1
  • 5

3 Answers3

1

There appears to be some confusion with how to go about getting a file, it's contents, and then checking the file contents for some arbitrary line. Rather than write this in a large compound command, I suggest breaking things down into a different style so that pipeline confusion is minimized. There are several ways to accomplish the same task, some are easier to follow than others, especially when debugging.

$fileoutput = @'
IP Port=2023
Security Option=2
'@
$FileToLookAt = Get-ChildItem -Path C:\Users\$env:UserName -Recurse -Filter "QA11test.hep" | Get-Content
if ($FileToLookAt)
{
    $found = ""
    $found = $FileToLookAt | Where-Object {$_ -eq 'SSL/TLS User Cipher Suites=DEFAULT'}
    if ($found)
    {
        $Message = "Your host explorer session has been updated."
        Set-Content -Path C:\Users\$env:UserName\Telnet\QA11TestSecure.hep -Value $fileoutput -Force
    } else 
    {
        $Message = "Script Failed."
    }
} else 
{
    $Message = "QA11test.hep not found."
}
Msg $env:UserName $Message

Edit: OK, there really should be checking on Set-Content to make sure the file/path exists. Assuming it does, something like this should work.

Brian McMahon
  • 369
  • 3
  • 12
  • I am writing "IP Port=2023" and "Security Option=2" to that file. – Jim Thunder Nov 13 '19 at 22:03
  • When you say writing to the file, I'm assuming you do not mean appending. – Brian McMahon Nov 13 '19 at 22:28
  • I mean inserting into that file. – Jim Thunder Nov 14 '19 at 14:55
  • the code you have above works very well! So thank you for that, but the problem is that it places those two lines at the top of the file. I need them inserted after the line with "SSL/TLS User...." – Jim Thunder Nov 14 '19 at 15:30
  • So for that I would read in that separate file you want the lines added to, insert the lines and write out the new version of the file, see this answer to a similar question: https://stackoverflow.com/a/11503611/2642844 – Brian McMahon Nov 14 '19 at 15:37
  • ok, trying out doing that. I'm so close but now for some reason my script can't find the original file. No idea why. I have this as my first line: $FileToLookAt = (Get-ChildItem -Path C:\Users\$env:UserName\ -Recurse -Filter QA11test.hep) But what the $FileToLookAt variable holds is C:\QA11test.hep when it SHOULD be the full path. Idk why it cuts out the middle. – Jim Thunder Nov 14 '19 at 17:16
  • You don't need the parentheses, but the path is available in $FileToLookAt.DirectoryName. If you want the path with a file name, concatenate it: $FileToLookAt.DirectoryName + "\QA11TestSecure.hep" – Brian McMahon Nov 14 '19 at 18:34
1

In the simplest case, all you need to do is insert a pipeline segment with a Get-Content command:

Get-ChildItem -Path C:\Users\$env:UserName -Filter QA11test.hep |
  Get-Content | # Send the lines of the input file(s) one by one to ForEach-Object
    ForEach-Object { ...

However, there are problems with your approach:

  • If a file is found, the output file is unconditionally written - even if no modifications are made to the contents of the input.

    • Additionally, due to the exit statements in your code, any remaining input lines are omitted from the output file.
  • If no file is found, the output file is either created as an empty file (0 bytes) or, if it already existed, replaced with an empty file.


Instead, I suggest using an approach based on the switch statement, which is faster, more concise, and more flexible:

Notably, this solution only creates / replaces the output file if an input file was found and modifications were made to it.

Get-ChildItem -Path C:\Users\$env:UserName -Filter QA11test.hep | ForEach-Object {
  $updated = $false
  $newLines = switch -File $_.FullName -Regex { # Loop over all file lines
    'SSL/TLS User Cipher Suites=DEFAULT' { 
       $_, "IP Port=2023", "Security Option=2" # send 2 additional lines
       $updated = $true
    }
    default { $_ } # pass other lines through
  }
  # Rewrite the file only if modifications were made.
  if ($updated) { $newLines | Set-Content C:\Users\$env:UserName\Telnet\QA11TestSecure.hep }
  # Emit the appropriate status message.
  Msg $env:UserName ('Nothing to update.', 'Your host explorer session has been updated.')[$updated]
}

Note: If you also need to know whether an input file was found at all - irrespective of whether it needed updating - set $found = $true inside the script block and check it after the command: if (-not $found) { Msg $env:UserName 'QA11test.hep not found' }

  • switch -File $_.FullName -Regex reads the input file line by line, and matches each line against the regex condition:

    • If the regex matches, the original line ($_) is output, along with 2 additional lines; plus, flag $updated is set to indicate that the content was modified.
    • All other lines (the default conditional) are output as-is.
  • $newLines = ... captures the (potentially modified) lines in an array.

    • Note that this means that all lines are stored in memory at once, but with text files that is usually not a concern.
  • Only if $updated is set is the output file written (Set-Content).

  • Finally, the Msg command is invoked with a status-appropriate message.

    • Note the ('msg1', 'msg2')[$updated] expression, which is a shortcut for choosing one of two values based on a flag: if $updated is $true, $true is coerced to array index 1 and therefore selects 'msg2'; if it is $false, the index is 0, and 'msg1' is selected.
mklement0
  • 382,024
  • 64
  • 607
  • 775
-1

Thank you everyone who helped!

(@mklement0, I'm going to modify this to do something close to what your last post does! That helps me a lot.)

I took bits and pieces from different posts and this is what I came up with that does what I need it to do.

This PowerShell script grabs the full path of a known file, "QA11test.hep" and loads it into a variable for later use. It then searches the contents of that file for a specific string "SSL/TLS..etc" and if it finds it, it adds two lines, "Port=2023" and "Security Option 2" below the line it found. Then it takes that new file and adds it to C:\Users\$env:UserName\Desktop\QA11TestSecure.hep without changing the original QA11test.hep file. So instead of piping from get-childitem to get-content, it puts the item retrieved from the get-childitem function into a variable that the get-content function can then search for. Works great!

$FileToLookAt = (Get-ChildItem -Path C:\Users\$env:UserName\ -Recurse -Filter "QA11test.hep") | % { $_.FullName }
$User = $env:UserName
if ($FileToLookAt){
    $updated = $false
    $addtls = Get-Content ($FileToLookAt) | Foreach-Object {
        $_ # send the current line to output
        if($_ -match "SSL/TLS User Cipher Suites=DEFAULT"){
            "IP Port=2023"
            "Security Option=2"
            $updated = $true
        }
    }
    if ($updated) {
        $addtls | Set-Content -Path C:\Users\$env:UserName\Desktop\QA11TestSecure.hep
        $Message = "Host Explorer updated.`nPlease ensure there is a gold LOCK icon at the bottom left `nof the host explorer window.`nIf not, please contact the help center. Thank you."
    } else {
        $message = "File was not updated.`nPlease contant the help center immediatly."
    }
} else {
    $Message = "QA11test.hep not found.`nPlease contact the helpdesk immediately."
}
Msg $env:UserName $Message
exit
Jim Thunder
  • 11
  • 1
  • 5
  • 1
    The right thing to do is to _work with those who answered_ to get their answers to a state that helps both you _and future readers_. I just tested my code, and it works - do tell me in detail what you think the error in the switch statement is - I'm always happy to correct my answer. Based on your question _as asked_, I don't see what key piece is missing. Again, the specifics of the solution that you ultimate need, in real life, eventually, are irrelevant here, and adding additional bits to the code in your answer serves only to confuse. – mklement0 Nov 15 '19 at 17:26
  • 1
    Understood re up-voting: Thank you for trying. Once you reach 15 points, your up-votes will count (I had already up-voted your question). Also thanks for adding an explanation. I understand that your code does what _you_ need it to do, but it doesn't fully match the question anymore (and modifying the question after the fact is ill-advised too, because it can invalidate existing answers) in other words: your answer is _not_ the solution to the question _as asked_ - it is the solution to what you _truly needed_ - the latter is only known to you, and irrelevant, if it wasn't part of the question – mklement0 Nov 15 '19 at 18:47