ssh Remote execution of nohup command does not exit

introduce

Under Linux system, the default user root is used
There is a script test in the home directory of the remote target machine SH, executable permission. There is only one command: sleep 10

Execute on local machine

ssh target "nohup ./test.sh &"

As a result, ssh does not exit immediately and waits for test Exit after sh execution is completed.

problem

Generally, we use the nohup command so that after the ssh connection to a server is disconnected, the previously executed commands still run normally on the server.
However, the previous phenomenon has nothing to do with the nohup command, but the ssh itself; The premise of nohup is that the user logs in to the server using ssh.
As for the relationship with nohup, I guess it is because in your impression, the execution method of the above nohup command should exit immediately, and the result contrast is too large, so it is regarded as a special problem.

resolvent

Manually specify redirection in the command
That is, replace the above command with

ssh target "nohup ./test.sh >/dev/null 2>&1 &"

Then OK

The following analysis shows that the nohup command has nothing to do with the ssh command in the "ssh host" cmd "mode (because this mode does not involve SIGHUP), so replace it with

ssh target "./test.sh >/dev/null 2>&1 &" That's it.

analysis

Generally, it deals with the task of ssh remotely executing a command. First, a child process of sshd is established on the remote target machine (the parent process is the initial sshd), and then the sshd process starts a bash process (if bash process is used) to execute the transmitted command. The sshd process established for this task has a certain relationship with the bash process in terms of file descriptors: usually, the three file descriptors of bash process are connected with the corresponding file descriptors of sshd through pipes. This can be done by finding the details of the fd directory of the corresponding processes of the established sshd process and Bash process in the / proc file system. ssh Remote execution command this way of establishing ssh connection. The sshd process displayed in ps -ef has "sshd" root@notty "Tag. This sshd process can be commanded by the command" ps -ef | grep -v grep | grep 'sshd.*notty' | awk '{print KAtex parse error: expected' EOF ', got'} 'at position 2: 2} " ̲'” Get, and the... $get of the related bash process. Remote execution of the following commands can be achieved in one step and the comparison results can be obtained:

ssh target "TMPSPID=\$(ps -ef | grep -v grep | grep  -e 'sshd.*notty' | awk '{print \$2}');echo \$TMPSPID;ls -l /proc/\$TMPSPID/fd;echo \$\$;ls -l /proc/\$\$/fd" 

If the command executed remotely is executed in the background, it can be found that the parent process of the newly started bash process has become 1, and the input descriptor 0 is redirected to / dev/null. Nohup prevents the process from being interrupted by the SIGHUP signal. Some redirection operations will also be performed during normal use, that is, when the standard I / O / errors are terminals, they will be redirected. However, when ssh executes commands remotely, these conditions are not met because the file descriptors 0, 1 and 2 (normally) are redirected to the pipeline. Therefore, no redirection will be performed when nohup is executed remotely. When the background command is executed remotely, although the standard input is redirected to / dev/null, the standard output and error are still pipes, so the sshd process started for this task will not end. Therefore, when executing remote commands, you must redirect standard output and standard errors on the command line.
For the above test SH script, several command execution methods are given below:

ssh target "./test.sh"           # Exit after the command is completed; Local Ctrl+C interrupts the ssh session without interrupting test Execution of SH (bash parent process becomes 1) (different from the behavior when the login terminal executes commands and the terminal is disconnected)
ssh target "./test.sh &"        # Exit after the command is completed; Local Ctrl+C interrupts the ssh session without interrupting test Execution of SH (bash parent process is already 1)
ssh target "nohup ./test.sh &"  # Exit after the command is completed; Local Ctrl+C interrupts the ssh session without interrupting test Execution of SH (bash parent process is already 1)
ssh target "nohup ./test.sh >/dev/null 2>&1 &"  # Start test After sh executes, it exits (bash parent process is already 1)
ssh target "./test.sh >/dev/null 2>&1 &"              # Start test After SH is executed, it will exit (the bash parent process is already 1), which also indicates that ssh does not exit has nothing to do with the nohup command itself

In fact, if you log in to target ssh first, execute/ test.sh &, and then exit ssh normally (i.e. exit command), then/ test.sh this script will not terminate, and will replace the parent process with 1; If you exit abnormally and close the connection directly, it will cause/ test.sh task terminated.

Supplement:

It seems that the above analysis is not in place, because simple commands can not show the real situation, such as execution

ssh target "./test.sh"

Execute the "ps -ef | grep 'test|notty' command on the remote machine, and the results are as follows:

ps -ef | grep 'test\|notty'
root     35929  3306  0 19:20 ?        00:00:00 sshd: root@notty
root     35931 35929  0 19:20 ?        00:00:00 /bin/bash ./test.sh"

It seems to carry out/ test. The bash process of SH is directly created by the displayed sshd process. In fact, this should not be the case. First execute a slightly more complex command:

ssh target "for w in a b c; do ./test.sh; done"

Similarly, you can see the following results by using the above view command:

ps -ef | grep 'test\|notty'
root     36219  3306  0 19:29 ?        00:00:00 sshd: root@notty
root     36221 36219  0 19:29 ?        00:00:00 bash -c for w in a b c; do ./test.sh; done
root     36228 36221  0 19:29 ?        00:00:00 /bin/bash ./test.sh

This shows that there are actually two-tier process relationships, sshd - bash -c - bash, that is, sshd first creates a bash, executes the passed string as a command in bash -c, and then creates and executes it by this bash/ test. The sub bash process of the SH script (this can create multiple). If the ssh host "cmd" command executed locally can return quickly, the following conditions must be met: the sshd process of the command object (generally sshd: root@notty ), no child process needs to wait for the end (by turning the first bash into a background process, or the first bash will immediately execute the command and exit naturally - that is, it starts some back-end child processes), and no other process has a pipeline connection relationship with it (it can be solved by redirection at the first bash or all the second layer bashs). In short, if you want the ssh host "cmd" command to return immediately, it is guaranteed to add "> / dev / null 2 > & 1 &" at the end of the whole command. Note that combined commands may need to be placed in {}, such as "{CMD;} >/ Dev / null 2 > & 1 & ". This is because redirection is only valid for a single simple command or a single composite command.
Here are some practical examples to help you understand (/ dev/null can also be a local file):

ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0</dev/null; done"

ssh does not return/ test.sh start one by one

ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0</dev/null; done &"

ssh does not return/ test.sh start one by one. The first bash (bash -c started by sshd) is executed in the background, but file descriptors 1 and 2 are also piped to sshd, so they are not returned

ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0</dev/null; done >/dev/null 2>&1 &"

ssh returns immediately/ tesh.sh start one by one

ssh target "for w in a b c; do ./test.sh ; done >/dev/null 2>&1 &"

ssh returns immediately/ test.sh start one by one; Due to execution/ test. The bash of SH is started by the first bash, and the first bash performs redirection, so the bash also inherits these redirections. In other words, the effect of this command is the same as that of the previous command, i.e. inner/ test.sh no redirection required

ssh target "for w in a b c; do ./test.sh & done >/dev/null 2>&1 &"

ssh exit now/ test.sh all started and the first bash exited

ssh target "for w in a b c; do ./test.sh & done >/dev/null 2>&1"

ssh exits immediately (why? Because after the first bash starts three. / tesh.sh bash processes, it exits; while the bash process executing. / test.sh inherits the file descriptor of the parent bash, so there is no pipeline to connect with sshd, so ssh exits

ssh target "for w in a b c; do ./test.sh & done "

ssh does not return because of execution/ test.sh is connected with sshd through pipes

ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0</dev/null & done" 

ssh returns because the first bash starts and ends after three child bashs, and there is no pipeline connection between the child Bash and sshd

ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0</dev/null & done &"

The same as above, because the first bash will exit after starting all child processes. At this time, it is meaningless to use the first bash as a background process

ssh target "./test.sh && ./test.sh >/dev/null 2>&1 &"

ssh will not return

ssh target "{ ./test.sh && ./test.sh; } >/dev/null 2>&1 &" 

ssh will return

CSDN_ Code 404: ssh Remote execution of nohup command does not exit

https://www.code404.icu/955.html

Keywords: Linux shell nohup

Added by overlordhu on Sun, 16 Jan 2022 22:41:42 +0200