介绍


Linux系统下,使用默认用户root
远程target机器的主目录下有个脚本test.sh,可执行权限,内容只有一条命令:sleep 10

在本地机器上执行

ssh target "nohup ./test.sh &"

结果ssh不立即退出,等test.sh执行完毕之后才退出。

问题


一般我们使用nohup命令是为了在断开到某个服务器的ssh连接之后,之前执行的命令仍然正常地在服务器运行。
但是前面的现象其实与nohup命令没有什么关系,只是ssh本身的问题;nohup其作用的前提是用户使用ssh登录到服务器上。
至于跟nohup扯上关系,我猜是因为在大家的印象中上面这种nohup命令的执行方式应该是立即退出的,结果反差太大,所以当作了一个特别问题。

解决方法


手动在命令里面指定重定向
即上面的命令换成

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

然后就OK

下面的分析表明了nohup命令与“ssh host “cmd””方式的ssh命令没有任何关系(因为这种方式不会涉及SIGHUP),所以换成

ssh target "./test.sh >/dev/null 2>&1 &" 就可以了。

分析


一般处理ssh远程执行某个命令的任务,在远程目标机器上先建立一个sshd的子进程(父进程是最初始的sshd),然后由这个sshd进程启动一个bash进程(如果使用bash进程)来执行传递过来的命令。 针对这次任务建立的sshd进程和bash进程在文件描述符方面有一定关系:通常bash进程的0 1 2三个文件描述符通过管道与sshd的相应文件描述符联系起来。这可以通过查找建立的sshd进程和bash进程在/proc文件系统下的相应进程的fd目录的详细情况。ssh远程执行命令这种建立ssh连接的方式在ps -ef 中显示的sshd进程是有"sshd root@notty"标记。此sshd进程的命令可以通过命令“ps -ef | grep -v grep | grep ‘sshd.*notty’ | awk '{print KaTeX parse error: Expected 'EOF', got '}' at position 2: 2}̲'”得到,而相关bash进程的…$获取。远程执行下面命令可以一步到位,得到比较结果:

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"

如果远程执行的命令是后台执行,那么可以发现新启动的bash进程的父进程成了1,而输入即描述符0重定向到了/dev/null。 nohup是防止进程被SIGHUP信号中断,正常使用的时候也会进行一些重定向操作,即当标准输入/输出/错误等是终端的时候,会对它们进行重定向。但是ssh远程执行命令时,这些条件都不满足,因为文件描述符0,1,2(正常情况下)都被重定向到管道了。所以远程执行nohup时不会进行相关重定向操作。而当远程执行后台命令的时候,虽然标准输入被重定向到了/dev/null,但是标准输出和错误还是管道, 所以针对这次任务启动的sshd进程还不会结束。所以执行远程命令时,还必须自己在命令行上重定向标准输出和标准错误才行。
对于上面的test.sh脚本,下面给出几种命令执行执行方式:

ssh target "./test.sh"           # 等待命令完成后退出;本地Ctrl+C中断ssh会话,不会中断test.sh的执行(bash父进程变为1)(与登录终端执行命令而终端连接断开时的行为不一样)
ssh target "./test.sh &"        # 等待命令完成后退出;本地Ctrl+C中断ssh会话,不会中断test.sh的执行(bash父进程本来就为1)
ssh target "nohup ./test.sh &"  # 等待命令完成后退出;本地Ctrl+C中断ssh会话,不会中断test.sh的执行(bash父进程本来就为1)
ssh target "nohup ./test.sh >/dev/null 2>&1 &"  # 启动test.sh执行后就会退出(bash父进程本来就为1)
ssh target "./test.sh >/dev/null 2>&1 &"              # 启动test.sh执行后就会退出(bash父进程本来就为1),这也表明ssh不退出与nohup命令本身没有什么关系

实际上如先ssh登录target,执行./test.sh &,然后正常退出ssh(即exit命令),那么./test.sh这个脚本也不会终止,而且会将父进程换成1;如果不正常退出,而是直接关闭连接,那么会导致./test.sh任务终止。

补充:


感觉上面的分析还不是很到位,因为简单命令还不能够显示出真实情况,比如执行

ssh target "./test.sh"

在远程机器上执行“ps -ef | grep ‘test|notty’”命令,结果如下

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”

好像执行./test.sh的bash进程直接由显示的sshd进程创建,其实情况应该不是这样的。先执行一个稍复杂的命令:

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

同样使用上面的查看命令可以看到如下结果:

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

这就表明了其实有两层进程关系,sshd - bash - c - bash,即sshd 先创建一个bash以bash -c的方式执行传递过来的作为命令的字符串,然后再由这个bash创建执行./test.sh脚本的子bash进程(这个可以创建多个)。而本地执行ssh host "cmd"形式命令要能迅速返回,必须满足的条件是:该命令对象的sshd进程(一般是sshd: root@notty),没有子进程需要等待结束(靠将第一个bash搞成后台进程,或者第一个bash会立即执行完命令自然退出——即它启动一些后台子进程), 而且没有其他进程与它有管道连接关系(靠重定向解决,在第一个bash处或者所有第二层bash处都可以)。简而言之,要ssh host "cmd"形式命令立即返回,在整个命令最后面添加“>/dev/null 2>&1 &”,是有保证的。注意,对于组合的命令, 可能需要放到{}中才行,比如“{ cmd; } >/dev/null 2>&1 &”这样的形式。这是因为重定向只对单个简单命令或单个复合命令有效。
下面通过一些实际例子的情况帮助大家认识(/dev/null也可以是某个本地文件):

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

ssh不返回,./test.sh一个一个启动

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

ssh 不返回,./test.sh一个一个启动。 第一个bash(由sshd启动的bash -c)是后台执行的,但是文件描述符1和2还与sshd有管道连接,所以不返回

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

ssh立即返回,./tesh.sh一个接一个启动

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

ssh立即返回,./test.sh一个接一个启动;由于执行./test.sh的bash是由第一个bash启动的,而第一个bash执行了重定向,所以该bash也继承了这些重定向,换言之这条命令与上条命令的效果一样,即内层的./test.sh无需重定向了

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

ssh立即退出,./test.sh全部启动,第一个bash也退出了

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

ssh立即退出(为什么?因为第一个bash启动三个./tesh.sh的bash进程后,退出了;而执行./test.sh的bash进程因为继承了父bash的文件描述符,所以没有管道与sshd连接,因此ssh退出

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

ssh不返回,因为执行./test.sh与sshd还有管道连接

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

ssh返回,因为第一个bash启动三个子bash之后结束,而子bash与sshd之间又没有管道上连接

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

同上,因为第一个bash启动所有子进程后会退出,此时将第一个bash作为后台进程已经意义不大

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

ssh不会返回

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

ssh会返回

CSDN_码404:ssh远程执行nohup命令不退出

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

ssh远程执行nohup命令不退出相关推荐

  1. ssh远程执行nohup命令

    通常我们使用ssh远程执行脚本时可以如下操作: ssh root@192.168.0.100 "./my.sh" 但是如果我们想用nohup拉起脚本,让脚本后台运行,这样是不行的, ...

  2. ssh远程执行linux命令nohup,ssh 远程执行命令 nohup 无效问题

    昨夜1:00多准备睡觉了,突然一哥们咨询了我一个问题. 他A机器上远程执行B机器(ssh user@ip "command")上的脚本,B上的服务并没有起来.看了下截图,脚本确实是 ...

  3. ssh-keygen产生公钥与私钥对,及密钥分发,ssh远程执行常用命令方法,和如何防止SSH登录入侵或被破解

    前言 SSH是安全的加密协议,用于远程连接Linux服务器,默认端口是22,安全协议版本是SSH2 . SSH原理 SSH(远程连接工具)连接原理:ssh服务是一个守护进程(demon),系统后台监听 ...

  4. ssh远程执行oracle命令,ssh远程执行命令技巧

    ssh可以直接在本地显示远程计算机所执行的命令 远程ip:192.168.4.36 本地:local5 [root@local5 ~] # ssh 192.168.4.36 df && ...

  5. Python ssh 远程执行shell命令

    #工具 python paramiko #远程执行命令 import paramikossh = paramiko.SSHClient() key = paramiko.AutoAddPolicy() ...

  6. ssh远程执行服务器命令,ssh远程连接服务器执行命令

    问题 首先说一下使用ssh远程连接服务器执行命令的方法: 为了方便描述,这里把测试服务器称之为A1,目标服务器称之为A2 A1与A2之间首先要建立ssh免密登录,在A1上生成公钥和私钥 ssh-key ...

  7. linux 带环境变量 远程执行,SSH远程执行命令环境变量问题

    SSH命令格式 usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address: ...

  8. SSH远程执行命令环境变量问题

    SSH命令格式 usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec][-D [bind_address:] ...

  9. ssh远程执行多个命令

    shell远程执行: 经常需要远程到其他节点上执行一些shell命令,如果分别ssh到每台主机上再去执行很麻烦,因此能有个集中管理的方式就好了.一下介绍两种shell命令远程执行的方法. 前提条件: ...

最新文章

  1. pylons中常用的paster命令
  2. 手把手带你撸一把springsecurity框架源码中的认证流程
  3. 误人子弟的网络,谈谈HTTP协议中的短轮询、长轮询、长连接和短连接(转载)
  4. Spark运行原理剖析
  5. AI开发者十问:10分钟了解AI开发的基本过程
  6. 易语言c调用,易语言调用C++编写的DLL
  7. Swift - 多线程实现方式(3) - Grand Central Dispatch(GCD)
  8. Android手机模拟器如何把语言设置为中文
  9. tortoise清理本地分支_使用TortorliseGit(小乌龟)删除本地分支,远程分支
  10. 南阳理工acm,水仙花数
  11. 学习笔记:修改网吧计费系统
  12. druid数据库连接池 数据库配置密码加密
  13. 投票系统C语言程序,C语言课程投票程序系统.doc
  14. ImageMagick将多张图片拼接成一张图片_高逼格九宫格图片,2020年朋友圈图片,自己做不求人...
  15. linux mysql 僵尸进程_Linux 系统中僵尸进程
  16. 后端好书阅读与推荐(续四)
  17. Emmet语法的使用
  18. 给通信专业研究生——安心完成培养,你不是在劣势下和计算机学生抢饭碗来的
  19. 2020年4月区块链安全大事件 | 黑客攻击早已蓄谋已久
  20. pass all options of select from View to controller

热门文章

  1. java swap函数_Java 利用swap函数交换两个整型数据值
  2. git找回本地被覆盖或者删除的文件/确保本地代码为最新时,强制使本地文件覆盖git库文件
  3. Metasploit(3) 渗透测试之通过客户端进行渗透
  4. 美团、青桔、哈啰共享单车“三国杀”,谁才是混战最后的赢家?
  5. python pywin32-ctypes模块_Python PyWin32 模块
  6. TI Cotex M3/4单片机关于寄存器操作详解
  7. JavaWeb - 基础面试题(详细)
  8. 窄带物联网迎来投资热 智慧城市扩张初现
  9. Java知识点(二)
  10. Linux RCU机制+内存屏障