powershellcmdcommand-line

PowerShell: escaping rules for CLI calls with special character sequences


I have gone through much documentation on Powershell 5.1 escaping rules, like what is described in this answer and this other answer but couldn't find a way to make code with special character sequences in the string work when it is send through cmd and powershell.

I've provide a simplify example here.

$specialChar = "`nMatch"
$rmtScript = "powershell Write-Output '$specialChar'"
cmd.exe /c $script1

Gives an error:

The string is missing the terminator: '.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : TerminatorExpectedAtEndOfString

If I remove the `n in $specialChar it works fine.

Any suggestions on how to solve this issue?

EDIT: My ultimate goal is to send a script through ssh that writes to a file, such as:

$specialChar = "`nMatch all`n"
$rmtScript = "powershell Add-Content -Force -Verbose " +
"-Path c:\ProgramData\ssh\sshd_config -Value '$specialChar'"
ssh $Username@$ComputerIP $rmtScript

The script should modify the file in Path with the format given through the special character sequences, so create a new line, write the words and new line. This code and the simplified example I added send the same error message when executed.


Solution

  • You fundamentally can not pass a multiline command to cmd /c[1] and, assuming that the ssh command you're ultimately interested in targets a Windows machine that uses the default shell,[2] which is also cmd.exe, you'll have the same problem there.

    Workaround:

    Avoid literal newlines in your PowerShell command by deferring the expansion of escape sequence `n and letting the target PowerShell instance expand it:

    # Note the use of '...' here, to prevent up-front expansion of `n
    $specialChar = '`nMatch' 
    # Note the use of embedded \"...\", to ensure that `n is expanded by PowerShell
    $rmtScript = "powershell Write-Output \`"$specialChar\`""
    cmd.exe /c $rmtScript
    

    Alternatively, use $([char 10]) or [char] 10 in expressions.

    Caveats:

    Therefore:

    $rmtScript = "powershell -NoProfile `"Write-Output `"^`"`"$specialChar`"^`"`"`""
    

    See this answer for additional information.


    [1] You can verify this as follows from PowerShell: cmd /c "echo a`nb" and cmd /c "echo a^`nb" both print just a (and using `r`n doesn't help either), i.e. the 2nd line is ignored. Therefore, something like cmd /c "powershell -c 'a`nb'" executes a syntactically broken command, powershell -c 'a, resulting in the error you saw.

    [2] However, a target server's default shell is configurable from there, and it is possible to make PowerShell the default shell, in which case you can specify PowerShell code directly in an ssh call, and you're also not subject to the single-line constraint; see the docs.