Search code examples
powershellsyntaxquoting

Run command line in PowerShell


I know there are lots of posts regarding this, but nothing worked for me.

I am trying to run this command line in PowerShell:

C:/Program Files (x86)/ClamWin/bin/clamd.exe --install

I have this in PowerShell:

&"C:/Program Files (x86)/ClamWin/bin/clamd.exe --install"

But all this does is execute clamd.exe, ignoring the --install parameter

How can I get the full command line to run?


Solution

  • Josef Z's comment on the question provides the solution:

    & "C:/Program Files (x86)/ClamWin/bin/clamd.exe" --install # double-quoted exe path
    

    or, given that the executable path is a literal (contains no variable references or subexpressions), using a verbatim (single-quoted) string ('...'):

    & 'C:/Program Files (x86)/ClamWin/bin/clamd.exe' --install # single-quoted exe path
    

    As for why your own solution attempt failed:

    • The call operator, &, expects only a command name/path as an argument, not a full command line.
      Invoke-Expression accepts an entire command line, but that complicates things further and can be a security risk.
    • If you want to store a whole command line in a variable, for later on-demand execution, use a script block ({ ... }): see this answer
    • To construct arguments programmatically for a call to an external program, use splatting - see this answer.

    As for why using & is the solution:

    • The need for quoting stands to reason: you need to tell PowerShell that C:/Program Files (x86)/ClamWin/bin/clamd.exe is a single token (path), despite containing embedded spaces.

    • The need for &, the so-called call operator is due to PowerShell's two fundamental parsing modes:

      • argument mode, which works like a traditional shell, where the first token is a command name, with subsequent tokens representing the arguments, which only require quoting if they contain shell metacharacters (chars. with special meaning to PowerShell, such as spaces to separate tokens);
        that is why --install need not, but can be quoted (PowerShell will simply remove the quotes for you before passing the argument to the target executable.)

      • expression mode, which works like expressions in programming languages.

    PowerShell decides based on a statement's first token what parsing mode to apply:

    If the first token is a quoted string - which we need here due to the embedded spaces in the executable path - or a variable reference (e.g., $var ...), PowerShell parses in expression mode by default.
    A quoted string or a variable reference as an expression would simply output the string / variable value.

    However, given that we want to execute the executable whose path is stored in a quoted string, we need to force argument mode, which is what the & operator ensures.


    Generally, it's important to understand that PowerShell performs nontrivial pre-processing of the command line before the target executable is invoked, so what the command line looks like in PowerShell code is generally not directly what the target executable sees.

    • If you reference a PowerShell variable on the command line and that variable contains embedded spaces, PowerShell will implicitly enclose the variable's value in double quotes before passing it on - this is discussed in this answer to the linked question.

    • PowerShell's metacharacters differ from that of cmd.exe and are more numerous (notably, , has special meaning in PowerShell (array constructor), but not cmd.exe - see this answer).

    • To simplify reuse of existing, cmd.exe-based command lines, PowerShell v3 introduced the special stop-parsing token, --%, which turns off PowerShell's normal parsing of the remainder of the command line and only interpolates cmd.exe-style environment-variable references (e.g., %USERNAME%). However, this comes with fundamental limitations - see the bottom section of this answer.