ssh -t svf "cd ~/w/logs; tail -f some_file.log; exec $SHELL -l"
-t means the local tty line discipline is set to raw (pass-through) mode and a pseudo-tty is created on the remote side.
That pseudo-tty will get default settings which on most systems means for instance that a
^C character will cause a SIGINT to be sent to the foreground process group of that pseudo-terminal.
Here, when you
ssh with a command,
login-shell-of-the-remote-user -c that-command. That's a non-interactive shell, so it doesn't perform job control.
In particular, that shell like every command it runs will run in the same process group which is the foreground process group of the terminal.
$SHELL -l shell though (note that
$SHELL is expanded on the local machine which is probably not what you want) will be an interactive shell (as it's called without
-c and its stdin is a terminal device), so will run commands (pipelines) in different process groups and makes them the foreground process group of the terminal or not as necessary.
Here, to make that only
tail -f receives the SIGINT, or at least that only it and not the shell is killed, you've got several options depending on the login shell of the remote user.
If the login shell of the remote user is
yash, you can do:
ssh -t svf 'set -m; cd ~/w/logs && tail -f some_file.log; exec "$SHELL" -l'
-m option enables job-control. That tells the shell to run pipelines in separate process groups (like for interactive shells) and (for
yash, generally not for all other shells) that the process group of pipelines run in foreground is made the foreground process group of the terminal device (so that they get the
^Z for instance), and the ones started in background are not (so that they be forbidden (receive SIGTTIN) to read from the terminal for instance).
tail -f will run in its own process group, which will be the foreground process group. If you type CTRL+C while
tail is running, only
tail will receive the SIGINT.
Another alternative if you know the shell is Bourne like, is to set a handler for SIGINT:
ssh -t svf 'trap : INT; cd ~/w/logs && tail -f some_file.log; exec "$SHELL" -l'
Here, we're configuring the shell so it executes the
: (no-op) command upon receiving SIGINT. Child processes will inherit that handler, but upon executing a command, the handler will be reset to the default (of terminating the process).
So now, upon CTRL-C while
tail is running, both the shell and
tail will receive the SIGINT, but only tail will die of it, and the shell will carry on executing
If you know
yash are available on the remote host (and the remote shell is Bourne or csh like), you could also do:
ssh -t svf 'cd ~/w/logs && bash -c "set -m; tail -f some_file.log"; exec "$SHELL" -l'
We invoke the
bash shell explicitly and turn the
-m option so that
tail will be started in its own foreground process group and only it will receive the SIGINT like for the firs solution.
bash-4.3 or above, you can replace the
bash -c "set -m; with
bash -mc ". That won't work with older versions which ignore the
-i) option passed the the
bash interpreter if combined with