powershellsshopenssh

How to get return code of program executed on remote server from powershell?


Very important, this is powershell specific.

There are many answers to variations of this question whose answer is to use single quotes instead of double quotes

e.g. ssh me@server "invalid_program; echo $?" -> ssh me@server 'invalid_program; echo $?'

This is not possible on windows in powershell with openssh. https://stackoverflow.com/a/75740191/8652920

Given there is a restriction that I have to ssh with double quotes only, is there any way to still force the environment variable expansion to happen on remote?

You can try this on your powershell.

PS C:\...> ssh me@remote "beep; echo $?"
Warning: Permanently added 'remote' (ECDSA) to the list of known hosts.
bash: beep: command not found
True

The stdout should be 127, not True.

% ssh me@remote 'beep; echo $?'
Warning: Permanently added 'remote' (ED25519) to the list of known hosts.
127
bash: beep: command not found

Solution

  • Preface:

    This [using single quotes] is not possible on windows in powershell with openssh.


    $ is a metacharacter in both PowerShell and POSIX-compatible shells such as bash, and in both shells "..." is an expandable i.e. interpolating string.

    Therefore, in ssh me@remote "beep; echo $?", it is PowerShell that expands $?, up front,[2] using the then-current value of its definition of $?, which is a Boolean value.

    To prevent unwanted up-front interpolation:


    [1] That is, PowerShell passes arguments that do not contain spaces unquoted on the process command line constructed behind the scenes. Unfortunately, this can break invocation of batch files (*.cmd, *.bat), because cmd.exe, the batch-file interpreter - inappropriately - parses its command line as if it had been passed from inside a cmd.exe session. Thus, an invocation from PowerShell such as .\foo.cmd 'a&b' breaks and requires awkward workarounds. GitHub issue #15143 is a proposal to prevent such problems, but, regrettably, it died from neglect. Note that in Unix-like environments this point is moot, because there are no process command lines - argument are invariably passed as an array of verbatim strings.

    [2] It follows from the above that if you were to run this command from a POSIX-compatible shell (i.e. from a Unix environment), undesired up-front interpolation would occur too, except that a number would be interpolated, namely the most recently executed command's exit code, which is what $? reflects in those shells.