The simplest solution is (note the \"$Line_1`n$Line_2\"
part):
PowerShell -c Add-Type -AssemblyName System.Windows.Forms; ^
$Line_1 = 'Hello!'; ^
$Line_2 = 'How are you?'; ^
[System.Windows.Forms.MessageBox]::Show(\"$Line_1`n$Line_2\")
Note that I've explicitly added the -c
(-Command
) parameter name to signal that a PowerShell command string is being passed. While that isn't necessary in Windows PowerShell, which defaults to -Command
, it is in PowerShell (Core) 7+, where -File
is now the default - see the CLI documentation for Windows PowerShell and PowerShell (Core) 7+.
That is, you must use $Line_1`n$Line_2
inside "..."
, an expandable string, and you must \
-escape the "
characters so that PowerShell doesn't strip them as part of its command-line parsing (in the absence of overall double-quoting, """
works too).
Unfortunately, the parsing rules change when for /f
is used in order to process PowerShell's output line by line and/or capture it in a variable:
Note: The following uses [Console]::WriteLine()
so as to produce console output, just for the sake of using similar syntax to the [System.Windows.Forms.MessageBox]::Show()
method call while allowing something to be captured by for /f
. In real life there is no good reason to call [Console]::WriteLine()
.
for /f "delims=" %%l in ('
PowerShell -c Add-Type -AssemblyName System.Windows.Forms^; ^
$Line_1 ^= 'Hello!'^; ^
$Line_2 ^= 'How are you?'^; ^
[Console]::WriteLine^(\"$Line_1`n$Line_2\"^)
') do echo [%%l]
= , ; ( )
must additionally be escaped (outside what cmd.exe
sees as a "..."
string).
If you additionally enclose a \"...\"
string in "..."
to prevent whitespace normalization (see next section) you must ^
-escaped the enclosing (outer) "
; e.g.,
^"\"Marley & Me\"^"
The line continuations (^
at the end of command-interior lines) are actually optional inside for /f
, but they were included for consistency.
Summary of quoting and escaping requirements:
Your multi-line technique with line continuations (^
at the end of lines) - which syntactically cannot use "..."
quoting around the entire command, because cmd.exe
doesn't support double-quoted multi-line strings - requires careful ^
-escaping of all cmd.exe
metacharacters that should be passed through to PowerShell, notably & | < > ^
, and, additionally, if PowerShell is called from inside a for /f
statement, , ; = ( )
- unless these characters happen to be part of a substring that cmd.exe
sees as double-quoted; e.g., a &
placed inside the \"...\"
string - e.g. \"$Line_1`n & $Line_2\"
- must not be ^
-escaped (but see below re whitespace normalization).
As an exception, metacharacter %
must always be escaped as %%
(which only works in batch files, not at the command prompt - see this answer).
Additionally, if setlocal enabledelayedexpansion
is in effect (or cmd.exe
was started with /V:ON
), !
must be escaped too, but inexplicably as follows:
- As
^^!
(sic) outside of what cmd.exe
sees as a "..."
string (where the other metacharacter require just one ^
)
- As
^!
inside such a string (where the other metacharacters require no escaping).
When calling via for /f
, line continuations are optional - that is, you may omit the ^
at the end of the command-interior lines.
Each statement must be ;
-terminated (as you have done), because the line-continuation (^
) results in no newline between the input lines, so PowerShell sees them as a single line on which multiple statements must be ;
-separated.
Because neither cmd.exe
nor PowerShell's initial command-line parsing knows about single-quoted strings ('...'
) and because the escaped "
characters in \"...\"
strings have no syntactic function during command-line parsing, such strings are broken into multiple arguments if they contain whitespace:
In effect, runs of multiple adjacent spaces inside such strings are normalized to a single space each. E.g., 'How are you?'
and \"How are you?\"
are ultimately seen as 'How are you?'
and "How are you?"
by PowerShell.
To avoid that, additionally enclose such strings in "..."
:
- Single-quoted PowerShell strings:
"'How are you?'"
- Note: This is not necessary if the entire command is enclosed in
"..."
- Double-quoted PowerShell strings:
^"\"How are you?\"^"
.
- The
^
-escaped enclosing "
chars. ensure that cmd.exe
still sees what is between the inner \"...\"
as double-quoted (because it doesn't recognize \
as an escape char.), obviating the need to ^
-escape cmd.exe
metacharacters there.
- Note: If the entire command is enclosed in
"..."
, a different approach is required: use "... "^""How are you?"^"" ..."
with powershell.exe
(Windows PowerShell), and "... ""How are you?"" ..."
with pwsh.exe
(PowerShell (Core) 7+).
In case you want to include comments in the PowerShell code, you must use the form
^<# ... #^>
- i.e. (escaped) inline comments - normal single-line comments (# ....
) are not supported (because it would require a newline to terminate them, but there are no newlines between statements in the invocation method at hand).
How PowerShell parses the arguments passed to its CLI's -Command
/ -c
parameter:
PowerShell gives you the choice between passing the command string
either: as a single argument, enclosed in overall "..."
; e.g.:
powershell -c "Get-Date -Format 'yyyy MMM'"
or: multiple arguments - possibly individually "..."
-quoted - which PowerShell then joins to form a single string; e.g.:
powershell -c Get-Date -Format "'yyyy MMM'"
In both cases, unescaped "
characters are stripped from the argument(s), as they are assumed to have merely syntactic function for the sake of the command line rather than the resulting PowerShell command.
After stripping syntactic "
characters and joining the resulting arguments with spaces, if applicable, Powershell interprets the resulting string as PowerShell code.
Note: This differs fundamentally from how arguments are parsed when you use the -File
CLI parameter to invoke a script file and pass arguments to it - see this answer for more.