Filter processes by module's FileName - powershell

If I need to filter processes by a module's FileName, the following code does the job:
Get-Process | where { $_.Modules.FileName -eq "xxx\yyy.dll”) }
But if I need to filter modules by FileName starting with a string, the following code doesn't seem to work:
Get-Process | where { $_.Modules.FileName.StartsWith("xxx\yyy.dll”)) }
As result, I see all the processes in the output. I'm very confused why filtering doesn't seem to work in case of StartsWith

The member modules might be a collection. Thus it needs to be iterated too. Like so,
(get-process) | % {
if($_.modules -ne $null) { # No modules, no action
$_.modules | ? { $_.filename.tolower().startswith("c:\program") }
}
}
As for the question, there are actually two iterations. Let's use explicit variables instead of pipelining and printing the acutal module files. Passing multiple $_s around is not easy to read syntax anyway. Like so,
foreach ($p in get-process) {
if ($p.modules -ne $null){
write-host $p.id $p.ProcessName
foreach($m in $p.modules){
if ($m.filename.tolower().startswith("c:\program") ) {
write-host `t $m.moduleName $m.FileName # ` markdown bug
}
}
write-host
}
}

Related

Powershell Different color in strings with echo in array

I trying to write a script, which show iis pools state with a different color.
And I can't understand why script coloring in one color all strings when I use echo.
Here script:
$pools = invoke-command -session $session -scriptblock {Import-Module WebAdministration; Get-ChildItem IIS:\AppPools | Where {$_.Name -like "*abc*"}}
$poolsShow = $pools | Select-Object -Property name, state
$poolsShow | ForEach-Object {
if($_.state -eq "Started") {
$Host.UI.RawUI.ForegroundColor = "Green";
echo $_;
[Console]::ResetColor();
}
if($_.state -eq "Stopped") {
$Host.UI.RawUI.ForegroundColor = "Red";
echo $_;
[Console]::ResetColor();
}
}
It is work if I go through the $pools, but if I select name and state via Select-Object - all strings are coloring in the color of the last service.
I have tried via Write-Host - and it's worked, but I didn't find a way, how to format output in one table with a headers only at first line and with the same width in every string.
You can take a similar approach as the one proposed in this answer, the only difference would be that the ANSI Escape Sequences are prepended to the property values of the objects created by Select-Object. Helpful answer provided by #mklement0 in the same question provides more details on this.
function status {
$ansi = switch($args[0]) {
Stopped { "$([char] 27)[91m" }
Started { "$([char] 27)[32m" }
}
$ansi + $args[0]
}
Invoke-Command -Session $session -ScriptBlock {
Import-Module WebAdministration
Get-ChildItem IIS:\AppPools | Where-Object { $_.Name -like "*abc*" }
} | Select-Object Name, #{ N='Status'; E={ status $_.State }}
A demo using custom objects:
0..5 | ForEach-Object {
[pscustomobject]#{
Name = "Test $_"
Status = ('Started', 'Stopped')[$_ % 2]
}
} | Select-Object Name, #{ N='Status'; E={ status $_.Status }}
To complement Santiago's helpful answer:
As for what you tried:
echo in PowerShell is a built-in alias for Write-Output, which does not print directly to the console - instead, it prints to the success output stream.
If the success output stream isn't captured or redirected in a given command, it is eventually printed to the console, after undergoing for-display formatting by PowerShell's formatting system.
Because your output objects have 4 or fewer properties, PowerShell applies tabular formatting by default; that is, the Format-Table cmdlet is implicitly used, which has a perhaps surprising implication:
So as to allow determining suitable column widths for the table, a 300-millisecond delay is introduced during which the objects are internally cached and analyzed.
While this behavior is helpful in principle, it has surprising side effects, notably in that direct-to-host output and output from other streams then can appear out of order; a simple example: [pscustomobject] #{ foo = 1 }; Write-Host 'Why am I printing first?? - see this answer for background information.
Therefore, the formatted table's rows only started printing after that delay, so your attempt to control their color one by one with ForEach-Object was ineffective.
As an aside: In PowerShell (Core) 7.2+ there's an additional consideration: formatted output now applies its own coloring by default, as controlled by the .OutputRendering property of the $PSStyle preference variable.
Santiago's answer bypasses this problem by using a calculated property to color individual property values rather than trying to control the coloring of the already-formatted representation of the object.
If you want a prepackaged, general-purpose solution, you can use the Out-HostColored function from this Gist (authored by me), which in your case would make the solution as simple as piping your objects to Out-HostColored.ps1 #{ Started = 'Green'; Stopped = 'Red' }:
# Download and define function `Out-HostColored` on demand (will prompt).
# To be safe, inspect the source code at the specified URL first.
if (-not (Get-Command -ErrorAction Ignore Out-HostColored1)) {
$gistUrl = 'https://gist.github.com/mklement0/243ea8297e7db0e1c03a67ce4b1e765d/raw/Out-HostColored.ps1'
if ((Read-Host "`n====`n OK to download and define function ``Out-HostColored```n from Gist ${gistUrl}?`n=====`n(y/n)?").Trim() -notin 'y', 'yes') { Write-Warning 'Aborted.'; exit 2 }
Invoke-RestMethod $gistUrl | Invoke-Expression 3>$null
if (-not ${function:Out-HostColored}) { exit 2 }
}
# Emit sample objects and color parts of their formatted representation
# based on regex patterns.
0..5 | ForEach-Object {
[pscustomobject]#{
Name = "Test $_"
State = ('Started', 'Stopped')[$_ % 2]
}
} |
Out-HostColored.ps1 #{ Started = 'Green'; Stopped = 'Red' }
Output:
Add -WholeLine if you want to color matching lines in full.
The hashtable maps search text patterns to colors.
Whenever a pattern is found in the formatted representations of the input objects, it is colored using the specified color.
Note that this means that finding what to color is purely based on string parsing, not on OOP features (such as checking specific properties).
Note that the hashtable keys are regexes by default, unless you also specify
-SimpleMatch.
Thus you could easily make the above more robust, if needed, such as replacing Started = with '\bStarted\b' = in order to only match full words.

Powershell Pipe to Switch Statement to format a phone number

I am attempting to query AD to get the phone number for a user then format it to a standard format (###-####). I'm using a Switch statement because I've seen the number in a handful of different formats. With the way I have my code set up though I am getting an error: "The term Switch is not recognized as a name of a cmdlet, function, script file..."
Here's the code:
$ADInfo = Get-ADUser $User.Email.split("#")[0] -Properties * -Server $DC
$User.'Phone Number' = $ADInfo.telephoneNumber | Switch -regex ($_) {
'^\d{5}$'
{
"{0:38#-####}" -f $_
break
}
'^\d{7}$'
{
"{0:###-####}" -f $_
break
}
default
{
break
}
}
Am I misunderstanding how the pipeline works? I suppose I could just save this info to a temp variable and then enter a Switch statement but this seemed like an effective way to use the pipeline.
Anyhow, any help is appreciated! Thanks!
Am I misunderstanding how the pipeline works?
Yes.
The pipeline can only pipe stuff to commands - and switch is not a command, it's a language keyword.
You can wrap your switch statement in a ForEach-Object block and pipe the input to that:
$User.'Phone Number' = $ADInfo.telephoneNumber | ForEach-Object {
Switch -regex ($_) {
'^\d{5}$'
{
"{0:38#-####}" -f $_
}
'^\d{7}$'
{
"{0:###-####}" -f $_
}
}
}
As Ansgar points out, the break statements here are redundant (and not required), since your test cases are mutually exclusive
As suggested by Mathias R. Jessen, here's an example of how to use Switch without having to iterate through things with a ForEach loop, or piping anything to it.
Switch will iterate an array on its own, so there's no need to nest it in a loop, or even pipe anything in this case. It can be done as such:
$ADInfo = Get-ADUser $User.Email.split("#")[0] -Properties * -Server $DC
$User.'Phone Number' = Switch -regex ($ADInfo.telephoneNumber) {
'^\d{5}$'
{
"{0:38#-####}" -f $_
}
'^\d{7}$'
{
"{0:###-####}" -f $_
}
}
Aside from that I would recommend using Continue instead of Break. Further example of when continue would be used within the Switch scriptblock:
$TestData = #('John.Doe#Company.Com','JDoe','(555)867-5309','Taco Tuesday')
Switch -regex ($TestData){
'#' {"$_ is an email address.";continue}
'^[+()\-\d\s]+$' {"$_ is a phone number.";continue}
default {"$_ is unknown."}
}
Here we have something being done in the default block, so we want to include a continue statement in the previous cases so that if a match is found it moves to the next item in the array, and does not execute the default case. The output of that would be:
John.Doe#Company.com is an email address.
JDoe is unknown.
(555)867-5309 is a phone number.
Taco Tuesday is unknown.

Powershell: idiomatic way of mutating items passing through a stream?

When processing an input stream it's sometimes necessary to effect changes on the objects passing through the stream. It's also useful to allow those objects to pass out the other end so that they may be piped into other processes. Is there a more idiomatic/concise way than this to mutate and pass along?
$input | % {
if ($_.Office.length -ne 11) { $_.Errors += "Bad office" }
$_ #needed to allow additional piping
}
Some other operator? Something like... %!
$input | %! {
if ($_.Office.length -ne 11) { $_.Errors += "Bad office" }
} #whatever goes in must come out
I don't see anything wrong with the first example there, but you could write your own function and then use that:
function Do-Modification {
[CmdletBinding()]
param(
[Parameter(
ValueFromPipeline=$true
)]
$myObject
)
Process {
if ($myObject.Office.length -ne 11) {
$myObject.Errors += "Bad Office"
}
$myObject
}
}
$inputs | Do-Modification
# or to use it in a larger ForEach-Object (even though it's redundant here)
$inputs | ForEach-Object {
$_ | Do-Modification
}
Edit to add:
This is a contrived example; it's probably not worth it to reinvent the wheel (writing your own pipeline function) except for code re-use (where it is very much worth it in my opinion). It's up to you to decide where one approach or the other makes sense.

Exiting the function while in loops like Foreach-Object in PowerShell

I have a function like this in Powershell:
function F()
{
$something | Foreach-Object {
if ($_ -eq "foo"){
# Exit from F here
}
}
# do other stuff
}
if I use Exit in the if statement, it exits powershell, I don't want this behavior. If I use return in the if statement, foreach keeps executing and the rest of the function is also executed. I came up with this:
function F()
{
$failed = $false
$something | Foreach-Object {
if ($_ -eq "foo"){
$failed = $true
break
}
}
if ($failed){
return
}
# do other stuff
}
I basically introduced a sentinel variable holding if I broke out of the loop or not. Is there a cleaner solution?
Any help?
function F()
{
Trap { Return }
$something |
Foreach-Object {
if ($_ -eq "foo"){ Throw }
else {$_}
}
}
$something = "a","b","c","foo","d","e"
F
'Do other stuff'
a
b
c
Do other stuff
I'm not entirely sure of your specific requirements, but I think you can simplify this by looking at it a different way. It looks like you just want to know if any $something == "foo" in which case this would make things a lot easier:
if($something ? {$_ -eq 'foo')) { return }
? is an alias for Where-Object. The downside to this is that it will iterate over every item in the array even after finding a match, so...
If you're indeed searching a string array, things can get even simpler:
if($something -Contains 'foo') { return }
If the array is more costly to iterate over, you might consider implementing an equivalent of the LINQ "Any" extension method in Powershell which would allow you to do:
if($something | Test-Any {$_ -eq 'foo'}) { return }
As an aside, while exceptions in the CLR aren't that costly, using them to direct procedural flow is an anti-pattern as it can lead to code that's hard to follow, or, put formally, it violates the principal of least surprise.

Is it possible to terminate or stop a PowerShell pipeline from within a filter

I have written a simple PowerShell filter that pushes the current object down the pipeline if its date is between the specified begin and end date. The objects coming down the pipeline are always in ascending date order so as soon as the date exceeds the specified end date I know my work is done and I would like to let tell the pipeline that the upstream commands can abandon their work so that the pipeline can finish its work. I am reading some very large log files and I will frequently want to examine just a portion of the log. I am pretty sure this is not possible but I wanted to ask to be sure.
It is possible to break a pipeline with anything that would otherwise break an outside loop or halt script execution altogether (like throwing an exception). The solution then is to wrap the pipeline in a loop that you can break if you need to stop the pipeline. For example, the below code will return the first item from the pipeline and then break the pipeline by breaking the outside do-while loop:
do {
Get-ChildItem|% { $_;break }
} while ($false)
This functionality can be wrapped into a function like this, where the last line accomplishes the same thing as above:
function Breakable-Pipeline([ScriptBlock]$ScriptBlock) {
do {
. $ScriptBlock
} while ($false)
}
Breakable-Pipeline { Get-ChildItem|% { $_;break } }
It is not possible to stop an upstream command from a downstream command.. it will continue to filter out objects that do not match your criteria, but the first command will process everything it was set to process.
The workaround will be to do more filtering on the upstream cmdlet or function/filter. Working with log files makes it a bit more comoplicated, but perhaps using Select-String and a regular expression to filter out the undesired dates might work for you.
Unless you know how many lines you want to take and from where, the whole file will be read to check for the pattern.
You can throw an exception when ending the pipeline.
gc demo.txt -ReadCount 1 | %{$num=0}{$num++; if($num -eq 5){throw "terminated pipeline!"}else{write-host $_}}
or
Look at this post about how to terminate a pipeline: https://web.archive.org/web/20160829015320/http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx
Not sure about your exact needs, but it may be worth your time to look at Log Parser to see if you can't use a query to filter the data before it even hits the pipe.
If you're willing to use non-public members here is a way to stop the pipeline. It mimics what select-object does. invoke-method (alias im) is a function to invoke non-public methods. select-property (alias selp) is a function to select (similar to select-object) non-public properties - however it automatically acts like -ExpandProperty if only one matching property is found. (I wrote select-property and invoke-method at work, so can't share the source code of those).
# Get the system.management.automation assembly
$script:smaa=[appdomain]::currentdomain.getassemblies()|
? location -like "*system.management.automation*"
# Get the StopUpstreamCommandsException class
$script:upcet=$smaa.gettypes()| ? name -like "*StopUpstreamCommandsException *"
function stop-pipeline {
# Create a StopUpstreamCommandsException
$upce = [activator]::CreateInstance($upcet,#($pscmdlet))
$PipelineProcessor=$pscmdlet.CommandRuntime|select-property PipelineProcessor
$commands = $PipelineProcessor|select-property commands
$commandProcessor= $commands[0]
$ci = $commandProcessor|select-property commandinfo
$upce.RequestingCommandProcessor | im set_commandinfo #($ci)
$cr = $commandProcessor|select-property commandruntime
$upce.RequestingCommandProcessor| im set_commandruntime #($cr)
$null = $PipelineProcessor|
invoke-method recordfailure #($upce, $commandProcessor.command)
if ($commands.count -gt 1) {
$doCompletes = #()
1..($commands.count-1) | % {
write-debug "Stop-pipeline: added DoComplete for $($commands[$_])"
$doCompletes += $commands[$_] | invoke-method DoComplete -returnClosure
}
foreach ($DoComplete in $doCompletes) {
$null = & $DoComplete
}
}
throw $upce
}
EDIT: per mklement0's comment:
Here is a link to the Nivot ink blog on a script on the "poke" module which similarly gives access to non-public members.
As far as additional comments, I don't have meaningful ones at this point. This code just mimics what a decompilation of select-object reveals. The original MS comments (if any) are of course not in the decompilation. Frankly I don't know the purpose of the various types the function uses. Getting that level of understanding would likely require a considerable amount of effort.
My suggestion: get Oisin's poke module. Tweak the code to use that module. And then try it out. If you like the way it works, then use it and don't worry how it works (that's what I did).
Note: I haven't studied "poke" in any depth, but my guess is that it doesn't have anything like -returnClosure. However adding that should be easy as this:
if (-not $returnClosure) {
$methodInfo.Invoke($arguments)
} else {
{$methodInfo.Invoke($arguments)}.GetNewClosure()
}
Here's an - imperfect - implementation of a Stop-Pipeline cmdlet (requires PS v3+), gratefully adapted from this answer:
#requires -version 3
Filter Stop-Pipeline {
$sp = { Select-Object -First 1 }.GetSteppablePipeline($MyInvocation.CommandOrigin)
$sp.Begin($true)
$sp.Process(0)
}
# Example
1..5 | % { if ($_ -gt 2) { Stop-Pipeline }; $_ } # -> 1, 2
Caveat: I don't fully understand how it works, though fundamentally it takes advantage of Select -First's ability to stop the pipeline prematurely (PS v3+). However, in this case there is one crucial difference to how Select -First terminates the pipeline: downstream cmdlets (commands later in the pipeline) do not get a chance to run their end blocks.
Therefore, aggregating cmdlets (those that must receive all input before producing output, such as Sort-Object, Group-Object, and Measure-Object) will not produce output if placed later in the same pipeline; e.g.:
# !! NO output, because Sort-Object never finishes.
1..5 | % { if ($_ -gt 2) { Stop-Pipeline }; $_ } | Sort-Object
Background info that may lead to a better solution:
Thanks to PetSerAl, my answer here shows how to produce the same exception that Select-Object -First uses internally to stop upstream cmdlets.
However, there the exception is thrown from inside the cmdlet that is itself connected to the pipeline to stop, which is not the case here:
Stop-Pipeline, as used in the examples above, is not connected to the pipeline that should be stopped (only the enclosing ForEach-Object (%) block is), so the question is: How can the exception be thrown in the context of the target pipeline?
Try these filters, they'll force the pipeline to stop after the first object -or the first n elements- and store it -them- in a variable; you need to pass the name of the variable, if you don't the object(s) are pushed out but cannot be assigned to a variable.
filter FirstObject ([string]$vName = '') {
if ($vName) {sv $vName $_ -s 1} else {$_}
break
}
filter FirstElements ([int]$max = 2, [string]$vName = '') {
if ($max -le 0) {break} else {$_arr += ,$_}
if (!--$max) {
if ($vName) {sv $vName $_arr -s 1} else {$_arr}
break
}
}
# can't assign to a variable directly
$myLog = get-eventLog security | ... | firstObject
# pass the the varName
get-eventLog security | ... | firstObject myLog
$myLog
# can't assign to a variable directly
$myLogs = get-eventLog security | ... | firstElements 3
# pass the number of elements and the varName
get-eventLog security | ... | firstElements 3 myLogs
$myLogs
####################################
get-eventLog security | % {
if ($_.timegenerated -lt (date 11.09.08) -and`
$_.timegenerated -gt (date 11.01.08)) {$log1 = $_; break}
}
#
$log1
Another option would be to use the -file parameter on a switch statement. Using -file will read the file one line at a time, and you can use break to exit immediately without reading the rest of the file.
switch -file $someFile {
# Parse current line for later matches.
{ $script:line = [DateTime]$_ } { }
# If less than min date, keep looking.
{ $line -lt $minDate } { Write-Host "skipping: $line"; continue }
# If greater than max date, stop checking.
{ $line -gt $maxDate } { Write-Host "stopping: $line"; break }
# Otherwise, date is between min and max.
default { Write-Host "match: $line" }
}