ssh遠端執行nohup命令不退出

2021-07-08 10:06:28 字數 4312 閱讀 8990

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 ''」得到,而相關bash程序的pid可用$$獲取。遠端執行下面命令可以一步到位,得到比較結果:

ssh target "tmpspid=\$(ps -ef | grep -v grep | grep  -e 'sshd.*notty' | awk '');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'」命令,結果如下

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"

同樣使用上面的檢視命令可以看到如下結果:

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 &」,是***的。注意,對於組合的命令, 可能需要放到{}中才行,比如「 >/dev/null 2>&1 &」這樣的形式。這是因為重定向只對單個簡單命令或單個復合命令有效。

ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0/dev/null 2>&1 0/dev/null 2>&1 0/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 2>&1 0

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

ssh不會返回
ssh target " >/dev/null 2>&1 &"
ssh會返回

ssh遠端執行命令

使用ssh不僅可以登入到遠端的linux主機,還可以執行命令。如果是要登入到遠端linux主機,那麼使用下面的命令 ssh username remoteip example ssh hduser 192.168.0.1 這個將開啟乙個登入到遠端主機192.168.0.1的login shell如果...

ssh遠端執行命令

1.遠端執行普通命令 ssh username ip cmd1 cmd2 多條命令間用分號分割 ssh l username 192.168.1.1等同於 ssh username 192.168.1.1 2.遠端執行需要互動的命令 例如top命令,直接執行ssh username ip top 會...

模擬ssh遠端執行命令

目錄python從入門到放棄完整教程目錄 from socket import import subprocess server socket af inet,sock stream server.bind 127.0.0.1 8000 server.listen 5 print start.whi...