why sls aka select-string does not work - powershell

I am newbie on powershell. Today I tried something very simple
Alias | sls -Pattern 'echo'
which produced echo, but what I want is Alias echo -> Write-Out.
In bash, you can just do
alias | grep 'echo'
My question is why sls does not work. BTW, if I replace sls with findstr, it worked.

If you want to get an alias with a particular name you can do:
alias -name echo
The echo -> Write-Out is the DisplayName:
(alias -name echo).DisplayName
The Get-Alias command returns a sequence of objects, each of which represents a single command alias. When displayed in powershell, these objects are formatted as a table containing the CommandType, Name and ModuleName properties.
When you pipe into findstr, it is the string representations of these columns which are being filtered, so any match displays the whole table row:
Alias echo -> Write-Output
When you pipe into Select-String each object is being bound to the -InputObject parameter of the Select-String cmdlet. Since Select-String operates on text, it just calls ToString on the received object to get its string representation.
ToString only returns the Name property. You can see this by executing the following:
alias | %{$_.tostring()}
Therefore any matches from Select-String only match on the alias name.

select-string only behaves like grep when used with a text file. With a powershell object, the behavior changes (as Lee explained in his answer).
This can be demonstrated with:
alias > out.txt; cat out.txt | sls -pattern 'echo'
Which returns Alias echo -> Write-Output because now slsis operating on a text file.
The other solutions to do what you want are:
alias | where DisplayName -like '*echo*'
alias | out-string -stream | sls -pattern 'echo'
This converts the powershell object to a string so that sls works like grep.

Related

Seaching windows environment for a specific value

I want to search the windows environment variables for a specific string
Get-ChildItem -Path Env: | TEE-OBJECT -variable newvar1 | grep windir $newvar1
It works for first time
STDIN
windir C:\WINDOWS
and then fails the subsequent times
grep: Could not open 'System.Collections.DictionaryEntry'
How do i remove old variables from the dictionary (if that is the problem)?
Part 1 - Root Cause
Your core problem is grep windir $newvar1 - the command line parameters for grep are (from https://man7.org/linux/man-pages/man1/grep.1.html):
SYNOPSIS
grep [OPTION...] PATTERNS [FILE...]
DESCRIPTION
grep searches for PATTERNS in each FILE.
You're asking grep to search in a file ($newvar1), not the input stream. Each entry in $newvar1, gets serialised as the literal string System.Collections.DictionaryEntry so grep is basically looking for a file called System.Collections.DictionaryEntry, which doesn't exist.
Part 2 - Solution
The best bet is to go full idiomatic PowerShell and use #JPBlanc's answer, but if you're really wedded to grep just remove the trailing $newvar to use the input stream (stdin) instead of a file for input:
Get-ChildItem -Path Env: `
| TEE-OBJECT -variable newvar1 `
| grep windir
or, if you don't actually need the values stored in $newvar1 for downstream processing, just simply:
Get-ChildItem -Path Env: `
| grep windir
Part 3 - Why no error the first time?
Your original command works the first time because $newvar1 isn't defined yet, so it's equivalent to:
Get-ChildItem -Path Env: `
| TEE-OBJECT -variable newvar1 `
| grep windir $null
... so grep is defaulting to searching the input stream (stdin) rather than a file.
You can confirm this if you enable strict mode - you'll get this error from PowerShell instead:
Set-StrictMode -Version "Latest";
Get-ChildItem -Path Env: `
| TEE-OBJECT -variable newvar1 `
| grep windir $newvar1
# InvalidOperation:
# Line |
# 3 | | grep term $newvar1
# | ~~~~~~~~
# | The variable '$newvar1' cannot be retrieved because it has not been set.
The second time you run the commnad, $newvar1 is already initialised with the results from the first time, so it's equivalent to:
Get-ChildItem -Path Env: `
| TEE-OBJECT -variable newvar1 `
| grep windir "System.Collections.DictionaryEntry"
which, as we've seen, tells grep to look for a file called System.Collections.DictionaryEntry, and results in an error.
Part 4 - More details
Note that $newvar1 isn't defined in the first call to grep because Tee-Object only creates the variable in its End block once it's processed all of its pipeline input (see the source code for Tee-Object.cs on GitHub), which doesn't happen until the entire pipeline has been processed, including the downstream calls to grep.
Not that it's very useful, but you can force the first command to fail by doing this:
(Get-ChildItem -Path Env: | TEE-OBJECT -variable newvar1) `
| grep windir $newvar1
# /usr/bin/grep: System.Collections.DictionaryEntry: No such file or directory
Wrapping the first two expressions in a Grouping Operator forces the pipeline inside to be fully evaluated first, which means Tee-Object's End block creates the $newvar1 variable before grep is invoked, and we get the file-searching behaviour instead of the input stream behaviour.
You an try :
Get-ChildItem -Path Env: | Where-object {$_.value -like 'C:\Users*'}
Get-ChildItem returns a list of objects with properties Name and value. Then Where-object, allow you to filter on these properties, $_ representthe object, you can choose the operator (in this case -like) in the list of Powershell operators).

Out-file , txt are created , no data saved [duplicate]

I'm new to scripting and I am trying to write the information returned about a VM to a text file. My script looks like this:
Connect-VIServer -Server 192.168.255.255 -Protocol https -User xxxx -Password XXXXXX
Get-VM -Name xxxxxx
Get-VM xxxxx | Get-HardDisk | Select Parent, Name, Filename, DiskType, Persistence | FT -AutoSize
Out-File -FilePath C:Filepath
I am able to connect to the VM, retrieve the HDD info and see it in the console. The file is created where I want it and is correctly named. No data is ever put into the file. I have tried Tee-Object with the same results. I've also tried the -append switch. I did see a post about the data being returned as an array and Powershell is not able to move the data from an array to a string. Do I need to create a variable to hold the returned data and write to file from there?
Thanks
Guenther Schmitz' answer is effective, but it's worth explaining why:
Your Out-File -FilePath C:Filepath is a stand-alone command that receives no input.
An Out-File call with no input simply creates an empty file (0 bytes).
In order for cmdlets such as Out-File to receive input from (an)other command(s) (represented as ... below), you must use the pipeline, which means that you must place a | after the input command(s) and follow it with your Out-File call:Note that I'm using the shorter -Path parameter alias for the less commonly used -FilePath[1]
... | Out-File -Path C:Filepath
In the simplest case, as above, the entire command (pipeline) is placed on the same line; if you want to spread it across multiple lines for readability, you have have two choices:
Put a line break immediately after |, which tells PowerShell that the command continues on the next line:
... |
Out-File -Path C:Filepath
End a line with an explicit line continuation, which means placing ` at the very end of a line:
... `
| Out-File -Path C:Filepath
Alternatively, since you're using Out-File with its default behavior, you could use >, an output redirection, instead:
... > C:Filepath
A couple of asides:
Using Out-File with something other than strings, and using Format-* cmdlets in general, means that the output is only suitable for display (human consumption), not for further programmatic processing.
If you want to send output to both the console and a file, use the Tee-Object cmdlet, as TobyU suggests:
... | Tee-Object -Path C:Filepath
[1] Strictly speaking, -LiteralPath is the best choice in this case, because -Path interprets its arguments as wildcard expressions. However, omitting -Path, i.e. specifying the file path as a positional argument, as is common, implicitly binds to -Path.
try this:
Get-VM xxxxx |
Get-HardDisk |
Select Parent, Name, Filename, DiskType, Persistence |
Out-File -FilePath C:\Filepath

How to get the first word of output from a PowerShell command

I am trying to get first word from the output of this powershell command
Get-ChildItem -Path Cert:\Certificate::LocalMachine\My | findstr -i ecimas
Which is returning output like:
ffdrggjjhj ecims.example.com
How can I return the string "ffdrggjjhj" only?
You should just be able to split the output like so:
(Get-ChildItem -Path Cert:\Certificate::LocalMachine\My | findstr -i ecimas).split()[0]
Usually powershell looks more like this. Since there's objects, parsing isn't needed.
get-childitem Cert:\LocalMachine\TrustedPublisher | where subject -match wireless |
select -expand thumbprint
ABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCD

Writing console output to a file - file is unexpectedly empty

I'm new to scripting and I am trying to write the information returned about a VM to a text file. My script looks like this:
Connect-VIServer -Server 192.168.255.255 -Protocol https -User xxxx -Password XXXXXX
Get-VM -Name xxxxxx
Get-VM xxxxx | Get-HardDisk | Select Parent, Name, Filename, DiskType, Persistence | FT -AutoSize
Out-File -FilePath C:Filepath
I am able to connect to the VM, retrieve the HDD info and see it in the console. The file is created where I want it and is correctly named. No data is ever put into the file. I have tried Tee-Object with the same results. I've also tried the -append switch. I did see a post about the data being returned as an array and Powershell is not able to move the data from an array to a string. Do I need to create a variable to hold the returned data and write to file from there?
Thanks
Guenther Schmitz' answer is effective, but it's worth explaining why:
Your Out-File -FilePath C:Filepath is a stand-alone command that receives no input.
An Out-File call with no input simply creates an empty file (0 bytes).
In order for cmdlets such as Out-File to receive input from (an)other command(s) (represented as ... below), you must use the pipeline, which means that you must place a | after the input command(s) and follow it with your Out-File call:Note that I'm using the shorter -Path parameter alias for the less commonly used -FilePath[1]
... | Out-File -Path C:Filepath
In the simplest case, as above, the entire command (pipeline) is placed on the same line; if you want to spread it across multiple lines for readability, you have have two choices:
Put a line break immediately after |, which tells PowerShell that the command continues on the next line:
... |
Out-File -Path C:Filepath
End a line with an explicit line continuation, which means placing ` at the very end of a line:
... `
| Out-File -Path C:Filepath
Alternatively, since you're using Out-File with its default behavior, you could use >, an output redirection, instead:
... > C:Filepath
A couple of asides:
Using Out-File with something other than strings, and using Format-* cmdlets in general, means that the output is only suitable for display (human consumption), not for further programmatic processing.
If you want to send output to both the console and a file, use the Tee-Object cmdlet, as TobyU suggests:
... | Tee-Object -Path C:Filepath
[1] Strictly speaking, -LiteralPath is the best choice in this case, because -Path interprets its arguments as wildcard expressions. However, omitting -Path, i.e. specifying the file path as a positional argument, as is common, implicitly binds to -Path.
try this:
Get-VM xxxxx |
Get-HardDisk |
Select Parent, Name, Filename, DiskType, Persistence |
Out-File -FilePath C:\Filepath

Concisely parsing a pipe

I have lots of text output from a tool that I want to parse in Powershell 2.0. The output is nicely formatted so I can construct a regex to pull out the data I want using select-string. However getting the subsequent matches out of select-string seems long-winded. There has to be a shorter way?
This works:
p4 users | select-string "^\w+(.\w+)?" |
select -Expand Matches | %{p4 changes -u $_.Value}
But All those Matches and Values are verbose. There's nothing obvious in the select-string help file, but is there a way to make it pump out just the regex matches as strings? A bit like:
p4 users | select-string "^\w(.\w+)?" -ImaginaryMagicOption | %{p4 changes -u $_}
In this case, it may be a bit easier to use -match e.g.:
p4 users | Foreach {if ($_ -match '^\w+(.\w+)?') { p4 changes -u $matches[0] }}
This is only because the output of Select-String is MatchInfo object that buries the Matches info one level down. OTOH it can be made to work:
p4 users | Select-String "^\w+(.\w+)?" |
Foreach {p4 changes -u $_.Matches[0].Value}