Search code examples
windowsbashpowershellcygwinquotes

How to add a quotation mark in PowerShell?


I wanted to make a command to install the Scoop package manager for Windows

It should be run from the cygwin shell It works like this:

  1. Requests administrator permission
  2. Opens a PowerShell window
  3. Runs the Scoop installation command

I made this command, it looks like this:
powershell -NoProfile -ExecutionPolicy Bypass -Command 'Start-Process powershell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -NoExit -Command iex ""& {$(irm get.scoop.sh)} -RunAsAdmin""" -Verb RunAs -Wait'

This command runs, but there is one problem - there is no quotation mark next to the "&" symbol

Error photo

Error text:

At line:1 char:5
+ iex & {# Issue Tracker: https://github.com/ScoopInstaller/Install/iss ...
+     ~
The ampersand (&) character is not allowed. The & operator is reserved for future use; wrap an ampersand in double quot
ation marks ("&") to pass it as part of a string.
At line:339 char:30
+         set args=%args:(=``(%,
+                              ~
Missing argument in parameter list.
At line:689 char:31
+ $ErrorActionPreference = 'Stop'
+                               ~
The string is missing the terminator: '.
At line:241 char:30
+         if ($retries -eq 10) {
+                              ~
Missing closing '}' in statement block or type definition.
At line:240 char:29
+     while ($retries -le 10) {
+                             ~
Missing closing '}' in statement block or type definition.
At line:108 char:33
+ function Test-ValidateParameter {
+                                 ~
Missing closing '}' in statement block or type definition.
At line:1 char:7
+ iex & {# Issue Tracker: https://github.com/ScoopInstaller/Install/iss ...
+       ~
Missing closing '}' in statement block or type definition.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : AmpersandNotAllowed

As you can see, the script is loaded and enclosed in curly braces, but still not executed because there is no quotation mark next to the "&" character

And now it is interpreted as follows:

iex & {<scoop installation script>} -RunAsAdmin

And it should be like this:

iex "& {<scoop installation script>} -RunAsAdmin"

I took the script itself from the official Scoop installation documentation - https://github.com/ScoopInstaller/Install#for-admin

So please help me put quotes in my install command 🙏


Thank you in advance!


Solution

  • Preface:

    • Note the Cygwin context, which means that the syntax rules of POSIX-compatible shells such as bash initially apply; notably this means:
      • The embedded " are automatically escaped as \" for the outer powershell.exe process command line.
      • When the outer powershell.exe process then makes the inner powershell.exe call, it constructs a raw Windows process command line, where manual \-escaping of embedded " chars. is necessary.

    Solution:

    • Escape the embedded "" as \"":

      • "" (as used by you) escapes the " for inclusion inside the enclosing "..." string in the outer powershell.exe call

      • \ then escapes the resulting " for use with the inner powershell.exe -Command CLI call.

        • With -Command, the PowerShell CLI removes unescaped " instances during command-line parsing, before interpreting the resulting code as PowerShell code; this is the initial problem you ran into - see this answer for details.
      • Note: \`" is an alternative to \"", given that " inside "..." can also be escaped as `"

    • `-escape the $ in $(irm get.scoop.sh)

      • Because the outer powershell.exe call uses "..." quoting around its -ArgumentList value, the $ inside it must be `-escaped in order to prevent premature expansion (interpolation), given that the intent is for the inner - then-elevated - powershell.exe call to interpret the $(...) subexpression.
    powershell -NoProfile -ExecutionPolicy Bypass -Command 'Start-Process powershell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -NoExit -Command iex \""& {`$(irm get.scoop.sh)} -RunAsAdmin\""" -Verb RunAs -Wait'