+ Post New Thread
Page 1 of 2 12 LastLast
Results 1 to 15 of 17
Scripts Thread, Powershell: Not having the same command 5 times just with different variables? in Coding and Web Development; I am terrible with titles explaining my need, so I'll just explain in the body as usual. So we have ...
  1. #1


    Join Date
    Jan 2012
    Posts
    2,614
    Thank Post
    934
    Thanked 350 Times in 266 Posts
    Rep Power
    212

    Powershell: Not having the same command 5 times just with different variables?

    I am terrible with titles explaining my need, so I'll just explain in the body as usual.

    So we have some Emergency Lighting software that generates .RAP files that are just text files containing the results of the last test, such as if any batteries aren't charging or the emergency lamp needs checking, but it generates it as one huge report with no actual sorting - So you'll get Check lamp, check lamp, check lamp, check supply, battery not charging, check lamp, check lamp, battery voltage low, check lamp, you get the picture - this report also includes, jumbled in with it, all the lights that haven't gotten any errors. So I've written a script to do the following:

    • Grab the latest .RAP file, load each line as a separate entry of an array
    • Grab certain lines that are always in the same position (from top and bottom), use them to create a text file ($output, or $body if we're using the automatic emailer)
    • Set a counter at 0. Scan $array[counter] for any of the 5 error messages (which are always on the line after the lights identification)
    • If it finds '* ERROR *', write $array[counter-1] to a new array depending on which error it was
    • Once $count -eq $array.length (so each line has been checked), append the contents of each error array one-after-the-other to $output (so now the errors are in categories)
    • E-mail this file, or open it, depending on which version of the script is run (there are two versions, e-mail runs automatically, file is the manual one)


    Thing is, I'm using the same if command 5 times just with different array names and I can't help but think there's got to be a better way of doing it.. But after spending some considerable time researching and optimising where I can, I still can't figure it out. Anybody with more experience in this care to point me in the right direction?

    Code:
    $reportarray = (Get-Content (Get-ChildItem -File "C:\Comet\report\*.RAP" | Sort LastWriteTime | Select -Last 1))
    $output = "C:\Comet\Lighting Test " + [string](Get-Date).day + "-" + [string](Get-Date).month + "-" + [string](Get-Date).year + ".txt"
    $counter = 0
    $n = "`r`n"
    $reportarray[6..9], $reportarray[-14..-4] | Out-File $output
    while ($counter -ne $reportarray.count) {
    	if ($reportarray[$counter] -match '`* Check emergency lamp `*') {
    		[array]$CELlog += $reportarray[$counter-1]
    		}
    	if ($reportarray[$counter] -match '`*Reading failure - Check supply`*') {
    		[array]$RFCSlog += $reportarray[$counter-1]
    		}
    	if ($reportarray[$counter] -match '`* Battery not charging `*') {
    		[array]$BNClog += $reportarray[$counter-1]
    		}
    	if ($reportarray[$counter] -match '`* Battery capacity low *') {
    		[array]$BCLlog += $reportarray[$counter-1]
    		}
    	if ($reportarray[$counter] -match '`* Battery voltage low `*') {
    		[array]$BVLlog += $reportarray[$counter-1]
    		}
    	$counter ++
    	}
    "$n$n------------- Check Emergency Lamp -------------" | Out-File $output -Append
    if ($CELlog.count -eq 0) {
    	"None" | Out-File $output -Append
    	} else {
    	$CELlog | Out-File $output -Append
    	}
    "$n$n-------- Reading failure - Check supply --------" | Out-File $output -Append
    if ($RFCSlog.count -eq 0) {
    	"None" | Out-File $output -Append
    	} else {
    	$RFCSlog | Out-File $output -Append
    	}
    "$n$n------------- Battery not charging -------------" | Out-File $output -Append
    if ($BNClog.count -eq 0) {
    	"None" | Out-File $output -Append
    	} else {
    	$BNClog | Out-File $output -Append
    	}
    "$n$n------------- Battery capacity low -------------" | Out-File $output -Append
    if ($BCLlog.count -eq 0) {
    	"None" | Out-File $output -Append
    	} else {
    	$BCLlog | Out-File $output -Append
    	}
    "$n$n------------- Battery voltage low --------------" | Out-File $output -Append
    if ($BVLlog.count -eq 0) {
    	"None" | Out-File $output -Append
    	} else {
    	$BVLlog | Out-File $output -Append
    	}
    Invoke-Item $output
    Last edited by Garacesh; 20th November 2013 at 04:08 PM.

  2. #2


    Join Date
    May 2009
    Posts
    3,016
    Thank Post
    268
    Thanked 798 Times in 604 Posts
    Rep Power
    289
    MMmmm. Some quick thoughts :
    Put your criteria in an array. Abstract the compare into a function.
    Do your grouping with a hash of arrays, with the key as the criteria and the error lines as the elements of the array.
    Use a foreach key to loop through the hash and loop through the arrays that are the values to get your output groups.

    I suspect there maybe a more concise ... powershell way.
    Last edited by pcstru; 20th November 2013 at 06:52 PM.

  3. #3


    Join Date
    May 2009
    Posts
    3,016
    Thank Post
    268
    Thanked 798 Times in 604 Posts
    Rep Power
    289
    (a more powershell way)

    ... I think you could use select-string with a regex to get the lines in your 5 if statements while looping through the file. You can pipe that into select object and generate calculated properties to pipe onto to sort-object with the sort based on a made up property which identifies the group. Then you just need to combine that with your header file and spit it out. Max 10 lines including your first 6. I think.

  4. #4

    Join Date
    Mar 2008
    Location
    Surrey
    Posts
    2,168
    Thank Post
    98
    Thanked 319 Times in 261 Posts
    Blog Entries
    4
    Rep Power
    113
    To be honest, I'd get rid of the loop to start with. You don't need it particularly.

    Code:
    [array]$CELlog = $reportarray | Where {$_ -match '`* Check emergency lamp `*'}
    $RFCSlog = $reportarray | Where {$_ -match '`*Reading failure - check supply*`'}
    And so on.

    That should remove a fair chunk. If you'd like to change it around a bit more, you might use a single sys object with noteproperties (eventtype and message maybe) rather than five separate variables. You could then export to CSV, or stick with the Out-File and use the | Where pipeline again.

  5. #5


    Join Date
    Jan 2012
    Posts
    2,614
    Thank Post
    934
    Thanked 350 Times in 266 Posts
    Rep Power
    212
    See, I was thinking of using a {$_ -match ''} (actually, I initially tried using -like.. Whoops!) but couldn't figure it out. I was trying to use ForEach-Object but couldn't figure out how to write it out.

    It's slightly helpful to know I was initially on the right track, at least, even if I did give up eventually xD

    Problem is, that will just get me a tonne of lines full of 'ERROR' but not giving me the actual location of the error (as the error is on the previous line), which is why I felt the need to use a looping counter (hence $reportarray[$counter] and $reportarray[$counter-1])
    Last edited by Garacesh; 21st November 2013 at 10:57 AM.

  6. #6

    Join Date
    Mar 2008
    Location
    Surrey
    Posts
    2,168
    Thank Post
    98
    Thanked 319 Times in 261 Posts
    Blog Entries
    4
    Rep Power
    113
    Quote Originally Posted by Garacesh View Post
    See, I was thinking of using a {$_ -match ''} (actually, I initially tried using -like.. Whoops!) but couldn't figure it out. I was trying to use ForEach-Object but couldn't figure out how to write it out.

    It's slightly helpful to know I was initially on the right track, at least, even if I did give up eventually xD

    Problem is, that will just get me a tonne of lines full of 'ERROR' but not giving me the actual location of the error (as the error is on the previous line), which is why I felt the need to use a looping counter (hence $reportarray[$counter] and $reportarray[$counter-1])
    Ahh, now I see.

    In that case you could maybe populate the starting array like so:

    Code:
    $rawreport = (Get-Content (Get-ChildItem -File "C:\Comet\report\*.RAP" | Sort LastWriteTime | Select -Last 1))
    $reportarray = @()
    
    foreach ($line in $rawreport) {
        $report = New-Object System.Object
        $report | Add-Member -MemberType NoteProperty -Name Error -Value $line
        $foreach.MoveNext()
        $report | Add-Member -MemberType NoteProperty -Name Location $foreach.current
        $reportarray += $report
    }
    Then use as you see fit.

  7. #7


    Join Date
    Jan 2012
    Posts
    2,614
    Thank Post
    934
    Thanked 350 Times in 266 Posts
    Rep Power
    212
    That.. is actually a useful place to start. Unfortunately, there's no "No Error" line, so what happens with that is that sometimes the Error is in the Error column, other times it's in the Location column.

    I might have a crack at working with that though, if for no other reason that educational purposes.

  8. #8

    Join Date
    Mar 2008
    Location
    Surrey
    Posts
    2,168
    Thank Post
    98
    Thanked 319 Times in 261 Posts
    Blog Entries
    4
    Rep Power
    113
    You can, of course, also make the MoveNext conditional on whether the line it's reading is what you expect to find.

    I'm getting into this now, so if you've got a few lines of example input then I'd be happy to have a poke at it.

  9. #9


    Join Date
    Jan 2012
    Posts
    2,614
    Thank Post
    934
    Thanked 350 Times in 266 Posts
    Rep Power
    212
    As much as I dislike 'being given the answer' as it may be, quite frankly I dislike the current state of my code as current.. So have at it I've cut it down because it's all the same and redacted information, but kept the lines in place (otherwise lines 6 to 9 and -14 to -4 would have changed) Probably been a tad over-zealous but m'eh

    Code:
    ----------  
    ----------           
    ----------
    ----------        
    ----------
    
    ****** V2K Status test ALL            ******
    Date: 13-11-2013
    Time: 01:36:08
    
    Site: -----------             
          -----------                         
          -----------                               
          -----------                                
          -----------      
          -----------                      
    
    Ftg.     7152   FAUX ENTRY - GOOD LAMP        OK
    Ftg.     7167   FAUX ENTRY - BAD LAMP    Warning
                    ***** Check emergency lamp *****
    Ftg.    14900   FAUX ENTRY - GOOD LAMP        OK
    Ftg.     7663   FAUX ENTRY - GOOD LAMP        OK
    Ftg.    14387   FAUX ENTRY - GOOD LAMP        OK
    Ftg.    22116   FAUX ENTRY - BAD LAMP    Warning
                    ***** Battery capacity low *****
    Ftg.    17704   FAUX ENTRY - GOOD LAMP        OK
    Ftg.     5166   FAUX ENTRY - GOOD LAMP        OK
    Ftg.    15514   FAUX ENTRY - BAD LAMP    Warning
                    ***** Battery capacity low *****
    Ftg.     8893   FAUX ENTRY - BAD LAMP    Warning
                    ***** Check emergency lamp *****
    Ftg.     5173   FAUX ENTRY - BAD LAMP    Warning
                    ***** Check emergency lamp *****
    Ftg.     8894   FAUX ENTRY - BAD LAMP    Warning
                    ***** Check emergency lamp *****
    Ftg.     8888   FAUX ENTRY - BAD LAMP    Warning
                    ***** Battery capacity low *****
    Ftg.     7163   FAUX ENTRY - GOOD LAMP        OK
    Ftg.    15375   FAUX ENTRY - BAD LAMP      Error
                    *Reading failure - Check supply*
    Ftg.    11914   FAUX ENTRY - GOOD LAMP        OK
    Ftg.    11915   FAUX ENTRY - BAD LAMP    Warning
                    ***** Battery not charging *****
    Ftg.    14899   FAUX ENTRY - GOOD LAMP        OK
    Ftg.    16590   FAUX ENTRY - GOOD LAMP        OK
    Ftg.    14397   FAUX ENTRY - BAD LAMP      Error
                    *Reading failure - Check supply*
    Ftg.    11614   FAUX ENTRY - BAD LAMP    Warning
                    ***** Battery not charging *****
    Ftg.    11612   FAUX ENTRY - BAD LAMP      Error
                    *Reading failure - Check supply*
    Ftg.    11610   FAUX ENTRY - BAD LAMP      Error
                    *Reading failure - Check supply*
    Ftg.    17327   FAUX ENTRY - GOOD LAMP        OK
    Ftg.    11616   FAUX ENTRY - BAD LAMP    Warning
                    ***** Battery capacity low *****
    Ftg.     4819   FAUX ENTRY - GOOD LAMP        OK
    Ftg.     7156   FAUX ENTRY - GOOD LAMP        OK
    Ftg.     4869   FAUX ENTRY - GOOD LAMP        OK
    Ftg.     4861   FAUX ENTRY - BAD LAMP    Warning
                    ***** Check emergency lamp *****
    Ftg.     7154   FAUX ENTRY - GOOD LAMP        OK
    Ftg.     4832   FAUX ENTRY - GOOD LAMP        OK
    Ftg.     4837   FAUX ENTRY - BAD LAMP    Warning
                    ***** Check emergency lamp *****
    Ftg.     7169   FAUX ENTRY - BAD LAMP    Warning
                    ***** Battery not charging *****
    Ftg.     4827   FAUX ENTRY - GOOD LAMP        OK
    Ftg.     9717   FAUX ENTRY - BAD LAMP    Warning
                    ***** Check emergency lamp *****
    Ftg.     8370   FAUX ENTRY - BAD LAMP    Warning
                    ***** Battery capacity low *****
    Ftg.    13650   FAUX ENTRY - GOOD LAMP        OK
    Ftg.    13652   FAUX ENTRY - GOOD LAMP        OK
    
    Luminaire(s) tested  =      591
    Luminaire(s) warning =      134
    Luminaire(s) error   =        6
    
    Battery voltage low  =        0
    Battery not charging =        4
    Battery capacity low =        2
    Check emergency lamp =      128
    Reading failure(s)   =        7
    
    Alarm settings in progress..... ON 
    
    ****** V98.4b ******************************
    Last edited by Garacesh; 21st November 2013 at 02:44 PM.

  10. #10

    Join Date
    Mar 2008
    Location
    Surrey
    Posts
    2,168
    Thank Post
    98
    Thanked 319 Times in 261 Posts
    Blog Entries
    4
    Rep Power
    113
    Just been playing around a bit while killing time, and cooked up the following. Whether it's more elegant or not is a matter of opinion, but I think it at least gives you options and a starting point to turn either this, or another solution, to oyur own purposes. It all goes into an array of custom system objects, and you can then deal with them however you wish. A few Where-Object cmdlets will separate out the different statuses and/or reasons into separate files easily enough.

    Code:
    $report = (Get-Content (Get-ChildItem -File "thefile" | Sort LastWriteTime | Select -Last 1))
    
    $lamps = @()
    
    foreach ($line in $report) {
        $lamp = New-Object System.Object
        $lamp | Add-Member -MemberType NoteProperty -Name ID -Value $null
        $lamp | Add-Member -MemberType NoteProperty -Name Status -Value $null
        $lamp | Add-Member -MemberType NoteProperty -Name Reason -Value $null
    
        switch -wildcard ($line) {
            "*OK" {
                $lamp.ID = $line.Substring(4,12).TrimStart()
                $lamp.Status = "OK"
                $lamps += $lamp
            }
            "*Warning" {
                $lamp.ID = $line.Substring(4,12).TrimStart()
                $lamp.Status = "Warning"
                [void]$foreach.moveNext()
                $lamp.Reason = ($foreach.Current.TrimStart(" *")).TrimEnd("*")
                $lamps += $lamp
            }
            "*Error" {
                $lamp.ID = $line.Substring(4,12).TrimStart()
                $lamp.Status = "Error"
                [void]$foreach.moveNext()
                $lamp.Reason = ($foreach.Current.TrimStart(" *")).TrimEnd("*")
                $lamps += $lamp
            }
        }
    }
    
    $lamps | Sort-Object Status, Reason
    Last edited by jamesb; 21st November 2013 at 04:41 PM.

  11. #11


    Join Date
    Jan 2012
    Posts
    2,614
    Thank Post
    934
    Thanked 350 Times in 266 Posts
    Rep Power
    212
    Well that's certainly a more concise way of doing it, but it's dawned on me I don't actually care (from the logical perspective) about why It's not working, just that it's not working (it's up to the Maintenance guys to care why).. Removing the "*OK" { ... } section would sort it fine, but I figured I'd keep working on it. Learning and all..

    So at first I tried:
    Code:
    foreach ($line in $report) {
        $lamp = New-Object System.Object
        $lamp | Add-Member -MemberType NoteProperty -Name ID -Value $null
        $lamp | Add-Member -MemberType NoteProperty -Name Location -Value $null	
        $lamp | Add-Member -MemberType NoteProperty -Name Error -Value $null
    
        switch -wildcard ($line) {
            !"*  OK" {
    	$lamp.ID = $foreach.Current.Substring(4,12).TrimStart()
    	$lamp.Location = $foreach.Current.Substring(15,22)
    	[void]$foreach.moveNext()
    	$lamp.Error = ($foreach.Current.TrimStart(" *")).TrimEnd("*")
    	$lamps += $lamp
            }
        }
    }
    and variations thereof, but that didn't work. I'm guessing it's the NOT that's throwing it.

    So I tried to rework it a bit, using what I've learnt so far..
    Code:
    $lamps = @()
    $lamp = New-Object System.Object
    $lamp | Add-Member -MemberType NoteProperty -Name ID -Value $null
    $lamp | Add-Member -MemberType NoteProperty -Name Location -Value $null	
    $lamp | Add-Member -MemberType NoteProperty -Name Error -Value $null
    
    $report | ForEach-Object (where -notmatch "*   OK") {
    	$lamp.ID = $foreach.Current.Substring(4,12).TrimStart()
    	$lamp.Location = $foreach.Current.Substring(15,22)
    	[void]$foreach.moveNext()
    	$lamp.Error = ($foreach.Current.TrimStart(" *")).TrimEnd("*")
    	$lamps += $lamp
    }
    Unfortunately, that isn't working either. Keeps throwing up "You cannot call a method on a null-valued expression" per-line (and the subsequent file is full of blank entries to the table) - also I'm guessing that [void]$foreach.moveNext() isn't going to work, either, since I'm only feeding data one-at-a-time due to '| ForEach-Object'

    Am I on the right track, or am I just doing it completely wrong? xD
    Last edited by Garacesh; 22nd November 2013 at 11:04 AM.

  12. #12

    Join Date
    Mar 2008
    Location
    Surrey
    Posts
    2,168
    Thank Post
    98
    Thanked 319 Times in 261 Posts
    Blog Entries
    4
    Rep Power
    113
    You're definitely on the right track - you've also nailed the error. There's a bit of an oddity with foreach and ForEach-Object when used in the pipeline.

    foreach builds the entire dataset before running - hence why you can get away with the [void]$foreach.moveNext() while ForEach-Object passes values one at a time. In a particularly large dataset (gigabytes for example) there's a clear advantage to not holding the whole thing in memory, but in general foreach will give you more options and run substantially faster.

    To be honest I'd just reshuffle the line a little:

    Code:
    foreach ($item in ($report | Where {$_ -notmatch "*OK"})

  13. #13


    Join Date
    Jan 2012
    Posts
    2,614
    Thank Post
    934
    Thanked 350 Times in 266 Posts
    Rep Power
    212
    Ohh, bloody hell. I was trying
    Code:
    foreach ($item in $report) (Where {$_ -notmatch "*OK"})
    foreach ($item in $report Where {$_ -notmatch "*OK"})
    foreach ($item in $report Where -notmatch "*OK")
    and other such variants at one point!

  14. #14

    Join Date
    Mar 2008
    Location
    Surrey
    Posts
    2,168
    Thank Post
    98
    Thanked 319 Times in 261 Posts
    Blog Entries
    4
    Rep Power
    113
    All about the pipeline.

  15. #15


    Join Date
    Jan 2012
    Posts
    2,614
    Thank Post
    934
    Thanked 350 Times in 266 Posts
    Rep Power
    212
    It doesn't seem to like that It doesn't throw any errors in-Editor, so it's formatted properly, but when ran it spams:

    parsing "*OK" - Quantifier {x,y} following nothing. $scriptlocation:14 char:45
    foreach {$item in ($report | Where {$_ -notmatch "*OK"})) {

    This time I have no idea what that error means. (except the :14 char:45 bit)

SHARE:
+ Post New Thread
Page 1 of 2 12 LastLast

Similar Threads

  1. Replies: 1
    Last Post: 21st October 2013, 08:22 PM
  2. Why is GP applying but not at the same time???
    By abillybob in forum Windows Server 2008 R2
    Replies: 21
    Last Post: 5th September 2013, 10:23 AM
  3. iTALC Having the same workstation in multiple Classrooms
    By mitcheldrake in forum Network and Classroom Management
    Replies: 0
    Last Post: 2nd May 2011, 06:22 PM
  4. [Pics] Sometimes I Have The Same Response To Waking Up
    By DaveP in forum Jokes/Interweb Things
    Replies: 0
    Last Post: 18th March 2011, 10:57 AM
  5. Replies: 3
    Last Post: 1st September 2008, 10:30 PM

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •