判斷ssh遠程命令是否執行結束

bash&shell系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.htmlhtml


注:這是一個沒什麼鳥用的功能。不過也算是一種拓展。node

一般在那些"一鍵化部署"的shell腳本中,可能須要使用ssh執行遠程命令來實現一些簡單的自動化,這些遠程命令可能須要執行一段時間才能結束(如yum命令)。例如,遠程ssh配置yum源,遠程ssh安裝軟件包。shell

爲了讓腳本實現"並行"執行,這個遠程ssh命令每每還會加上"-f"選項使其進入後臺執行。此時,若是後續的遠程任務正好要依賴於這個命令已經執行完成,那麼咱們要判斷前面的任務是否執行完成。例如,在配置軟件的時候,必須先判斷軟件是否安裝結束。數組

判斷的方式挺簡單,只需判斷這個ssh進程是否存在就能夠了。例如:bash

[root@node1 ~]# ssh 192.168.100.101 -f 'yum makecache'
[root@node1 ~]# killall -s 0 ssh
[root@node1 ~]# echo $?

這樣的方法沒錯,也能應付絕大多數狀況,但若是有多個遠程後臺命令的ssh進程,就沒法知道具體是哪一個ssh進程。session

因而,能夠採用另外一種方法,將執行遠程命令的ssh進程放進後臺,再用$!來獲取最近的後臺ssh進程。例如:ssh

[root@node1 ~]# ssh 192.168.100.101 -f 'yum makecache' & echo $!
[1] 76115
76115

但這是錯誤的方法,若是你如今去查看ssh進程,你會發現進程號不是76115。tcp

[root@node1 ~]# pstree -p | grep 'ssh('
           |-ssh(76116)

由於ssh在執行遠程後臺命令(加上"-f"選項)的時候,它自身會在創建ssh鏈接後再fork一個後臺ssh進程用來執行遠程命令。ui

也就是說,當ssh執行遠程後臺命令的時候,會有兩個ssh進程spa

  • 第一個ssh進程是初始ssh進程,用於創建鏈接、發送要執行的命令、fork新的ssh進程等,當這些任務結束後,這個ssh進程會消逝;
  • 第二個ssh進程是第一個ssh進程fork出來的新進程(調試的結果將顯示"debug1: forking to background"),用來執行遠程命令。它是後臺進程,掛靠在init/systemd進程下。當遠程命令執行結束時,這個後臺ssh進程會消逝。

注意,只有使用了"-f"選項,第一個ssh進程纔會fork新的後臺ssh進程,由於前臺的任務(沒有使用"-f")能夠直接在第一個ssh進程上執行。

第二個後臺ssh進程沒法用$!捕捉,$!捕捉到的只是&的後臺,而對於ssh ... &中的"&"來講,它是將ssh鏈接進程(即第一個ssh進程)置於後臺,而不是將fork出來的ssh後臺進程再放入後臺。因此上面的"echo $!"的結果76115比後臺ssh進程號76116要小。

那麼有什麼好方法能夠判斷多個遠程ssh進程中的每個?絕大多數時候都能使用的方式是直接從$!的結果加1來判斷ssh的進程號。可是極少數狀況下,fork出來的進程號不必定會是加1的。若是想要無比精確的判斷,我我的沒有想到好方法,只能經過比較愚笨的方式來實現判斷:將每一個後臺ssh進程的pid號保存起來(存放到每一個變量中,或數組中)。

例如,有兩個執行遠程命令的ssh進程:

ssh 192.168.100.101 -f 'sleep 50'
ssh_pid1=`ps x | awk '/ssh.*slee[p]/{print $1}' | tail -1`
ssh 192.168.100.101 -f 'sleep 60'
ssh_pid2=`ps x | awk '/ssh.*slee[p]/{print $1}' | tail -1`

# ssh_pid1 finished?
kill -0 $ssh_pid1
echo $?

# ssh_pid2 finished?
kill -0 $ssh_pid2
echo $?

最後補上ssh鏈接或執行遠程命令時,內部過程的詳細信息。這些信息使用ssh -vvv便可獲取,此處給出的是篩選後的一小部分。

當ssh創建鏈接或執行前臺遠程命令(沒有使用"-f"選項)時:

OpenSSH_6.6.1, OpenSSL 1.0.1e-fips 11 Feb 2013
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 56: Applying options for *
debug2: ssh_connect: needpriv 0
debug1: Connecting to 192.168.100.101 [192.168.100.101] port 22.
debug1: Connection established                          # tcp鏈接創建
.....................
debug1: Authentication succeeded (publickey).
Authenticated to 192.168.100.101 ([192.168.100.101]:22).  # 用戶認證成功
debug1: channel 0: new [client-session]
debug3: ssh_session2_open: channel_new: 0
debug2: channel 0: send open                  # ssh鏈接創建
debug1: Requesting no-more-sessions@openssh.com
debug1: Entering interactive session.         # ssh鏈接進程進入交互式模式


[1]+  Stopped                 ssh -vvvv 192.168.100.101

當執行遠程後臺任務時(加上"-f"選項):

[root@node1 ~]# ssh -vvv 192.168.100.101 -f 'sleep 50' & echo $! 
[1] 65570
65570                 # echo $!獲得的上一個後臺進程位65570
[root@node1 ~]# OpenSSH_6.6.1, OpenSSL 1.0.1e-fips 11 Feb 2013
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 56: Applying options for *
debug2: ssh_connect: needpriv 0
debug1: Connecting to 192.168.100.101 [192.168.100.101] port 22.
debug1: Connection established.        # tcp鏈接創建
....................................
debug1: Authentication succeeded (publickey).     # 用戶認證成功
Authenticated to 192.168.100.101 ([192.168.100.101]:22).
debug2: fd 4 setting O_NONBLOCK
debug1: channel 0: new [client-session]
debug3: ssh_session2_open: channel_new: 0
debug2: channel 0: send open                 # ssh鏈接創建
debug1: Requesting no-more-sessions@openssh.com
debug1: forking to background             # 注意此處:fork一個新ssh進程到後臺
debug1: Entering interactive session.     # ssh鏈接進程進入交互式模式
debug2: callback start
......................................
相關文章
相關標籤/搜索