powershellcmdescapingspecial-characters

How to make ($Line_1`n$Line_2) work in a CMD script?


I use the following code in a CMD script file

PowerShell Add-Type -AssemblyName System.Windows.Forms;^
$Line_1 = 'Hello!';^
$Line_2 = 'How are you?';^
[System.Windows.Forms.MessageBox]::Show($Line_1)

The above will show only ($Line_1)
If ($Line_1`n$Line_2) is used, nothing will be shown.

How do I make it show both $Line_1 and $Line_2?


Solution

  • Obviously $Line_1 + "`n" + $Line_2 and "$Line_1`n$Line_2" works normally. It's just tricky to send the command string to PowerShell from cmd with its legacy quirks because:

    According to the documentation PowerShell expects the command in a single string in the last parameter (which isn't quite true since the document wasn't updated correctly), so you need to quote the whole thing or escape all the delimiters. The easiest solution is to use a single line and escape the quotes in "`n" like this

    PowerShell "Add-Type -AssemblyName System.Windows.Forms; $Line_1 = 'Hello!'; $Line_2 = 'How are you?'; [System.Windows.Forms.MessageBox]::Show($Line_1 + \"`n\" + $Line_2)"
    

    If you want to put the commands in multiple lines then you can't quote the string. To put the whole thing as a single argument now you need to escape all the spaces (somehow you don't need to escape ; in this case, possibly because after the command line is passed to PowerShell, it calls GetCommandLineW and parses again the whole thing itself)

    PowerShell Add-Type^ -AssemblyName^ System.Windows.Forms;^
    $Line_1^ =^ 'Hello!';^
    $Line_2^ =^ 'How^ are^ you?';^
    [Windows.Forms.MessageBox]::Show($Line_1^ +^ \"`n\"^ +^ $Line_2)"
    

    Alternatively you can avoid that "`n" string by getting the new line directly with [char]10

    PowerShell -Command Add-Type -AssemblyName System.Windows.Forms;^
    $Line_1 = 'Hello!';^
    $Line_2 = 'How are you?';^
    [System.Windows.Forms.MessageBox]::Show($Line_1 + [char]10 + $Line_2)
    

    Finally a solution that works without any escaping which utilizes the EncodedCommand option of PowerShell which receives the base64 encoded string of a UTF-16 command string. You can get the encoded version by running this in PowerShell

    $str = @'
    >> Add-Type -AssemblyName System.Windows.Forms;
    >> $Line_1 = 'Hello!';
    >> $Line_2 = 'How are you?';
    >> [System.Windows.Forms.MessageBox]::Show($Line_1 + "`n" + $Line_2)
    >> '@
    PS C:\Users> [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($str))
    QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7AAoAIAA9ACAAJwBIAGUAbABsAG8AIQAnADsACgAgAD0AIAAnAEgAbwB3ACAAYQByAGUAIAB5AG8AdQA/ACcAOwAKAFsAUwB5AHMAdABlAG0ALgBXAGkAbgBkAG8AdwBzAC4ARgBvAHIAbQBzAC4ATQBlAHMAcwBhAGcAZQBCAG8AeABdADoAOgBTAGgAbwB3ACgAIAArACAAIgAKACIAIAArACAAKQA=
    

    After having the encoded version you can call this from cmd

    PowerShell -EncodedCommand QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7AAoAJABMAGkAbgBlAF8AMQAgAD0AIAAnAEgAZQBsAGwAbwAhACcAOwAKACQATABpAG4AZQBfADIAIAA9ACAAJwBIAG8AdwAgAGEAcgBlACAAeQBvAHUAPwAnADsACgBbAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwAuAE0AZQBzAHMAYQBnAGUAQgBvAHgAXQA6ADoAUwBoAG8AdwAoACQATABpAG4AZQBfADEAIAArACAAIgBgAG4AIgAgACsAIAAkAEwAaQBuAGUAXwAyACkA