I came across some PowerShell code like Get-Process | ?{ $_.ProcessName -Match "^ex.*" } but didn’t know what ?{ … } does. I only knew %{ … }, which is the shorthand notation of a For-Each where $_ refers to the current element. So my journey began.

The Get-Alias cmdlet

A quick internet search for powershell shorthand notations lead me to the blog post Weekend Scripter: Deciphering Windows PowerShell Shorthand. The first thing it mentions is Get-Alias which lets you look up shorthands:

Get-Alias iex       # positional argument
Get-Alias -Name iex # named argument
CommandType  Name                      Version  Source
-----------  ----                      -------  ------
Alias        iex -> Invoke-Expression

It also works the other way around, looking up the shorter aliases of commands:

Get-Alias -Definition Invoke-Expression
CommandType  Name                      Version  Source
-----------  ----                      -------  ------
Alias        iex -> Invoke-Expression

Too many alias lookup results

When I tried Get-Alias ? it answered my question (? is equivalent to Where-Object), but to my surprise some other single character aliases were listed too:

CommandType  Name                 Version  Source
-----------  ----                 -------  ------
Alias        % -> ForEach-Object
Alias        ? -> Where-Object
Alias        h -> Get-History
Alias        r -> Invoke-History

So there is obviously some special meaning to the question mark. However, the PowerShell documentation About Special Characters does not list it in the table of recognized escape sequences. I gave it a shot and tried Get-Alias `? anyway, but the result remained the same. I also tried a few other common escaping patterns such as Get-Alias \? and wrapping in quotes, but it either returned the same list or nothing.

Escaping wildcard characters

The Get-Alias documentation mentions that you can

Enter […] a pattern, such as “s*”. Wildcards are permitted.

It doesn’t say anything about ? though. The page I found About Wildcards lists the question mark together with the other supported wildcards * and […], confirms that it matches one character in that position, but does not contain any information about how to escape them.

My search continued to Supporting Wildcard Characters in Cmdlet Parameters, which has a section about Handling Literal Characters in Wildcard Patterns:

If the wildcard pattern you specify contains literal characters that should not be interpretted as wildcard characters, use the backtick character (`) as an escape character. When you specify literal characters in the PowerShell API, use a single backtick. When you specify literal characters at the PowerShell command prompt, use two backticks.

So Get-Alias ``? it is:

CommandType  Name               Version  Source
-----------  ----               -------  ------
Alias        ? -> Where-Object

What is the PowerShell API?!

I couldn’t find any meaningful information about the mentioned PowerShell API, in which case you need a single backtick to escape ? only. I arrived at the conclusion that it refers to the usage of PowerShell in a .NET language such as C#. The following code for a small console demo application supports my theory:

using System;
using System.Management.Automation;

namespace TestPowershellApi
{
    class Program
    {
        static void Main(string[] args)
        {
            PowerShell ps = PowerShell
                .Create()
                .AddCommand("Get-Alias")
                .AddArgument("`?");            // positional argument
                //.AddParameter("Name", "`?"); // named argument

            foreach (PSObject result in ps.Invoke())
            {
                Console.WriteLine("{0} -> {1}",
                    result.Members["Name"].Value,
                    result.Members["Definition"].Value
                );
            }
        }
    }
}
? -> Where-Object

Note that there are also PowerShell reference assemblies available as separate packages for certain use cases, but the built-in System.Management.Automation.PowerShell is all you need if you want to execute PowerShell commands in C#.

Quadruple escaping for Invoke-Expression

In the (rare?) case that you want to evaluate a command or expression string in a PowerShell script or the PowerShell command prompt, then you need not 1, not 2, but 4 backticks:

Invoke-Expression "Get-Alias ````?"

My assumption is that one level of escaping is necessary for the command line itself, and the other because everything that is entered by the user is a string at first. For example, 40+2 is a sequence of characters. However, it gets evaluated to 42. This requires parsing and casting the digits to numbers. If every input is implicitly converted, then it could be necessary to escape a single backtick with another one in order to not consume it and interpret the subsequent character as wildcard instead of as literal.

Summary

? is an alias of Where-Object, which selects objects from a collection based on their property values. You can find that out with Get-Alias. To look up just this alias and not every single-character alias, you must escape the question mark:

  • Single backtick if you use the PowerShell API, such as using System.Management.Automation in C#:

    PowerShell.Create().AddCommand("Get-Alias").AddArgument("`?")
    
  • Two backticks in PowerShell scripts and the command prompt in general:

    Get-Alias ``?
    
  • Four backticks if it’s a string that you want to run as code:

    Invoke-Expression "Get-Alias ````?"
    

The following commands are identical:

Get-Process | ?{ $_.ProcessName -Match "^ex.*" }
Get-Process | Where-Object { $_.ProcessName -Match "^ex.*" }
Get-Process | Where-Object ProcessName -Match "^ex.*"
  1. Script block format with shorthand
  2. Script block format
  3. Comparison statement format
  4. Bonus: very short variant with slightly different behavior:
ps | Where ProcessName -M "^ex.*"