Analysis
Your command:
gnome-terminal -- ssh -L …
when run from a shell or from a shell script runs synchronously. The shell waits for it to exit before accepting the next command. The important thing is gnome-terminal
does few things to exit early.
If gnome-terminal
runs from within gnome-terminal
(so the environment contains GNOME_TERMINAL_*
variables) then the new gnome-terminal
process will delegate the task to the already running gnome-terminal
and exit.
If not from within gnome-terminal
then yet another new gnome-terminal
will be created to handle the task with no connection to the original terminal and your script. The gnome-terminal
process your script spawned will exit after delegating the task.
So in any case ssh
will run under gnome-terminal
that is not a child of your script. The child of your script will exit early and allow the script to continue.
With konsole
or xterm
it's different. The very process your script spawns handles the task of being the terminal emulator for ssh
, so your script doesn't continue until you close the new window.
Solutions
There are few solutions:
Use gnome-terminal
. You don't need full Gnome desktop to use this program. I use KDE Plasma and I have installed gnome-terminal
for testing, to be able to answer questions like this.
Your script will (still) depend on gnome-terminal
.
Spawn xterm
(or konsole
or whatever) asynchronously: xterm -e ssh -L … &
. I think this may be the simplest solution. The xterm
process will be a child of your script but the script won't wait for it. Note the script cannot easily tell if ssh
authenticated yet and if port forwarding works; but I guess the same problem occurs with gnome-terminal
, so probably your script takes this into consideration.
Your script will depend on xterm
(or konsole
or whatever).
Use a terminal multiplexer: screen
or tmux
.
Your script will depend on screen
or tmux
. This seems no better than the above alternatives, but these terminal multiplexers are not associated to any desktop environment and they work even without any desktop. I'm mentioning them to make the answer more complete.
Run ssh
without creating any additional terminal. See below.
Your script will get rid of one dependency.
Running ssh
without creating additional terminal
In many cases this solution is the most elegant. It may or may not be good for you. You haven't revealed what your ssh
does on the remote side. If it's only about forwarding ports (so you can use ssh -N
) or if the command to run on the remote side is non-interactive then you should use ssh -f
:
-f
Requests ssh
to go to background just before command execution. This is useful if ssh
is going to ask for passwords or passphrases, but the user wants it in the background. […]
Even if your ssh
is not going to ask for passwords/passphrases, -f
is better than ssh … &
because you can use it with -o ExitOnForwardFailure
and check its exit status before you assume the tunnel works.
Additionally use -o ControlMaster=yes -o ControlPath=…
(in short -MS …
) so you can easily terminate the master ssh
later with -O exit
. In this answer of mine you will find a script that does this. A perfect script would terminate the master connection and clean up before exiting, even if terminated with Ctrl+C (use trap
).
OTOH if you can set things up so ssh
does not ask for anything then consider autossh
in the background (autossh … &
) to create a tunnel and keep trying to renew it in case the connection breaks. See the last part of this other answer.