Search code examples
powershellescapingquoting

PSExec and Powershell fails to run a program located in Program Files (x86)


I am struggling to use Psexec inside of a PS script to execute an interactive program. I have tried this:

PsExec.exe -i \\192.168.100.95 -u Administrador -p Test1234 cmd /c "echo . | powershell notepad" 2> $null

... and it runs perfectly fine. Notepad is launched on a remote machine. Now, when I want to run .exe from Program Files (x86) I get absolutely nothing. I have tried this variations to run 1.exe located in ProgramFiles(x86):

PsExec.exe -i \\192.168.100.95 -u Administrador -p Test1234 cmd /c "echo . | powershell "${env:ProgramFiles(x86)}\1.exe"" 2> $null

PsExec.exe -i \\192.168.100.95 -u Administrador -p Test1234 cmd /c "echo . | powershell "${env:ProgramFiles(x86)}" + "\1.exe"" 2> $null

However none of them work. Any idea what´s wrong?


Solution

  • Try the following:

    psexec cmd /c 'echo . | powershell "& \"${env:ProgramFiles(x86)}\1.exe\"' 2>$null
    

    Note: To better focus on the fundamentals of the solution, I've simplified the psexec command, but the original command should work too.

    • The entire string passed to cmd /k is single-quoted to prevent PS from interpolating elements up front, notably ${env:ProgramFiles(x86)} whose expansion should be deferred until the command is executed on the target machine.

      • Note that you normally need a double-quoted string when you pass a command line to cmd /c when invoking from cmd.exe itself. From within PowerShell, however, this is not a requirement: PowerShell first parses the string - whether single- or double-quoted originally - interpolates, if applicable, and then passes the resulting string double-quoted to the external command.
    • Note the & \"...\" construct in the context of the powershell argument, which ensures that the path with embedded spaces is correctly executed.

      • Curiously, PS requires " chars. to be escaped as \" when a parameter is passed from the outside world (as opposed to escaping as `" inside the realm of PS).

      • The command passed to powershell as a whole must be double-quoted, because cmd.exe - in whose context powershell is invoked due to cmd /c - only recognizes double quotes as parameter delimiters and only double quotes protect the enclosed content (mostly) from interpretation.


    Why your commands didn't work:

    • The primary problem was that the executable path that you wanted powershell.exe to invoke ended up containing spaces (C:\Program Files...), causing PowerShell not to recognize the entire path as a single argument. Such a path must be (a) quoted and (b) invoked with &, the call operator.
      (In the 2nd attempt, with + ... (string concatenation), you would have had to use & also, and enclose the concatenation in (...)).

      • For debugging, using cmd /k instead of cmd /c can give you a better sense of how the command is ultimately executed (/k keeps the console window open after execution of the command).
    • A subtler point is that by using a double-quoted string overall, ${env:ProgramFiles(x86)} was expanded on the source machine rather than on the target machine, where the definition of that environment variable may or may not be the same.