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:
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.
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