批量无交互远程SSH操作(2012-07-11)

前言
为了参与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
        }
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值