2

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?

mklement0
  • 382,024
  • 64
  • 607
  • 775
Okrx
  • 119
  • 1
  • 3
  • 13

2 Answers2

3

You're putting yourself in Escape Hell by mixing PowerShell, CMD and PsExec. If all you want is run an executable on a remote host, just stick with CMD and PsExec (run the command from CMD too):

PsExec.exe -i \\192.168.100.95 -u Administrador -p Test1234 cmd /c echo. ^| "%ProgramFiles(x86)%\1.exe" 2>nul

That way you just need to escape the pipe (^|) and put the path with spaces in double quotes.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • While that certainly simplifies the invocation, note how the OP mentions "struggling to use Psexec _inside of a PS script_" (emphasis mine). – mklement0 Mar 15 '16 at 03:26
  • 1
    This command works perfectly fine and I'm really grateful for your help but this needs to be run from a PS script. – Okrx Mar 15 '16 at 10:57
3

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • This is simply awesome. Great break down of how it should be done. Thank you @mklement0. I had to remove the space between ProgramFiles and (x86) to make it work but other than that your comment is flawless. – Okrx Mar 15 '16 at 11:05
  • @Okrx: My pleasure; glad to hear it. Re extraneous space: serves me right for faking the variable on my 32-bit machine... Thanks for the feedback; I've fixed the answer. – mklement0 Mar 15 '16 at 12:29