Reputation: 782
Very simple setup, please do try this at home.
$args
p ARGV
@echo off
ruby test.rb "foo bar moo"
powershell .\test.ps1 "foo bar moo"
pause
Run test.bat. Guess the output.
If you think like me, you're wrong.
["foo bar moo"]
foo
bar
moo
Ruby is my witness that the three words are passed as single argument. What in Bill Gates' name is PowerShell doing? And how can I make it work logically?
Or what am I missing?
Upvotes: 3
Views: 2595
Reputation: 439487
In order to invoke a script file (.ps1
) with arguments, use the PowerShell CLI's -File
(-f
) parameter, not the (implied) -Command
(-c
) parameter:
powershell -File .\test.ps1 "foo bar moo"
Note that PowerShell (Core) (pwsh
, PowerShell versions starting with v6) now defaults to -File
.[1]
As for what you tried:
powershell .\test.ps1 "foo bar moo"
is implicitly the same as (you can also use -c
instead of -Command
):
powershell -Command .\test.ps1 "foo bar moo"
and that causes PowerShell to evaluate the arguments in two stages:
First, syntactic "
around individual arguments are stripped and the resulting arguments are then joined with spaces to form a single string.
Second, the resulting string - .\test.ps1 foo bar moo
in this case - is then interpreted as PowerShell source code, and, as you can see, the original "
have been stripped by then, resulting in the script receiving 3 arguments.
Note that the same applies in cases where the script path has embedded spaces and must therefore be quoted:
:: !! BROKEN, because PowerShell *removes the double quotes and
:: !! ultimately executes: & C:\path with spaces\script.ps1
powershell -Command ^& "C:\path with spaces\script.ps1"
-Command
's behavior therefore also involves subjecting the arguments to additional interpretation according to PowerShell's rules, unlike with -File
. E.g.,
powershell -File .\test.ps1 $HOME
would pass $HOME
verbatim to the script, whereas
powershell -Command .\test.ps1 $HOME
would pass the value of PowerShell's automatic $HOME
variable.
With -Command
, passing "
characters that are to become part of the resulting PowerShell code being evaluated indeed requires escaping:
\"
(sic), though that can result in broken command lines when calling from batch files / cmd.exe
"..."
to -Command
, and escape pass-through "
as ""
in PowerShell (Core) / as "^""
(sic) in Windows PowerShell.See this answer for more guidance on when to use -File
vs. -Command
.
[1] It is understandable to expect -File
, not -Command
to be the default parameter, given that most shell and script-engine CLIs require an explicit parameter (typically, -c
or -e
) to pass a command string rather than a script-file path. Fortunately, in PowerShell (Core) 7+ -File
now is the default, a change that was necessary to support shebang lines on Unix.
Additionally, most shell and script-engine CLIs expect the command string to be passed as a single argument, with any additional arguments then becoming arguments passed to the code in the command string, whereas PowerShell simply stitches multiple arguments together and then interprets the result as the command string; see GitHub issue #4024
Upvotes: 5