天天看点

批量在远程执行命令

对于运维来说,同时管理多台机器是很辛苦的事情,特别是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,如需转载请自行联系原作者