none
PS Difference v3 to v1 with $Commands1 = ForEach ($PIDArray in $PIDs) {...}, stops at "in" RRS feed

  • Question

  • I have this powershell script I have been running for some time that I have had to update to include functionality. It runs on a 2003x64 Server, but last time I updated it, it was done on XP machine, now I am updating on a Win7 box. The script works fine on my Win7 v3-v4 Powershell, where as it fails on v1 on the ForEach ($Array in <<<<<

    It's a pretty simple script, and I have tried modifying it to exhaustion to get it to work correctly, but can't seem to get it right. Is there a way around this, or do I have the syntax wrong for v1 that I can easily change? The below works fine in v3-v4. I am afraid if it is failing on this ForEach, and I can't resolve, it may fall on others further in the script...

    #JCBIII Kill Process and Send Email Details
    #Ver1 06212009 JCBIII - Find Process running "x" minutes email and kill
    #Ver2 09022014 JCBIII - Match PID with Commandline, kill, email and restart
    
    #Setting Up Mailer
    $emailSmtpServer = "smtpmail.domain.com"
    $emailMessage = New-Object System.Net.Mail.MailMessage
    $emailMessage.From = "test@domain.com"
    $emailMessage.To.Add( "me@domain.com" )
    $emailMessage.Subject = "Notepad - Kill/Restart Script..."
    $emailmessage.IsBodyHTML = $true
    
    #Process Age
    $startTimeLimit = (get-date) - (new-timespan -minutes 5)
    
    #Finding defined process using Age as requirement
    $processes_to_kill = get-process | where {$_.StartTime -lt $startTimeLimit -and ($_.path -like "*notepad.exe") }
    $PIDs = get-process | where {$_.StartTime -lt $startTimeLimit -and ($_.path -like "*notepad.exe") } | select Id -ExpandProperty Id
    $Commands1 = ForEach ($PIDArray in $PIDs) {get-WmiObject Win32_Process -Filter "name = 'notepad.exe'" | where {$_.ProcessId -like $PIDArray} | select CommandLine -ExpandProperty CommandLine }
    
    #Function to email
    if ($processes_to_kill -ne $null)
    {
        ForEach ($Commands in $Commands1)
            {
            $emailMessage.Body = "<b>Notepad Process Restarted: </b><BR>" + $Commands + "`r`n"
            $SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer)
            $SMTPClient.Send( $emailMessage )
            }
    }
    
    #Function to match process with Commandline, kill and restart
    if ($processes_to_kill -ne $null)
    {
        $processes_to_kill | foreach { $_.Kill()}
        ForEach ($Commandx in $Commands1)
            {
            Invoke-Expression "& $Commandx"
            }
    }


    The script fails in the finding defined process piece:

    #Finding defined process using Age as requirement
    $Commands1 = ForEach ($PIDArray in $PIDs) {get-WmiObject Win32_Process -Filter "name = 'notepad.exe'" | where {$_.ProcessId -like $PIDArray} | select CommandLine -ExpandProperty CommandLine }

    Error received is similar to this:

    Unexpected token 'in' in expression or statement.
    At line:x char:xx

    Any help with this is appreciated. (Of course the process names were changed, and email information, but it is all valid for notepad.exe process as well.)



    • Edited by Tutungzone Thursday, September 4, 2014 4:42 PM version catch
    Wednesday, September 3, 2014 8:34 PM

Answers

  • Here is how we could do this if you would prefer using PowerShell over VB adapted to PosH. It is really much easier.  PowerShell eliminates much of the work and is easier to understand.

    #Setting Up Mailer
    $mailprops=@{
        SmtpServer='smtpmail.domain.com'
        From='test@domain.com'
        To='me@domain.com'
        Subject='Notepad - Kill/Restart Script...'
        BodyAsHTML=$true
    }
    
    #Process Age
    $startTime=[DateTime]::Now.AddMinutes(-5)
    
    # Get process to kill and restart with mail message
    Get-Process | 
        Where-Object{$_.StartTime -lt $startTime -and $_.path -like '*notepad.exe' } |
        ForEach-Object{
            $Commandline=(get-WmiObject Win32_Process -Filter "Handle=$($_.ID)").CommandLine
            Write-Host $Commandline -Fore green
    $Body="<b>Notepad Process Restarted: </b><br /> $Commandline" Send-MailMessage @mailprops -Body $body $_.Kill() Invoke-Expression " & $Commandline" }

    Notice how PowerShell uses less code to accomplish the same thing.


    ¯\_(ツ)_/¯







    • Edited by jrv Wednesday, September 3, 2014 11:31 PM
    • Marked as answer by Tutungzone Thursday, September 4, 2014 12:53 AM
    Wednesday, September 3, 2014 11:12 PM

All replies

  • Please don't filter the error.  We need the exact error -all of it with no edits.  So far what you have posted is just not useful.  There is no line "ForEach($Array in"

    ¯\_(ツ)_/¯

    Wednesday, September 3, 2014 10:39 PM
  • Here is how we could do this if you would prefer using PowerShell over VB adapted to PosH. It is really much easier.  PowerShell eliminates much of the work and is easier to understand.

    #Setting Up Mailer
    $mailprops=@{
        SmtpServer='smtpmail.domain.com'
        From='test@domain.com'
        To='me@domain.com'
        Subject='Notepad - Kill/Restart Script...'
        BodyAsHTML=$true
    }
    
    #Process Age
    $startTime=[DateTime]::Now.AddMinutes(-5)
    
    # Get process to kill and restart with mail message
    Get-Process | 
        Where-Object{$_.StartTime -lt $startTime -and $_.path -like '*notepad.exe' } |
        ForEach-Object{
            $Commandline=(get-WmiObject Win32_Process -Filter "Handle=$($_.ID)").CommandLine
            Write-Host $Commandline -Fore green
    $Body="<b>Notepad Process Restarted: </b><br /> $Commandline" Send-MailMessage @mailprops -Body $body $_.Kill() Invoke-Expression " & $Commandline" }

    Notice how PowerShell uses less code to accomplish the same thing.


    ¯\_(ツ)_/¯







    • Edited by jrv Wednesday, September 3, 2014 11:31 PM
    • Marked as answer by Tutungzone Thursday, September 4, 2014 12:53 AM
    Wednesday, September 3, 2014 11:12 PM
  • I guess the old VBS style writing bleeds over sometime... this achieves the same result, in v3 and up. I recognise what you are doing here, but there still is an error, just not in the same place with v2. The error below is the reason I wrote the mailer a little different specific for v2, where as your calls are more suited for v3 PS.

    Is there a way I can get passed this issue using your provided code in v2?

    Unrecognized token in source text.

    At C:\TechScripts\killprocess.ps1:20 char:26
    +         Send-MailMessage @ <<<< mailprops -Body $body

     

    Thursday, September 4, 2014 1:02 AM
  • The title is to ease searches related to the same thing... you have no idea how hard it is to search for array names specific to your process when you are actually looking for something similar.

    If I used my real variables: 

    $Commands1 = ForEach ($PIDArray in $PIDs) {get-WmiObject Win32_Process...
    It will be rare a search will find anything... using Array, or variable in place for actual script naming conventions in subject will help bring more searches here when looking for something similar. 


    Thursday, September 4, 2014 1:08 AM
  • If I change the @ to the variable call from:

     Send-MailMessage @mailprops -Body $body

    To this:

     Send-MailMessage $mailprops -Body $body

    The script continues to do as it is supposed to, but without email, with a new error:

    The term 'Send-MailMessage' is not recognized as a cmdlet, function, operable p
    rogram, or script file. Verify the term and try again.
    At C:\TechScripts\killprocess.ps1:20 char:25
    +         Send-MailMessage  <<<< $mailprops -Body $body

    Thursday, September 4, 2014 1:18 AM
  • Send-MailMessage does not work in PS V2.

    Create a function Send-MailMessage and install it on V2 in your profile.  Now on V2 that function willsend mail and on V3 and later the V3 CmdLet will send mail.

    Learning PowerShell is, at times,  frustrating.  It requires a certain basic understand of computer technology.  Very few people today have this knowledge.  Corporations are importing people fromIndia and CHina because Americans cannot learn the technology.

    PS2 does not hae the same support as PS3 and later.  We can add pseudo functions to PS2 to allow PS3 code to work.

    Todays admins need to learn how this works. It is a fundamental requirement of all enterprises that technicians know how computers, system and programs work.  Scripts are a good way to begin learning this.


    ¯\_(ツ)_/¯

    Thursday, September 4, 2014 1:30 AM
  • I replaced your v3 email string... with my v2 email string (which also works in ALL versions of PowerScript BTW) and now everything works as desired. I appreciate your help:

    #Setting Up Mailer
    $emailSmtpServer = "smtpmail.domain.com"
    $emailMessage = New-Object System.Net.Mail.MailMessage
    $emailMessage.From = "from@domain.com"
    $emailMessage.To.Add( "to@domain.com" )
    $emailMessage.Subject = "Process - Kill/Restart Script..."
    $emailmessage.IsBodyHTML = $true
    
    #Process Age
    $startTime=[DateTime]::Now.AddMinutes(-5)
    
    # Get process to kill and restart with mail message
    Get-Process | 
        Where-Object{$_.StartTime -lt $startTime -and $_.path -like '*notepad.exe' } |
        ForEach-Object{
            $Commandline=(get-WmiObject Win32_Process -Filter "Handle=$($_.ID)").CommandLine
            #Write-Host $Commandline -Fore green
            $emailMessage.Body = "<b>List of running processes killled and restarted: </b><BR>" + $Commandline + "`r`n"
            $SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer)
            $SMTPClient.Send( $emailMessage )
            $_.Kill()
            Invoke-Expression " & $Commandline"
        }

    Thursday, September 4, 2014 1:36 AM
  • 1) Send-MailMessage does not work in PS V2.

    2) Corporations are importing people fromIndia and CHina because Americans cannot learn the technology.

    If you read through the question, it was related to v2, so I am not sure why you would provide a v3 fix. Its ok though... got through it fine.

    As far as the second comment quoted here... American's CAN learn the technology, so this is a bit of a biased statement, and is not needed on these forums. When in reality, there are so many other platforms to write functions like this in, it is a choice or preference to use it. I use many different technologies to write scripts like this. 

    I do thank you for your time and answer though, it got me where I needed to be to finish it.

    Thursday, September 4, 2014 2:04 AM
  • You are right.  I am biased in favor of a world of intelligent people who can think.  Americans have gotten lazy.  They need social media and forums to solve the crisis of the new technology.  They fail to understand the basics.

    I just gave you a perfect example of that.

    I gave a general method for solving a problem that was being solved by pasting code together that made little sense.  In that I used a V2 generalization. 

    There was push-back. I countered with a perfect pattern that challenges the intelligence of the reader.  Can you evaluate the generalization and provide a current and acceptable solution?  You failed to do so.

    The solution is to supply a parallel function so the script will work in all versions and be most flexible.

    SOLUTION:

    Create a Send-MailMessage function and provide it as a global function in the V2 profile.

    Now I realize that this is technically way beyond the average desktop tech.  Solving this may make you a better tech and get you a raise.  Assuming you accept the challenge.  (Mission Impossible)

    Cheers…


    ¯\_(ツ)_/¯

    Thursday, September 4, 2014 2:17 AM
  • Nothing is related to V2. V2 and XP are obsolete.  Get a Timex.

    ¯\_(ツ)_/¯

    Thursday, September 4, 2014 2:19 AM
  • If you read through the question, it was related to v2, so I am not sure why you would provide a v3 fix. Its ok though... got through it fine.

    I recommend you read the title of you own question.

    In case you can't find it: "PS Difference v2 to v3 with $Variable = ForEach ($array in $array2) {...}, stops at "in"

    I notice that you asked about V3.  You guys who learned about arrays in night school need to get with the times.  the Alabama Two-Step is obsolete and so are arrays.

    PowerShell is not about arrays or old thinking.  It is for modern technical people.  Do you homework.


    ¯\_(ツ)_/¯

    Thursday, September 4, 2014 2:24 AM
  • My limited knowledge of adding a function to powershell given the attributes described in our above discussions, I would assume it would be something like:

    Function Global:Send-MailMessage($SmtpServer, $Subject, $Body, $From, $To)
       {
          $emailSmtpServer = $SmtpServer
    	  $emailMessage = New-Object System.Net.Mail.MailMessage
    	  $emailMessage.From = $From
    	  $emailMessage.To.Add($To)
    	  $emailMessage.Subject = $Subject
    	  $emailmessage.IsBodyHTML = $true
    	  $emailMessage.Body = $Body
    	  $SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer)
    	  $SMTPClient.Send( $emailMessage )
    	}  

    Close? I appreciate the learning experience, don't appreciate the demeanor. Interesting the attitude you give to someone asking a simple question, and actually trying to do this, not looking for a free ride... yet get the run around. 

    As far as your comment about XP and v2 being obsolete, I would agree with Xp, not with v2, as it is standard on Win2003 SP2/3 with applications not written for newer OS technology, and is still a currently supported OS.

    I don't wear watches there skippy... but thanks for offering yours. 

    Thursday, September 4, 2014 3:48 AM
  • You are close.  Now make sure the function signature is identical to the Splat or it will fail and add conditional code.

    if($host.Version.Major -le 2){
        function Send-MailMessage{
            Param(
                [string]$SmtpServer,
                [string]$Subject,
                [string]$Body,
                [string]$From, 
                [string]$To,
                [switch]$bodyAsHTML
            )
            
            $msg=New-Object System.Net.Mail.MailMessage
            $msg.From=$From
            $msg.To.Add($To)
            $msg.Subject = $Subject
            $msg.IsBodyHTML=$bodyAsHtml
            $msg.Body=$Body
            $SMTPClient=New-Object System.Net.Mail.SmtpClient($SmtpServer)
            $SMTPClient.Send( $msg )
        }  
    }

    It is better to create a full emulation of the V3 Send-MailMessage with all parameters and place that in the profile.  THen the function will always be available.

    WARNING: WS2003 is obsolete and will no longer be supported in 6 months.

    No matter just try and make scripts work on both for most things by adding support functions where needed.

    No issue but what I tried to show you is to make your approach to PowerShell easier than using those very old and mostly poorly written methods that you originally copied.


    ¯\_(ツ)_/¯

    Thursday, September 4, 2014 8:15 AM
  • Send-MailMessage does not work in PS V2.

    Send-MailMessage first appeared in PowerShell v2. The v2 cmdlet does not, however, support an alternate port number.


    -- Bill Stewart [Bill_Stewart]

    Thursday, September 4, 2014 2:15 PM
    Moderator
  • Send-MailMessage does not work in PS V2.

    Send-MailMessage first appeared in PowerShell v2. The v2 cmdlet does not, however, support an alternate port number.


    -- Bill Stewart [Bill_Stewart]


    Right - I forgot about that.  W2003 probably only has V1 installed and should be upgraded.

    ¯\_(ツ)_/¯

    Thursday, September 4, 2014 2:55 PM
  • My apologies... looks like it is v1:

    PS C:\TechScripts> get-host


    Name             : ConsoleHost
    Version          : 1.0.0.0
    InstanceId       : 9fc4c602-96a6-493c-8b16-c38710fda6df
    UI               : System.Management.Automation.Internal.Host.InternalHostUserI
                       nterface
    CurrentCulture   : en-US
    CurrentUICulture : en-US
    PrivateData      : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy

    --

    The server is a 2003x64 SP2 install... I had thought that 2003 SP2 came with PS v2, guess I was wrong. Can't afford a reboot on this DB server to update WMF at this time, not sure if it is even worth it at this point as we should be cycling these servers out in the next few months.

    JRV... thank you again!

    Thursday, September 4, 2014 4:21 PM
  • I recommend updating.  It will save you many headaches when it is time to upgrade.


    ¯\_(ツ)_/¯

    Thursday, September 4, 2014 4:26 PM
  • True... unless I just include params in my scripts to deal with different versions I presume. 

    function Get-PSVersion {
        if (test-path variable:psversiontable) {$psversiontable.psversion} else {[version]"1.0.0.0"}
    }

    Or something similiar, if I remember right I can specify a specific version for the script as well... formatting may be wrong but something like:

    #Requires –Version 2.0
    Either way, I will definitely consider during the patch reboot next week. 

    Thursday, September 4, 2014 4:38 PM