Filter the output of a command as if it was text - powershell

I have a simple question, but I am also a beginner in PowerShell. I think it has to do with the fact that the output of the Get-Process command (alias ps) is objects and not text.
I want to get a list of the services running that have the name "sql" in them.
This is what I tried so far, but every attempt returns nothing:
Get-Service | where {$_ -match 'sql'}
Get-Service | where {$_ -like 'sql'}
Get-Service | Select-String sql
I am looking for a pattern that lets me treat the output of every command as searchable text.

Just forget it :o)
Outputs are objects. You are right, and you are going to use this.
So mjolinor has the shortest answer, but for your knowledge just test:
Get-Service | Get-Member
So you will understand that
Get-Service | Where-Object {$_.name -match ".*sql.*" }
also works, and there you've got your text as a property of the object.

Most answers here focus on finding the service name with "sql" in the name, not on filtering the entire output as if it was text. Also, the accepted answer uses a non-PowerShell function, "findstr".
So, granted, what follows is not the most elegant solution, but for sake of completeness I would like to provide the 100% PowerShell solution that takes the question of the OP literally:
(get-Service | Out-String) -split "`r`n" | Select-String sql
We need Out-String, because using the solutions provided in other answers doesn't provide us the full text output of the Get-Service command, only the Name parameter.
We need to split on newlines, because Select-String seems to treat the entire text as one long string, and returns it as a whole, if "sql" is found in it.
I use Select-String instead of findstr, because findstr is not a PowerShell function.
This is a purist answer, and in practice, for this specific use-case, I would not recommend it. But for people coming here through Google Search based on the question title, this is a more accurate answer...

Get-Service | Select-String -Pattern "sql"
This works just like grep. And you can even sort:
Get-Service | Select-String -Pattern "sql" | sort

The other answers are right of course about your specific question of starting services that have "sql" in their name, but to answer the generic question:
You can do Get-Service | Out-String, and you will get the output as string, much like how Unix commands work.
Also when the output is piped to non-PowerShell commands, it does get converted to text, so for example: Get-Service | grep sql would work the way you wanted.
But again, like #JPBlanc says, it is good embrace the way PowerShell works, which is that the outputs are objects. It gives you way more control and keeps things simple and readable (the Unix commands with sed, awk and what not operating on text output of other command outputs can get very cryptic!).

You're working way too hard at it:
Get-Service *sql*

If anyone wants more information on logical operations, please see Using the Where-Object Cmdlet:
• -lt -- Less than
• -le -- Less than or equal to
• -gt -- Greater than
• -ge -- Greater than or equal to
• -eq -- Equal to
• -ne -- Not equal to
• -like - Like; uses wildcards for pattern matching
Get-Service | where {$_ -match 'sql'} would be Get-Service | where {$_ -eq "sql"}
Get-Service | where {$_ -like 'sql'} would be Get-Service | where {$_ -like "sql"}
And now an actual example.
PS C:\> Get-Service | where {$_.name -like "net*"}
Status Name DisplayName
------ ---- -----------
Running Net Driver HPZ12 Net Driver HPZ12
Running Netlogon Netlogon

That the text of the name is a property of the object is important to get your head around, and how to use the property values in a filter.
Another aspect of PowerShell you can leverage to solve this is selecting properties out of objects with Select-Object (alias select):
Get-Service | select -expand name
will get you a string array with the names of the servers, and two of your original three filters would work on that. The -like isn't going to work, because there's no wildcards in the test string. The only thing it will ever match is just 'sql'.
I still believe the first solution I posted is best. It's important to know how to do late filtering, but also how to use early filtering when you can.

If you want to list all services with "sql" in the service name, just use:
get-service -name *sql*

You probably want this:
Function Select-ObjectPropertyValues {
param(
[Parameter(Mandatory=$true, Position=0)]
[String]
$Pattern,
[Parameter(ValueFromPipeline)]
$input
)
$input | Where-Object {($_.PSObject.Properties | Where-Object {$_.Value -match $Pattern} | Measure-Object).count -gt 0} | Write-Output
}
Here we are going though each property of an object to see if it matches the given pattern. If the object contains one or more such properties, we write it out. End result: grep by all properties of an object.
Put it in your configuration files and grep to your heart's content.

how about:
Get-Service| Out-String -stream | Select-String sql
where the key point is that -stream option converts the Out-String output in separate lines of text.

Related

grep gci output in powershell

I am trying to determine if some environment variables are set (for postgres environment). They usually start with PG. (E.g. PGUSER, PGPASSWORD, etc). The following command does output it. (Provided I set it previously).
gci env:* | sort name | more
To eliminate the scrolling I tried the following:
gci env:* | sort name | select-string "PG"
This doesn't return anything. What am I doing wrong here?
Edit: the alternative I have for now:
gci env:* | sort name | % { $var = $_.Name + ":" + $_.Value; Write-Output $var } | select-string "PG"
There must be a better alternative.
You're using the wrong mindset. Don't try to work with PowerShell like everything is a string. That's Unix-like thinking, and it's going to work as well as driving nails with a screwdiver. You need to switch to object-oriented thinking because in PowerShell you're working with objects 99% of the time.
Generally, you would just do this for something as simple as what you're looking for:
Get-ChildItem Env:PG* | Sort-Object -Property Name
If the globbing that Get-ChildItem supports doesn't work, you would want to use Where-Object with the -like operator which is similar globbing to what Get-ChildItem can do:
Get-ChildItem Env:* | Where-Object Name -like 'PG*' | Sort-Object -Property Name
If you need to search values, you can do it like this:
Get-ChildItem Env:* | Where-Object Value -like 'PG*' | Sort-Object -Property Name
And if you want to do both, you'd use the full synax of Where-Object:
Get-ChildItem Env:* | Where-Object { $_.Name -like 'PG*' -or $_.Value -like 'PG*' } | Sort-Object -Property Name
Or you can use the -match operator, which lets you specify a .Net regular expression:
Get-ChildItem Env:* | Where-Object Name -match '^PG' | Sort-Object -Property Name
Or if you know exactly what you're looking for:
$Vars = 'PGUSER', 'PGPASSWORD'
Get-ChildItem Env:* | Where-Object Name -in $Vars | Sort-Object -Property Name
Remembering, of course, that PowerShell is usually case-insensitive. You can specify -clike, -cmatch, -cin, etc. if you want case-sensitive operators.
Alternately, you can use the $env: automatic variable namespace.
if ($null -eq $env:PGUSER) { 'Not set' }
See also Get-Help about_Environment_Variables.
Beware that setting environment variables permanently is not exactly self-evident. It's described briefly in the above link, but the bottom line is that you have to call [System.Environment]::SetEnvironmentVariable(), which you can find documented here. In Windows land, environment variables are basically legacy features with the exception of Windows OS level variables (like PATH) so they're no longer supported like you might expect.
Your approach to how this command should work and your instinct that there has to be a better alternative is exactly correct. This is quite a frustrating issue in my mind and I also asked a variation on this question a few days back.
Select-String only handles strings and what you are passing to it in the above is not a string, so it returns nothing. Obviously, you might think that since Select-String requires a string, that it would implicitly change it into a string, but no. So the next thing to consider is to change it to a string, but that creates even more confusion.
gci env:* | sort name | out-string | select-string "Pro"
So now you just get everything returned. What's happening here is that out-string returns all lines as a single string, so if there is any hit for "Pro" you get everything returned.
What you need to do is to use out-string -stream which splits the string up by linebreaks so that you get a string per line, and only then do you get rational output.
gci env:* | sort name | out-string -stream | select-string "Pro"
More on this here: Using PowerShell sls (Select-String) vs grep vs findstr. The github request linked to in there is trying to change the functionality so that select-string will implicitly have an out-string -stream in the background so that your original command will work.
Often we need strings to output results and there is nothing wrong with wanting to manipulate strings (in fact, it depends what you need of course - if you need to do further object manipulations, keep it as an object for that, but if you just need the string output, you should not have to jump through hoops to get that!). If you use a string-manipulation tool like select-string then PowerShell should at least convert the incoming information to a string to provide meaningful output. Compare with findstr: if you pipe the above to findstr.exe, exactly that will happen and it will implicitly convert with | out-string -stream for findstr (and all other external / non-PowerShell programs) and so gci env:* | findstr "Pro" (on a PowerShell console!) gives you rational output. select-string is a string-manipulation tool so I find the idea that people are not thinking right about it for expecting a string-manipulation tool to manipulate the incoming information as a string to be unfair on users. PowerShell is an incredibly versatile language but I think this is a common area of confusion. Hopefully, future versions of select-string will operate in the above fashion as a result of the change request on GitHub, but in the meantime, just use | out-string -stream and it will all work as expected, including for other string manipulations which you can then deal with easily:
(gci env:* | sort name | out-string -stream) -replace "Pro", "XXX" | select-String "XXX"
to keep this short: Your approach doesn't work in PowerShell. All you need to do is
# Short Version
gci env: | ? Name -match "PG" | sort Name
# Long Version
Get-ChildItem -Path env: |
Where-Object -FilterScript { $_.Name -match "PG" } |
Sort-Object -Property Name
Select-String works fine with string content piped one by one instead of a big stream.
Cheers

Where-Object, Select-Object and ForEach-object - Differences and Usage

Where-Object, Select-Object and ForEach-Object
I am a PowerShell beginner. I don't understand too much. Can someone give examples to illustrate the differences and usage scenarios between them?
If you are at all familiar with either LINQ or SQL then it should be much easier to understand because it uses the same concepts for the same words with a slight tweak.
Where-Object
is used for filtering out objects from the pipeline and is similar to how SQL filters rows. Here, objects are compared against a condition, or optionally a ScriptBlock, to determine whether it should be passed on to the next cmdlet in the pipeline. To demonstrate:
# Approved Verbs
Get-Verb | Measure-Object # count of 98
Get-Verb | Where-Object Verb -Like G* | Measure-Object # 3
# Integers 1 to 100
1..100 | Measure-Object # count of 100
1..100 | Where-Object {$_ -LT 50} | Measure-Object # count of 49
This syntax is usually the most readable when not using a ScriptBlock, but is necessary if you want to refer to the object itself (not a property) or for more complicated boolean results. Note: many resources will recommend (as #Iftimie Tudor mentions) trying to filter sooner (more left) in the pipeline for performance benefits.
Select-Object
is used for filtering properties of an object and is similar to how SQL filters columns. Importantly, it transforms the pipeline object into a new PSCustomObject that only has the requested properties with the object's values copied. To demonstrate:
Get-Process
Get-Process | Select-Object Name,CPU
Note, though, that this is only the standard usage. Explore its parameter sets using Get-Help Select-Object where it has similar row-like filtering capabilities like only getting the first n objects from the pipeline (aka, Get-Process | Select-Object -First 3) that continue onto the next cmdlet.
ForEach-Object
is like your foreach loops in other languages, with its own important flavour. In fact, PowerShell also has a foreach loop of its own! These may be easily confused but are operationally quite different. The main visual difference is that the foreach loop cannot be used in a pipeline, but ForEach-Object can. The latter, ForEach-Object, is a cmdlet (foreach is not) and can be used for transforming the current pipeline or for running a segment of code against the pipeline. It is really the most flexible cmdlet there is.
The best way to think about it is that it is the body of a loop, where the current element, $_, is coming from the pipeline and any output is passed onto the next cmdlet. To demonstrate:
# Transform
Get-Verb | ForEach-Object {"$($_.Verb) comes from the group $($_.Group)"}
# Retrieve Property
Get-Verb | ForEach-Object Verb
# Call Method
Get-Verb | ForEach-Object GetType
# Run Code
1..100 | ForEach-Object {
$increment = $_ + 1
$multiplied = $increment * 3
Write-Output $multiplied
}
Edit (Feb, 2023): thanks to #IkemKrueger for a missing }.
You have two things in there: filtering and iterating through a collection.
Filtering:
principle: Always use filtering left as much as possible. These two commands do the same thing, but the second one won't transmit a huge chunk of data through the pipe (or network):
Get-Process | where-Object {$_.Name -like 'chrome'} | Export-Csv
'c:\temp\processes.csv'
Get-Process -Name chrome | Export-Csv c:\temp\processes.csv
This is great when working with huge lists of computers or big files.
Many commandlets have their own filtering capabilities. Run get Get-Help get-process -full to see what they offer before piping.
iterating through collections:
Here you have 3 possibilities:
batch cmdlets is commandlet built in capability of passing a collection to another commandlet:
Get-Service -name BITS,Spooler,W32Time | Set-Service -startuptype
Automatic
WMI methods - WMI uses it's own way of doing the first one (different syntax)
gwmi win32_networkadapterconfiguration -filter "description like
'%intel%'" | EnableDHCP()
enumerating objects - iterating through the list:
Get-WmiObject Win32_Service -filter "name = 'BITS'" | ForEach-Object
-process { $_.change($null,$null,$null,$null,$null,$null,$null,"P#ssw0rd") }
Credits:
I found explanations that cleared the mess in my head around all these things in a book called : Learn Powershell in a month of lunches (chapters 9 and 13 in this case)

Using PowerShell how to I split the string of a selected property

I am very new to PowerShell and I can't seem to find a solution to the the question about splitting my selected properties value in powershell which works for me.
My current code without the split is:
((get-Acl 'c:\temp').Access | where {$_.IdentityReference -like '*\*'} | Select IdentityReference
The purpose is to get a list of users who have access to a folder.
the results give me the domain and the user.
Domain\username
and I just want the username as it will be used in a further SQL query to look up details.
so I figured the best way to do it was to split the returned string on the '\' and take the 2nd value of the array it creates.
so far I am not getting any results back.
You can create custom results with Select-Object:
(get-Acl 'c:\temp').Access | where {$_.IdentityReference -like '*\*'} | Select #{n='User'; e={$_.IdentityReference.Split('\')[1]}}
In PSv3+ you can take further advantage of member-access enumeration, combined with Split-Path -Leaf (which, as in WayneA's answer, is repurposed to extract the last \-separated component from a <realm>\<username> token):
(Get-Acl c:\temp).Access.IdentityReference | Split-Path -Leaf
Note that I've omitted the where {$_.IdentityReference -like '*\*'} filter, because - as far as I can tell - all .IdentifyReference match this pattern (with the first \-based token either being a domain name, a machine name, or a literal such as NT AUTHORITY or BUILTIN).
Also, this outputs an array of strings containing usernames only - whereas a Select-Object call without -ExpandProperty would output custom objects with the specified (possibly calculated) property/ies instead.
In PSv4+, you can make the command slightly more efficient by using the .ForEach() collection method:
(Get-Acl c:\temp).Access.IdentityReference.ForEach({ ($_ -split '\\')[-1] })
EBGreens answer is spot on, but I like to use split-path
You can do something like:
(get-Acl 'c:\temp').Access | where {$_.IdentityReference -like '*\*'} | Select #{name="UserName";Expression={$_.IdentityReference | split-path -leaf}}

Shorter Syntax for grabbing a string? [Select-String]

I do a lot of regex matching using Select-String in Powershell.
For example, the simplest and maybe the most common match, an IPv4 address:
$regex = \b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b
Now, if I was to match this in a line that said:
$output = `
"Blah blah blah, 202.100.100.9, you're going to match the IP in the middle of this line,
but not on this line, because '2.a.3.one' is not a valid IPv4 address"
and then I do:
$output | Select-String $regex
It will give me the entire line.
I can't really use that in it's raw form.
However if I use this:
$output | Select-String $regex | %{$_.Matches} | %{$_.Value}
It will give me JUST the IP address, which is great.
My question is:
Is there a simpler way to do this?
I'd rather not type out | %{$.Matches} | %{$.Value} every time I want to grab just one particular string.
If you prefer Select-String over the -replace operator (which is just syntactic sugar for calling [Regex]::Replace), PowerShell V3 has a few shortcuts that can save some typing.
First, there is an alias for Select-String => sls.
Second, with simple foreach-object script blocks, you can replace the script block with just the property.
Combining these, you can use
$output | sls $regex | % Matches | % Value
To save even more typing, PowerShell can tab complete Matches but not Value.
Another option that is even less typing is to use property syntax:
($output | sls $regex).Matches.Value
As a bonus, tab completion can complete both Matches and Value in this example. Note that this second example works in V2 but only if there is a single matching line. If there are multiple matching lines, only in V3 will you see all the results, V2 would show nothing or an error if strict mode is enabled.
Try this:
$output -replace ".*($regex).*",'$1'
First I want to thank everyone for their help and their efforts in trying to figure this out for me. All of your suggestions have been good, and useful.
I had a think, and I realized that I am looking for a command that doesn't really exist, so I just wrote a function for it instead:
function Regex-Match {
[cmdletbinding()]
param (
[parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false)]$RegexString,
[parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true)]$SearchString
)
$Results = #($SearchString | Select-String -Pattern $RegexString | % {$_.Matches} | % {$_.Value})
Return $Results
}
To further shorten this, I can use
New-Alias -Name regx -Value Regex-match
Example command:
"test string" | regx "\w\s\w"
or
Regex-Match -SearchString (gc .\Stuff.txt) -RegexString "\w+"

How to count objects in PowerShell?

As I'm reading in the PowerShell user guide, one of the core PowerShell concepts is that commands accept and return objects instead of text. So for example, running get-alias returns me a number of System.Management.Automation.AliasInfo objects:
PS Z:\> get-alias
CommandType Name Definition
----------- ---- ----------
Alias % ForEach-Object
Alias ? Where-Object
Alias ac Add-Content
Alias asnp Add-PSSnapIn
Alias cat Get-Content
Alias cd Set-Location
Alias chdir Set-Location
...
Now, how do I get the count of these objects?
This will get you count:
get-alias | measure
You can work with the result as with object:
$m = get-alias | measure
$m.Count
And if you would like to have aliases in some variable also, you can use Tee-Object:
$m = get-alias | tee -Variable aliases | measure
$m.Count
$aliases
Some more info on Measure-Object cmdlet is on Technet.
Do not confuse it with Measure-Command cmdlet which is for time measuring. (again on Technet)
As short as #jumbo's answer is :-) you can do it even more tersely.
This just returns the Count property of the array returned by the antecedent sub-expression:
#(Get-Alias).Count
A couple points to note:
You can put an arbitrarily complex expression in place of Get-Alias, for example:
#(Get-Process | ? { $_.ProcessName -eq "svchost" }).Count
The initial at-sign (#) is necessary for a robust solution. As long as the answer is two or greater you will get an equivalent answer with or without the #, but when the answer is zero or one you will get no output unless you have the # sign! (It forces the Count property to exist by forcing the output to be an array.)
2012.01.30 Update
The above is true for PowerShell V2. One of the new features of PowerShell V3 is that you do have a Count property even for singletons, so the at-sign becomes unimportant for this scenario.
Just use parenthesis and 'count'. This applies to Powershell v3
(get-alias).count
#($output).Count does not always produce correct results.
I used the ($output | Measure).Count method.
I found this with VMware Get-VmQuestion cmdlet:
$output = Get-VmQuestion -VM vm1
#($output).Count
The answer it gave is one, whereas
$output
produced no output (the correct answer was 0 as produced with the Measure method).
This only seemed to be the case with 0 and 1. Anything above 1 was correct with limited testing.
in my exchange the cmd-let you presented did not work, the answer was null, so I had to make a little correction and worked fine for me:
#(get-transportservice | get-messagetrackinglog -Resultsize unlimited -Start "MM/DD/AAAA HH:MM" -End "MM/DD/AAAA HH:MM" -recipients "user#domain.com" | where {$_.Event
ID -eq "DELIVER"}).count
Get-Alias|ForEach-Object {$myCount++};$myCount
158
Please try this to get total count of objects.
(Get-Alias).Count