前言
为了参与http://bbs.chinaunix.net/thread-3757201-9-1.html的这个讨论,整理一下以前写的脚本。现在看来,这个脚本写的很简陋,并发操作也只是分批的控制而不是完全的并行,不过工作起来还算简单稳定。
概述
此脚本使用 expect 来对需要交互输入用户名和密码等操作进行自动化,并有对于主机提示自动加入信任列表、操作失败记录日志等功能。
正文
这个脚本分三个文件,逐一附录如下:
1. 主脚本:batch_ssh .sh
3. expect 的函数库文件:func.exp,现在看来,也没有再新加过函数……不过这个远大蓝图还是曾经存在过的哈
-------------------------------------------------------------------------------------- 对于常用的 scp 操作,有对上述脚本做了针对性的调整,以更加方便的使用:
1. 主脚本 batch_scp.sh
2. remote_exec.exp
3. func.exp
为了参与http://bbs.chinaunix.net/thread-3757201-9-1.html的这个讨论,整理一下以前写的脚本。现在看来,这个脚本写的很简陋,并发操作也只是分批的控制而不是完全的并行,不过工作起来还算简单稳定。
概述
此脚本使用 expect 来对需要交互输入用户名和密码等操作进行自动化,并有对于主机提示自动加入信任列表、操作失败记录日志等功能。
正文
这个脚本分三个文件,逐一附录如下:
1. 主脚本:batch_ssh .sh
#!/bin/bash
# batch_ssh.sh
# 作者:亚丹
# 时间:2009-05-27
# seesea2517#gmail*com
# http://seesea.blog.chinaunix.net
# http://blog.csdn.net/nicenight
#
# 功能:批量无交互ssh操作
#
# 配置文件格式:
# IP xxxx ID/Name
# IP xxxx ID/Name
# IP xxxx ID/Name
# IP xxxx ID/Name
# IP xxxx ID/Name
server_list=$1
cmd=$2
if [ -z "$server_list" ] || [ -z "$cmd" ]
then
echo "Usage: $0 server_list command"
exit 1
fi
if [ ! -f "$server_list" ]
then
echo -e "File \"$server_list\" not exist!"
exit 1
fi
# The result directory
res_dir="result"
if ! [ -d $res_dir ]
then
mkdir $res_dir
fi
readonly MAX_THREAD=300 ;# The max number of the sub threads can be forked
readonly OLD_IFS=${IFS} ;# Save the current IFS
IFS=$'\n' ;# Set the IFS to '\n"
usr="ssh_user"
port=22
read -sp "Input the password for $usr: " psw
echo "\nGo!\n"
(( i = 0 ))
for line in `cat $server_list`
do
if [ -z "$line" ]
then
continue
fi
echo "$line" | grep '^#' > /dev/null
if [ $? = 0 ]
then
continue
fi
ip=`echo $line | awk '{print $1}'`
id=`echo $line | awk '{print $3}'`
if [ "$ip" == "" ]
then
continue
fi
./remote_exec.exp $usr $ip $port $psw $cmd >> "$res_dir/${id}.txt" &
# If the sub thread numbers upto the MAX_THREAD, then wait for all done
if (( ++i % $MAX_THREAD == 0 ))
then
echo "wait....."
wait
fi
echo -ne "\rProcessing $i."
done
IFS=${OLD_IFS}
wait
echo "Total $i done."
2. 主 expect 调用脚本 remote_exec.exp
#!/usr/bin/expect
source func.exp
if {$argc < 5 } {
puts "remote_exec needs 5 parameters."
exit 1
}
log_user 0
log_file log.txt
set in_user [lindex $argv 0]
set in_ip [lindex $argv 1]
set in_port [lindex $argv 2]
set in_psw [lindex $argv 3]
set in_cmd [lindex $argv 4]
set out_res ""
set ret [remote_exec $in_user $in_ip $in_port $in_psw $in_cmd out_res]
if {$ret < 0} {
puts "remote_exec failed (return code: $ret, return str: $out_res)."
exit 1
}
puts $out_res
3. expect 的函数库文件:func.exp,现在看来,也没有再新加过函数……不过这个远大蓝图还是曾经存在过的哈
#!/usr/bin/expect
# func.exp
# 作者:亚丹
# 时间:2009-05-27
# seesea2517#gmail*com
# http://seesea.blog.chinaunix.net
# http://blog.csdn.net/nicenight
#
# 功能:基本函数库文件
# -----------------------------------
# Function:
# Execute the command on the host in_ip
#
# Return:
# If successfully executed, return 0
# otherwise return the error code:
# -1: Connect timeout
# -2: Wrong password
# -3: Wrong password
# -4: Checking password timeout
#
# Input args:
# in_user: Specifys the user to login
# in_ip : Specifys the host for user to login
# in_psw : Specifys the password for user to login to the host
# in_cmd : Specifys the command(s) to execute
#
# Output args:
# out_res: Returns the result if executed successfully
# returns the error description if unsuccessful
# -----------------------------------
proc remote_exec {in_user in_ip in_port in_psw in_cmd out_res} {
upvar $out_res response
# The for loop is used to do the login action
set timeout 10
for {set i 1} {$i < 3} {incr i} {
# Last "" is used as an end symbol
# spawn -noecho ssh $in_user@$in_ip ls -ogt --time-style=+'%Y%m%d%H%M%S' | grep ^- && echo
spawn -noecho ssh $in_user@$in_ip -p $in_port $in_cmd && echo
expect {
-nocase "Password:"
{
send "$in_psw\n"
break
}
-nocase "(yes/no)"
{
send "yes\n"
continue
}
timeout
{
set response "Can't connect to host $in_ip\n"
return -1
}
}
}
expect {
-nocase "password:"
{
set response "Wrong password inputed for $in_user@$in_ip.\n"
return -2
}
-nocase "denied"
{
set response "Wrong password inputed for $in_user@$in_ip.\n"
return -3
}
timeout
{
set response "Timeout while checking password for $in_user@$in_ip.\n"
return -4
}
# -re "\[^/]+"
-re ".*"
{
set response $expect_out(0,string)
set response [string trim $response "\r\n /"]
return 0
}
}
}
-------------------------------------------------------------------------------------- 对于常用的 scp 操作,有对上述脚本做了针对性的调整,以更加方便的使用:
1. 主脚本 batch_scp.sh
#!/bin/bash
# batch_scp.sh
# 作者:亚丹
# 时间:2009-05-27
# seesea2517#gmail*com
# http://seesea.blog.chinaunix.net
# http://blog.csdn.net/nicenight
#
# 功能:批量无交互scp操作
#
# 配置文件格式:
# IP xxxx ID/Name
# IP xxxx ID/Name
# IP xxxx ID/Name
# IP xxxx ID/Name
# IP xxxx ID/Name
# para1 is the server list file
server_list=$1
if [ -z "$server_list" ]
then
echo "Usage: $0 server_list remote_dir local_dir"
exit 1
fi
# If server list file not exists, then exit
if [ ! -f "$server_list" ]
then
echo -e "File \"$server_list\" not exist!"
exit 1
fi
# para2 is the remote directory for scp
# if not inputed, then set to "/"
rdir=$2
if [ -z "$rdir" ]
then
rdir="/"
fi
# para3 is the local directory for scp
# if not inputed, then set to "./"
ldir=$3
if [ -z "$ldir" ]
then
ldir="./"
fi
# The result directory
res_dir="result"
if ! [ -d $res_dir ]
then
mkdir $res_dir
fi
readonly MAX_THREAD=2 ;# The max number of the sub threads can be forked
readonly OLD_IFS=${IFS} ;# Save the current IFS
IFS=$'\n' ;# Set the IFS to '\n"
usr="ssh_user"
port=22
read -sp "Input the password for $usr: " psw
(( i = 0 ))
for line in `cat $server_list`
do
if [ -z "$line" ]
then
continue
fi
echo "$line" | grep '^#' > /dev/null
if [ $? = 0 ]
then
continue
fi
ip=`echo $line | awk '{print $1}'`
id=`echo $line | awk '{print $3}'`
if [ "$ip" == "" ]
then
continue
fi
# ./remote_exec.exp $usr $ip $port $psw $rdir $ldir >> "$res_dir/${id}.txt" &
# make a local sub-dir for scp, use the id info
lsub_dir="$ldir/${id}"
if [ ! -d "$lsub_dir" ]
then
mkdir "$lsub_dir"
fi
# download and delete the file for release the space of the disk
# (./remote_exec.exp $usr $ip $port $psw $rdir "$lsub_dir" >> "$res_dir/${id}.txt"; sz "$lsub_dir/*"; rm -R "$lsub_dir";) &
# for test, not delete the local sub dir
(./remote_exec.exp $usr $ip $port $psw $rdir "$lsub_dir" >> "$res_dir/${id}.txt"; sz "$lsub_dir/*";) &
# If the sub thread numbers upto the MAX_THREAD, then wait for all done
if (( ++i % $MAX_THREAD == 0 ))
then
echo "wait....."
wait
fi
done
IFS=${OLD_IFS}
wait
echo "Total $i done."
2. remote_exec.exp
#!/usr/bin/expect
# remote_exec.exp
# 作者:亚丹
# 时间:2009-05-27
# seesea2517#gmail*com
# http://seesea.blog.chinaunix.net
# http://blog.csdn.net/nicenight
#
# 功能:实现无交互远程操作
source func.exp
if {$argc < 6 } {
puts "remote_exec needs 6 parameters."
exit 1
}
log_user 0
log_file log.txt
set in_user [lindex $argv 0]
set in_ip [lindex $argv 1]
set in_port [lindex $argv 2]
set in_psw [lindex $argv 3]
set in_rdir [lindex $argv 4]
set in_ldir [lindex $argv 5]
set out_res ""
set ret [remote_exec $in_user $in_ip $in_port $in_psw $in_rdir $in_ldir out_res]
if {$ret < 0} {
puts "remote_exec failed (return code: $ret, return str: $out_res)."
exit 1
}
puts $out_res
3. func.exp
#!/usr/bin/expect
# func.exp
# 作者:亚丹
# 时间:2009-05-27
# seesea2517#gmail*com
# http://seesea.blog.chinaunix.net
# http://blog.csdn.net/nicenight
#
# 功能:基本函数库文件
# -----------------------------------
# Function:
# Execute the command on the host in_ip
#
# Return:
# If successfully executed, return 0
# otherwise return the error code:
# -1: Connect timeout
# -2: Wrong password
# -3: Wrong password
# -4: Checking password timeout
#
# Input args:
# in_user: Specifys the user to login
# in_ip : Specifys the host for user to login
# in_psw : Specifys the password for user to login to the host
# in_rdir: Specifys the remote directory for scp
# in_ldir: Specifys the local directory for scp
#
# Output args:
# out_res: Returns the result if executed successfully
# returns the error description if unsuccessful
# -----------------------------------
proc remote_exec {in_user in_ip in_port in_psw in_rdir in_ldir out_res} {
upvar $out_res response
# The for loop is used to do the login action
set timeout 10
# Last "" is used as an end symbol
# spawn -noecho ssh $in_user@$in_ip ls -ogt --time-style=+'%Y%m%d%H%M%S' | grep ^- && echo
spawn -noecho scp -p $in_port $in_user@$in_ip:$in_rdir $in_ldir && echo
expect {
-nocase "Password:"
{
send "$in_psw\n"
}
-nocase "(yes/no)"
{
send "yes\n"
exp_continue
}
timeout
{
set response "Can't connect to host $in_ip\n"
return -1
}
}
expect {
-nocase "password:"
{
set response "Wrong password inputed for $in_user@$in_ip.\n"
return -2
}
-nocase "denied"
{
set response "Wrong password inputed for $in_user@$in_ip.\n"
send "\003"
exec kill [exp_pid]
close
wait
return -3
}
timeout
{
set response "Timeout while checking password for $in_user@$in_ip.\n"
return -4
}
-re "\[^/]+"
{
set response $expect_out(0,string)
set response [string trim $response "\r\n /"]
return 0
}
}
}