Running help <command> and piping output to Where-Object or Select-Object returns empty rows - powershell

Running the command help firewall | Select-Object Category. The result is one column blank Category.
The strange thing is that the empty rows number represent the amount of rows that help firewall would result without calling piping it to Select-Object
Or I'm trying to filter the output of help firewall to return only rows with Name that starts with "Get". Running help firewall | Where-Object Name -like "Get" just returns nothing.
Why aren't these pipes on help working? They are working perfectly on other commands.
Powershell Version 5.1 and using default windows console.

To complement Zilog80's helpful answer with background information:
Get-Command help reveals that help is not a mere alias of the Get-Help cmdlet, but a (built-in) function (submit $function:help to see its definition).
As you've noticed yourself:
while Get-Help outputs an object ([pscsustomobject]) with properties that reflect help-topic metadata such as Category, which is then rendered as display text by PowerShell's output-formatting system,
the help function returns strings - a stream of text lines representing the rendered help topic - of necessity.
You can observe the difference in output type by piping to the Get-Member cmdlet (help firewall | Get-Member vs. Get-Help firewall | Get-Member)
The purpose of the help function is to wrap Get-Help with interactive paging, to allow convenient navigation through lengthy help topics that don't fit onto a single console (terminal) screen.
This paging is provided via an external program (by default, more.com on Windows, and less on Unix-like platforms, configurable via $env:PAGER, but only in PowerShell (Core) 7+), and since PowerShell only "speaks text" when communicating with external programs, help must send a stream of strings (lines for display) to them, which it does via
Out-String -Stream.
Note:
When the external paging programs find that their stdout stream isn't connected to a console (terminal), they take no action other than simply passing the input through (in Unix terms, they then behave like cat).
Hypothetically, the help function itself could determine this condition and then (a) not pipe to the paging program and (b) relay the object output by Get-Help as-is.[1] However, determining a command's output target from inside that command, using PowerShell code, may not even be possible.
[1] The function actually already takes this action when a custom pager defined via $env:PAGER is found to be a PowerShell command rather than an external program.

Check the feedback from help help in PowerShell :
You can also type `help` or `man`, which displays one screen of text at a
time. Or, ` -?`, that is identical to `Get-Help`, but only
works for cmdlets.
The helpcommand display "screen of text" which means it is outputting [System.String] objects, not [PSCustomObject] objects.
Only -? will behave identically to Get-help and will provide [PSCustomObject] objects.
To see what's going on, check the different output from :
help firewall | %{ $_.GetType() }
And
Get-help firewall | %{ $_.GetType() }
And, for cmdlet,
Select-Object -? | %{ $_.gettype() }

Related

Why is PS Get-ChildItem so difficult

I did a ton of reading and searching about a way to have Get-ChildItem return a dir listing in wide format, in alphabetical order, with the number of files and directories in the current directory. Here is a image of what I ended up with, but not using GCI.
I ended up writing a small PS file.
$bArgs = "--%/c"
$cArgs = "Dir /n/w"
& cmd.exe -ArgumentList $bArgs $cArgs
As you can see I ended up using the old cmd.exe and passing the variables I wanted. I made an alias in my PS $Profile to call this script.
Can this not be accomplished in PS v5.1? Thanks for any help or advice for an old noob.
PowerShell's for-display formatting differs from cmd.exe's, so if you want the formatting of the latter's internal dir command, you'll indeed have to call it via cmd /c, via a function you can place in your $PROFILE file (note that aliases in PowerShell are merely alternative names and can therefore not include baked-in arguments):
function lss { cmd /c dir /n /w /c $args }
Note that you lose a key benefit of PowerShell: the ability to process rich objects:
PowerShell-native commands output rich objects that enable robust programmatic processing; e.g., Get-ChildItem outputs System.IO.FileInfo and System.IO.DirectoryInfo instances; the aspect of for-display formatting is decoupled from the data output, and for-display formatting only kicks in when printing to the display (host), or when explicitly requested.
For instance, (Get-ChildItem -File).Name returns an array of all file names in the current directory.
By contrast, PowerShell can only use text to communicate with external programs, which makes processing cumbersome and brittle, if information must be extracted via text parsing.
As Pierre-Alain Vigeant notes, the following PowerShell command gives you at least similar output formatting as your dir command, though it lacks the combined-size and bytes-free summary at the bottom:
Get-ChildItem | Format-Wide -AutoSize
To wrap that up in a function, use:
function lss { Get-ChildItem #args | Format-Wide -Autosize }
Note, however, that - due to use of a Format-* cmdlet, all of which output objects that are formatting instructions rather than data - this function's output is also not suited to further programmatic processing.
A proper solution would require you to author custom formatting data and associate them with the System.IO.FileInfo and System.IO.DirectoryInfo types, which is nontrivial however.
See the conceptual about_Format.ps1xml help topic, Export-FormatData, Update-FormatData, and this answer for a simple example.

PowerShell $_ syntax

In this answer the author proposed the following snippet:
dir -Path C:\FolderName -Filter *.fileExtension -Recurse | %{$_.FullName}
I can understand the majority of it, but I'm unable to search documentation for the last part. The output of the search is piped | and used in %{} and as $_.
I have experimented around it, %{} is a for-each statement I believe, bing search was not effective. $_ is also somewhat magic: it is a variable, with no name and thus immediately consumed? I don't care much for the .FullName, that part I sorted out. Again, bing search was not effective, nor searching for those char sequences in PowerShell docs.
Can anybody explain it to me?
%{} is not "a thing" - it's two things: % and {}
% is an alias for the ForEach-Object cmdlet:
PS ~> Get-Alias '%'
CommandType Name Version Source
----------- ---- ------- ------
Alias % -> ForEach-Object
... so it resolves to:
... |ForEach-Object { $_.FullName }
ForEach-Object is basically PowerShell's map function - it takes input via the pipeline and applies the operation described in the {} block to each one of them.
$_ is an automatic reference to the current pipeline input item being processed
You can think of it a bit like a foreach($thing in $collection){} loop:
1..10 |ForEach-Object { $_ * 10 }
# produces the same output as
foreach($n in 1..10){
$n * 10
}
Except we can now stick our loop in the middle of a pipeline and have it produce output for immediate consumption:
1..10 |ForEach-Object { $_ * 10 } |Do-SomethingElse
ForEach-Object is not the only thing that makes use of the $_ automatic variable in PowerShell - it's also used for pipeline-binding expressions:
mkdir NewDirectory |cd -Path { $_.FullName }
... as well as property expressions, a type of dynamic property definition supported by a number of cmdlets like Sort-Object:
1..10 |Sort-Object { -$_ } # sort in descending order without specifying -Descending
... Group-Object:
1..10 |Group-Object { $_ % 3 } # group terms by modulo congruence
... and Select-Object:
1..10 |Select-Object #{Name='TimesTen';Expression={$_ * 10}} # Create synthetic properties based on dynamic value calculation over input
To complement Mathias' answer, which explains the specific constructs well, with how you could / couldn't have discovered this information yourself, using PowerShell's own help system:
Relevant help topics and use of the help system:
Note: To get an overview of all aspects of PowerShell's help system, simply run help.
% is a built-in alias for the ForEach-Object cmdlet:
Use Get-Help ForEach-Object to view the help topic in the terminal.
If no local topics are found, you must download them via the Update-Help cmdlet.
Tips:
Add the -Online switch to open the (potentially more current) online version of the topic in your browser.
You can bootstrap your use of Get-Help with Get-Help Get-Help (or even help help):
Cmdlet-specific help comes in detail levels: terse (default, shows the syntax and overview description only), -Detailed (includes parameter descriptions and example commands) and -Full (additionally includes technical parameter information and extended notes).
-Examples can be used to show example commands only.
With keyword-based search (see below), you can limit results to topics of a certain category with the -Category parameter.
For convenience, you can also use the built-in help function, which wraps Get-Help calls with display paging (simply put: by piping the output to the more utility) and defaults to detail level -Full.
{...} is a script block literal, a block of arbitrary PowerShell code that can be invoked on demand:
help about_Script_Blocks shows the topic locally; the about_ prefix indicates that the topic is a conceptual help topic (rather than one covering a specific command); when you use Get-Help to search for a keyword (see below), you can (somewhat obscurely) limit the results to conceptual topics with -Category HelpFile.
Note: As of this writing, about_ topics can not yet be directly viewed online by adding -Online - see GitHub issue #13550 - but it's easy to google them by name.
$_ is a variable, as the $ sigil followed by an identifier implies, and is more specifically an automatic (built-in) variable:
help about_Variables covers variables in general.
help about_Automatic_Variables covers the automatic ones.
How the above can / cannot be discovered based on symbols and aliases alone:
Doing a web search for symbols is notoriously unhelpful.
As an aside: Running distinct syntax constructs such as % and { ... } together without whitespace between them (e.g. %{$_.FullName}) constitutes an additional barrier, and should therefore be avoided.
Narrowing your search by using only PowerShell's help system helps, but only to a limited degree:
%
Because Get-Help is aware of aliases, help % actually works fine and directly shows ForEach-Object's help topic.
help % -Examples shows example commands that include the use of script blocks and the automatic $_ variable.
Even though Get-Help supports keyword-based search, searching for symbol-based terms {} and $_ directly isn't helpful, because even when limiting the search to conceptual (about_-prefixed topics) with -Category HelpFile, there are either too many hits (help '$_' -Category HelpFile) or the relevant topic doesn't show at all (help '{}' -Category HelpFile)
$_ can be discovered indirectly, IF you already know that it is an instance of a variable:
help variables -Category HelpFile happens to take you directly to the relevant (local) about_Automatic_Variables topic,
whereas help variable -Category HelpFile lists the following matching topics about_Variable_Provider, ``, about_Automatic_Variables, about_Preference_Variables, about_Remote_Variables, about_Variables, and about_Environment_Variables
Note: Thanks to PowerShell's pervasive support for wildcard expressions, you could have performed the search also as follows: help about*variable* - be sure to enclose both sides of the search term in *.
$_ can be discovered indirectly, IF you already know that it is an instance of a script (code) block:
help about_*block* takes you directly to the relevant (local) about_Script_Blocks topic.
Potential future improvements:
It would be a great improvement if PowerShell's help system supported focused symbol-based searches.
Similarly, the ability to directly look up operators, such as -match, the regular-expression matching operator, would be helpful:
GitHub issue #11339 proposes just that.
On a related note, GitHub issue #11338 proposes adding the ability to look up documentation for .NET types (online).
This answer contains custom functions Show-OperatorHelp and Show-TypeHelp, which fill that gap for now (also available as Gists).

Why does "Get-Vm | Sls PoweredOff" not grep for VMs that are Powered off?

I am trying to use PowerCLI to search for a list of PoweredOff VMs.
I want to search the results of the command Get-Vm:
Vm01 PoweredOn 1 16
Vm02 PoweredOff 1 16
etc.
I want to search this list for "PoweredOff", but the PowerShell Sls doesn't seem to work if I type:
Get-Vm | sls PoweredOff
It will not show the PoweredOff virtual machines. Can anyone provide any guidance on outputting this a stream of text to search (rather then a list of Objects to search)?
PowerShell cmdlets return objects, not simple text output. You filter output by the values of specific properties with the Where-Object cmdlet.
Get-Vm | Where-Object { $_.PowerState -eq 'PoweredOff' }
Tabular or list output will normally show you the property names. However, not all of the objects' properties may be displayed by default, and sometimes the default output format of a particular type is made to look differently than the output would normally do (e.g. Get-Process output). You can get a list of all properties (and methods) of an object by using the Get-Member cmdlet. Add the parameter -Force to include intrinsic properties. Add the parameter -Static to show class methods instead of object methods.
For VMware's cmdlets you could also check the PowerCLI documentation, which lists the return types of the cmdlets.

Select-Object join behavior in scripts

In this post some is surprised that the following example doesn't work properly when run in a script
get-process | select-object cpu, name
dir | Select-Object name, length
When I put this in a script the second command doesn't show the length
And the answer to this:
PowerShell is joining both outputs. You could pipe the first output to
Format-Table [-auto] if you don't mind the format. Alternatively, you
can separate the first output from the following formatted output by
piping it to Out-Default, Out-Host, Out-string or Write-host
Now my question is why is it joining the output of those two seemingly unrelated commands and why doesn't this happen in the interactive console? They are not connected with a pipe. How does this work?
It seems that I still haven't quite understood basic concepts about piping in scripts.
See if this helps: blogs.msdn.com/b/powershell/archive/2006/04/30/586973.aspx
This issue isn't really the pipeline, but the default console formatting

PowerShell: programmatically access script documentation

Is there a way to programmatically load the documentation of a .ps1 script file outside of commands like get-help? In other words, can text defined under .SYNOPSIS, .DESCRIPTION, etc. be accessed programmatically other than filtering the string output of get-help itself?
Among other things, I'm trying to find where I have gaps in documentation coverage in my script library. I'd also like to be able to display lists of certain scripts with their synopsis attached.
Yes, those are all accessible. Get-Help returns (just like any other cmdlet) an object, and the default rendering of that object is what you see in the console.
However, if you pump get-help's output through format-list, like this:
get-help get-childitem | format-list
You'll get a list of name-value pairs of the properties. To get the synopsis, you can do the following:
get-help get-childitem |select-object -property synopsis
And the output:
Synopsis
--------
Gets the files and folders in a file system drive.
If your .ps1 file has no cmdlets defined in it (your comment-based help covers the whole script), get-help file.ps1|select synopsis should work. Otherwise, you'll need to "dot-source" the files to load the cmdlet definitions into memory, then use get-help as above.