PowerShell $_ syntax

1.9k Views Asked by At

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?

2

There are 2 best solutions below

1
On BEST ANSWER

%{} 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 
3
On

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:


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).