tl;dr
Your problem boils down to a simple confusion between the &
, the call operator and %
the built-in alias of the ForEach-Object
cmdlet.
However, the resulting error message is confusing and therefore worth explaining - see below.
Additionally, guidance on how to discover the meanings of symbols used in PowerShell is provided.
Instead of %
, use &
, the call operator to execute a command, such as the external docker-compose
program; however, given that you specified the program's name unquoted (and given that it doesn't contain special characters that would require quoting and that it isn't expressed via a variable reference), you do not even need &
in this case:
# Note: & is optional in this case.
& docker-compose -f localdevhelpers/docker-compose.yml -f mediaservice/docker-compose.yml up
As for what you tried:
%
is a built-in alias of the ForEach-Object
cmdlet, whose purpose isn't to invoke commands, but to operate on (typically) multiple inputs from the pipeline.
The reason for the - obscure - error message ("cannot call a method on a null-valued expression"):
ForEach-Object
is often used with one or more script blocks ({ ... }
) to operate on - e.g. 1..3 | ForEach-Object { 1 + $_ }
However, it also supports simplified syntax, whose syntax requirements your call accidentally met, which then led to the - obscure - runtime error:
Syntax form % foo bar ...
(ForEach-Object foo bar ...
) causes foo
to be interpreted as the name of a .NET method, with bar
and any subsequent arguments being considered the arguments to pass to that method.
The specified method is called on each input object, but - in the absence of pipeline input - there is no input object, so ForEach-Object
sees $null
in lieu of an actual input object. Because methods cannot be invoked on $null
, the error message you saw was reported.
Inspecting commands and operators:
You could have used Get-Help
or Get-Command
to determine what %
refers to:
# `Get-Help %` or even `% -?` would print help.
PS> Get-Command %
CommandType Name Version Source
----------- ---- ------- ------
Alias % -> ForEach-Object
This works, because %
is a command (an alias).
Among commands, it is unusual in that it is a symbol rather than a word-based name; another exception is ?
, which is a built-in alias of the Where-Object
cmdlet (because ?
is a wildcard metacharacter, you actually have to use Get-Command ``?
or Get-Help ``?
).
Other symbols are not commands - they're usually operators, which you cannot discover with Get-Command
, and only barely with Get-Help
(you'll get a lot of results, most of which aren't relevant).
Instead, consult the conceptual about_Operators help topic for help on operators.
This gap in discoverability between commands and operators is unfortunate - GitHub issue #11339 suggests extending Get-Help
to support lookup by operator symbols too.
In the meantime, the Show-OperatorHelp
custom function can provide this functionality (an MIT-licensed Gist; see this answer for a discussion).
Overview of symbol-relevant help topics and information:
about_Operators documents all operators.
about_Script_Blocks documents script blocks and their literal form, { ... }
about_Arrays documents arrays and their quasi-literal form, @(...)
- though note that @(...)
is technically an operator and is therefore also covered in about_Operators
about_Hash_Tables documents hash tables (hashtables) and their literal form, @{ .. .}
about_Splatting discusses argument splatting, i.e. the ability to pass a set of arguments (parameter values) via a variable containing a hashtable or array, which must be referenced via @
rather than $
(e.g, @params
).
about_Quoting_Rules documents quoting, i.e. '...'
and "..."
strings and their here-string variants, @'<newline>...<newline>'@
and @"<newline>...<newline>"@
about_Special_Characters documents PowerShell's escape sequences, whose syntax is based on PowerShell's escape character, `
, the so-called backtick
about_Comment_Based_Help - indirectly - documents PowerShell's comment syntax, #
for to-the-end-of-the-line comments, and <# ... #>
for block comments (which can be used both in-line and may span multiple lines).
A symbol not covered by the topics above is ;
the statement separator (which is only needed if you place multiple statements on a given line).
To discover all commands that start with a symbol (non-word character), use the following:
Get-Command -Type All | Where Name -Match '^\W'