Terminating SSH session executed by bash script

2020-02-08 04:47发布

问题:

I have a script I can run locally to remotely start a server:

#!/bin/bash
ssh user@host.com <<EOF
  nohup /path/to/run.sh &
EOF
echo 'done'

After running nohup, it hangs. I have to hit ctrl-c to exit the script.

I've tried adding an explicit exit at the end of the here doc and using "-t" argument for ssh. Neither works. How do I make this script exit immediately?

EDIT: The client is OSX 10.6, server is Ubuntu.

回答1:

I think the problem is that nohup can't redirect output when you come in from ssh, it only redirects to nohup.out when it thinks it's connected to a terminal, and I the stdin override you have will prevent that, even with -t.

A workaround might be to redirect the output yourself, then the ssh client can disconnect - it's not waiting for the stream to close. Something like:

nohup /path/to/run.sh > run.log &

(This worked for me in a simple test connecting to an Ubuntu server from an OS X client.)



回答2:

The problem might be that ...

... ssh is respecting the POSIX standard when not closing the session 
if a process is still attached to the tty.

Therefore a solution might be to detach the stdin of the nohup command from the tty:

nohup /path/to/run.sh </dev/null &

See: SSH Hangs On Exit When Using nohup

Yet another approach might be to use ssh -t -t to force pseudo-tty allocation even if stdin isn't a terminal.

man ssh | less -Ip 'multiple -t'

ssh -t -t user@host.com <<EOF
  nohup /path/to/run.sh &
EOF

See: BASH spawn subshell for SSH and continue with program flow



回答3:

Redirecting the stdin of the remote host from a here document while invoking ssh without an explicit command leads to the message: Pseudo-terminal will not be allocated because stdin is not a terminal.

To avoid this message either use ssh's -T switch to tell the remote host there is no need to allocate a pseudo-terminal or explicitly specify a command (such as /bin/sh) for the remote host to execute the commands provided by the here document.

If an explicit command is given to ssh, the default is to provide no login shell in the form of a pseudo-terminal, i. e. there will be no normal login session when a command is specified (see man ssh).

Without a command specified for ssh, on the other hand, the default is to create a pseudo-tty for an interactive login session on the remote host.

- ssh user@host.com <<EOF
+ ssh -T user@host.com <<EOF
+ ssh user@host.com /bin/bash <<EOF

As a rule, ssh -t or even ssh -t -t should only be used if there are commands that expect stdin / stdout to be a terminal (such as top or vim) or if it is necessary to kill the remote shell and its children when the ssh client command finishes execution (see: ssh command unexpectedly continues on other system after ssh terminates).

As far as I can tell, the only way to combine an ssh command that does not allocate a pseudo-tty and a nohup command that writes to nohup.out on the remote host is to let the nohup command execute in a pseudo-terminal not created by the ssh mechanism. This can be done with the script command, for example, and will avoid the tcgetattr: Inappropriate ioctl for device message.

#!/bin/bash
ssh localhost /bin/sh <<EOF
  #0<&-  script -q /dev/null nohup sleep 10 1>&- &
  #0<&- script -q -c "nohup sh -c 'date; sleep 10 1>&- &'" /dev/null  # Linux
  0<&- script -q /dev/null nohup sh -c 'date; sleep 10 1>&- &'        # FreeBSD, Mac OS X
  cat nohup.out
  exit 0
EOF
echo 'done'
exit 0


回答4:

You need to add a exit 0 at the end.



标签: bash ssh