天天看點

批量在遠端執行指令

對于運維來說,同時管理多台機器是很辛苦的事情,特别是CDN運維需要同時重新啟動1000台機器的apache的話或者擷取所有機器的狀态,靠人工一個個上遠端機器上去執行非常費勁,為此我寫了一個在一台機器上批量在多台機器上執行shell指令的小程式。

這個程式是順序在各個遠端機器上執行指令,并且把遠端執行的輸出列印出來。

雖然scp指令也可以執行遠端指令,但是這個程式還有一個好處就是有逾時時間(30秒),當某條指令執行超過30秒後,這個程式會繼續執行下一台機器,而遠端的機器上的指令還會繼續執行完畢。直接使用scp執行遠端指令的話必須等指令執行完畢才能退出。

其中用到了expect:

  Expect在這個程式裡就是用來幫助自動輸入scp的密碼,Expect主要用于把需要人工互動的程式變為程式自動化完成,這個對于運維批量部署系統,批量無人值守安裝,批量執行指令,批量上傳下載下傳

 現代的Shell對程式提供了最小限度的控制(開始,停止,等等),而把互動的特性留給了使用者。 這意味着有些程式,你不能非互動的運作,比如說passwd。 有一些程式可以非互動的運作,但在很大程度上喪失了靈活性,比如說fsck。這表明Unix的工具構造邏輯開始出現問題。Expect恰恰填補了其中的一些裂痕,解決了在Unix環境中長期存在着的一些問題。

  Expect使用Tcl作為語言核心。不僅如此,不管程式是互動和還是非互動的,Expect都能運用。

1.multi_scp_shell.sh

#!/bin/bash  

#author: yifangyou  

#create time:2011-05-17  

#用來通過scp在目标機器批量執行指令  

#配置檔案格式:  

#ssh_hosts=("1.1.1.1" "2.2.2.2")  

#ssh_ports=("22" "22") 這個可以預設,預設值為22,或者個數比ssh_hosts少時,使用預設值  

#ssh_users=("root" "root") 這個可以預設,預設值為root,,或者個數比ssh_hosts少時,使用預設值  

#ssh_passwords=("323" "222") 這個可以預設,預設的話需要從指令行輸入,或者個數比ssh_hosts少時,使用指令行輸入  

#執行:sh multi_scp_shell.sh conf_file_path 'cmd' 

if [ -z "$2" ]  

then 

echo "sh multi_scp_shell.sh conf_file_path 'cmd'";  

exit;  

fi  

default_ssh_user="root" 

default_ssh_port="22";  

#upload shell script file path  

scp_upload=scp_upload.sh  

#configure file path  

conf_file=$1  

#shell command  

scp_cmd=$2  

#判斷conf_file配置檔案是存在  

if [ ! -e "$conf_file" ]  

echo "$conf_file is not exists";  

#read configure file  

source $conf_file  

#若是沒有在配置檔案裡提供密碼,則在指令行輸入  

if [ "${#ssh_passwords[@]}" = "0" ] || [ "${#ssh_passwords[@]}" -lt "${#ssh_hosts[@]}" ]  

read -p "please input password:" -s default_ssh_password  

success_hosts="";  

fail_hosts="";  

for((i=0;i<${#ssh_hosts[@]};i++))  

do  

#remote ssh host  

ssh_host=${ssh_hosts[$i]};  

if [ "$ssh_host" != "" ]  

#remote ssh port  

ssh_port=${ssh_ports[$i]};  

if [ "$ssh_port" = "" ]  

ssh_port=$default_ssh_port; #use default value  

#remote ssh user 

ssh_user=${ssh_users[$i]};  

if [ "$ssh_user" = "" ]  

ssh_user=$default_ssh_user; #use default value  

#remote ssh password 

ssh_password=${ssh_passwords[$i]};  

if [ "$ssh_password" = "" ]  

ssh_password=$default_ssh_password; #use default value  

echo "["`date +"%F %T"`"] (scp -r $ssh_user@$ssh_host:$ssh_port exe '$scp_cmd') start" 

#scp file or dir  

echo "######################################$ssh_host output start############################################################" 

/usr/bin/expect scp_shell.sh "$ssh_host" "$ssh_port" "$ssh_user" "$ssh_password" "$scp_cmd" 

if [ "$?" -eq "0" ]  

success_hosts="$success_hosts,$ssh_host" 

else 

fail_hosts="$fail_hosts,$ssh_host" 

echo "######################################$ssh_host output end############################################################" 

echo "["`date +"%F %T"`"] (scp -r $ssh_user@$ssh_host:$ssh_port exe '$scp_cmd') end" 

echo "" 

echo "ssh_host[$i]=null" 

done  

echo "success_hosts=[$success_hosts]" 

echo "fail_hosts=[$fail_hosts]" 

2.scp_shell.sh的源代碼

#!/usr/bin/expect  

set scphost "[lindex $argv 0]" 

set port "[lindex $argv 1]" 

set scpuser "[lindex $argv 2]" 

set scppw "[lindex $argv 3]" 

#要執行的shell指令  

set cmd "[lindex $argv 4]" 

spawn ssh -p $port $scpuser@$scphost "$cmd" 

set timeout 30  

expect {  

#respose: "[email protected]'s password:" 

"*password*" {  

send "$scppw\r" 

}  

#the first connect will respose "Are you sure you want to continue connecting (yes/no)? yes" 

"*yes*" {  

send "yes\r" 

expect "*password*" 

busy {send_user "\n<error:busy>";exit 1;}  

failed {send_user "\n<error:failed>";exit 2;}  

timeout {send_user "\n<error:timeout>";exit 3;}  

#Permission denied not try again  

"*denied*" {  

send_user "\n<error:Permission denied>" 

exit 4  

busy {send_user "\n<error:busy>";exit 5;}  

failed {send_user "\n<error:failed>";exit 6;}  

timeout {send_user "\n<error:timeout>";exit 7;}  

exit 0 

3.配置檔案格式scp.conf

#ssh_ports=("22" "22") #wheen port_num < host_num use default=22,or ssh_ports is undefined use 22 as default value  

#ssh_users=("root" "root") #wheen user_num < host_num use default=root,or ssh_users is undefined use root as default value  

#ssh_passwords=("323" "222") #wheen password_num < host_num use default=input password,or ssh_users is undefined use input password 

4.運作代碼

找一台機器可以和要執行指令的機器聯通,安裝好expect(可以用expect指令測試是否已經安裝過了)

把scp_shell.sh,multi_scp_shell.sh,scp.conf放到同一個目錄下,運作multi_scp_shell.sh即可

5.運作效果

<a href="http://blog.51cto.com/attachment/201107/191455154.jpg" target="_blank"></a>

     本文轉自yifangyou 51CTO部落格,原文連結:http://blog.51cto.com/yifangyou/610307,如需轉載請自行聯系原作者