Explanation
It's not about wait
. I think this is what happens:
You use <<
, so stdin
of ssh
is redirected to some descriptor through which the entire here document flows.
This other answer explains how ssh
is able to capture Ctrl+C when -t
is used. This is what matters:
On the client side, ssh
will try to set the tty
used by stdin
to "raw" mode […] Setting raw mode means that characters that would normally send signals (such as Ctrl+C) are instead just inserted into the input stream.
In your case it's not the same stdin
your local shell uses. tty
used by your local shell is left intact, it is never set to "raw" mode.
So when you hit Ctrl+C it acts locally and terminates ssh
. At this moment the remote side gets SIGHUP
. Your trap
works and kills java
. I think there's a pitfall here: a trap
executes some code in response to a given signal but it doesn't prevent the signal from having its normal effect. Therefore it looks to me your java
would be killed even without a trap
because it's a job of the shell that gets terminated in response to SIGHUP
.
The shell that gets terminated stops reading and interpreting its stdin
. That's why everything that follows wait
is discarded.
Solution
commands() { cat <<-'COMMANDS'
cleanup() {
# cleanup code here
echo "Done. Logging out"
sleep 2
logout
}
echo "Preparing execution"
java -jar execute.jar &
executePID=$!
echo "Ready."
echo "CTRL+C to clean and close"
trap "kill $executePID; cleanup" INT HUP
wait $executePID
cleanup
COMMANDS
}
stty raw -echo; cat <(commands) - | ssh -t [email protected]; stty -raw echo
The last line is the actual command. First we prepare tty
so Ctrl+C cannot act locally. Then we concatenate commands and standard input, we pass it to the remote shell. I cannot do this with here document directly, the command
function is a workaround (it would be easier to use a regular file but you said you cannot use more than one). After ssh
and cat
exit we set the tty
to its normal state.
Having pseudo-terminal is essential so make sure ssh -t
works (use -tt
if needed).
The remote shell must read (buffer) all the commands from commands
, only then it can get Ctrl+C when you press it. I guess this means you cannot have too much code after wait
. Additionally whatever you want to do after SIGINT
must be run from inside the trap
. These are the reasons I used a single cleanup
function that does it all.
The code is not foolproof. Hit Ctrl+C too early or multiple times and you'll find yourself in the remote shell or with somewhat "broken" local tty
. In this latter case use the command reset
to reset your tty
.
You must press a key (e.g. Enter) after you see "Connection to … closed." The reason is cat
won't notice the pipe is broken (due to ssh
being no more) until it tries to write something to it.
Alternative
If for any reason the above solution doesn't work for you, use this simpler alternative. The here document is exactly as before. In this case we don't mess with tty
, Ctrl+C terminates local ssh
as it does with your original code. The difference (with respect to your code) is the trap
does the cleaning. I think it would be enough to trap SIGHUP
only.
ssh -t [email protected] <<-'COMMANDS'
cleanup() {
# cleanup code here
echo "Done. Logging out"
sleep 2
logout
}
echo "Preparing execution"
java -jar execute.jar &
executePID=$!
echo "Ready."
echo "CTRL+C to clean and close"
trap "kill $executePID; cleanup" INT HUP
wait $executePID
cleanup
COMMANDS
Note: when the trap
is triggered cleanup
echoes a message but you won't see it because your local ssh
is already disconnected. You will see the message only if java
exits without the trap
. Still your cleanup code (# cleanup code here
) should be executed.
wait $executePID
outside of the ssh command? You could then replace $executePID with the PID of the ssh command instead of the Java command. Also, are you saying that if you execute this script & ctrl-c it then you want it to run some additional commands to cleanup the remote host? If so, then what you need to look at creating your own trap handling function usingtrap ctrl_c INT
and then writing a function called ctrl_c that will be called if a SIGINT is triggered see this blog post.&
) so what might be happening is that your script launches the java process and that is where the ctrl-c is hitting the command. Are you definitely seeing the 'Ready' & 'CTRL-C' echos when you execute your script?&
in that example script. I'll fix it.