Search code examples
linuxbashsshgnugnu-screen

Is it declaratively impossible to get the returncode from a screen command over ssh?


I am writing a python program that needs to trigger long running programs through ssh, and I've decided on using screen.

I need to know the result of the program at some later point, so, following the advice here https://unix.stackexchange.com/a/219555/423606

The main issue I've run into while researching my use case is shell variable expansion. Namely, double quotes expanding $? when I didn't mean for it to.

So if we approach this problem statement as a thought experiment, we have the following requirements:

  1. must run screen over ssh
  2. must write returncode of screen command to disk

The issue I run into that makes me think this is impossible, is that there are necessarily two levels of quoting that need to happen here. Once, for the initial ssh command, and second after screen.

Let me show you what I mean.

$ ssh -t me@remote bash -c 'screen -d -m -L -Logfile ~/screenlog bash -c "$(exit 1); echo $? >> ~/screenlog.test"'
Connection to localhost closed.

Now we go to the vm to check...

me@remote:~# cat screenlog.test
0

Now let's just try running the screen command manually on the vm to verify what the code should have been.

me@remote:~# screen -d -m -L -Logfile ~/screenlog bash -c '$(exit 1); echo $? >> ~/screenlog.test'
me@remote:~# cat screenlog.test 
0
1

You might be asking at this point, why do bash -c after the screen command at all? What if we take out that level of quoting along with bash -c. Sure, let's try that.

$ ssh -t me@remote 'screen -d -m -L -Logfile ~/screenlog $(exit 1); echo $? >> ~/screenlog.test'
Connection to localhost closed.

...

me@remote:~# cat screenlog.test
0
1
0

I have to assume that there is a reason I don't understand why bash -c enables the accurate capture of the return code.

So I have demonstrated that both the ssh quoting level and the bash -c level are necessary. With two levels of quoting, and only two types of quotes, one of them being double quotes, is it a foregone conclusion that it is impossible to get the return code of a screen command run over ssh?

you can escape quotes

$ ssh -t me@remote 'screen -d -m -L -Logfile ~/screenlog bash -c \"$(exit 1); echo $? >> ~/screenlog.test\"'

Is this what you mean by escaping quotes? I just tried it and the file was never written so something broke in the command.


Solution

  • Properly quoting commands passed on ssh's commandline is complicated to get right.

    If the commands are non-interactive, "quoting hell" can be avoided by feeding them to ssh's stdin, either directly:

    ssh user@host bash <<'EOD'
    screen -d -m -L -Logfile ~/screenlog bash -c '$(exit 1); echo $? >> ~/screenlog.test'
    EOD
    

    or from a local file:

    ssh user@host bash <script
    

    Using bash (or other shell) as the command tells ssh to be non-interactive (and so it won't display MOTD, etc). The remotely invoked shell will read commands from stdin.


    Be careful that none of the commands attempts to read from stdin (unless you really intend it to do so):

    ssh user@host bash <<'EOD'
    date
    read line
    date
    echo "$line"
    date
    EOD