shell 中 expect 详解

一、概述

     Expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。Expect的作者DonLibes在1990年开始编写Expect时对Expect做有如下定义:Expect是一个用来实现自动交互功能的软件套件。通过expect系统管理员可以创建脚本用来实现对命令或程序提供输入,而这些命令和程序是期望从终端(terminal)得到输入,一般来说这些输入都需要手工输入进行的。Expect则可以根据程序的提示模拟标准输入提供给程序需要的输入来实现交互程序执行。甚至可以实现实现简单的BBS聊天机器人。

我们通过Shell可以实现简单的控制流功能,如:循环、判断等。但是对于需要交互的场合则必须通过人工来干预,有时候我们可能会需要实现与SSH、FTP服务器等进行免交互的自动连接功能,而Expect正是用来实现这种功能的工具。

Expect是不断发展的,随着时间的流逝,其功能越来越强大,已经成为系统管理员的的一个强大助手。Expect需要Tcl编程语言的支持,所以要在系统上运行Expect必须首先安装Tcl。

通过ssh远程操控主机时交互解决方式:

  1. 通过ssh的密钥对

  2. 通过sshpass工具提交密码

  3. 通过expect工具提交密码

二、安装expect

[root@localhost ~]# rpm -q expect
​
[root@localhost ~]# yum -y install expect

三、如何使用expect

在使用expect程序前,我们首先简单说下expect在常规使用中的工作流程:

首先expect的内部命令spawn启动指定进程-->expect获取期待的关键字-->内部命令send向指定进程发送响应内容-->进程执行完成后,退出expect程序。

通过上面expect的工作流程,我们可以认识到,expect的内置命令:spawn、expect、send等非常重要,所以下面我们先来了解下它们的使用。

3-1、spawn命令

spawn作用:启动新的产生交互的进程

spawn命令的语法:

spawn [选项] [需要执行的shell命令或程序等]

下面我们以修改一个已存在账户的密码为例,举例如下:

[root@localhost ~]# useradd tom
​
[root@localhost ~]# passwd tom
​
更改用户 tom 的密码 。
​
新的 密码:
​
无效的密码: 密码未通过字典检查 - 过于简单化/系统化
​
重新输入新的 密码:
​
passwd:所有的身份验证令牌已经成功更新。

基于expect交互界面做法

[root@localhost ~]# expect
​
expect1.1> spawn passwd tom
​
spawn passwd tom
​
1388
​
expect1.2> expect "密码:"
​
更改用户 tom 的密码 。
​
新的 密码:expect1.3> send "123456\r"
​
expect1.4> expect "密码:"
​
无效的密码: 密码少于 8 个字符
​
重新输入新的 密码:expect1.5> send "123456\r"
​
expect1.6> expect eof
​
passwd:所有的身份验证令牌已经成功更新。
​
expect1.7> exit

3-2、expect命令

expect作用:获取从spawn命令执行的命令和程序后产生的交互信息。看看是否匹配,如果匹配,就开始执行expect进程接收字符串。

expect命令的语法:

  expect [选项] 表达式 [动作]

  选项:比如"-re"表示使用正则表达式来进行匹配。

案例:

[root@localhost ~]# expect
​
expect1.1> spawn passwd tom
​
spawn passwd tom
​
1437
​
expect1.2> expect "新的 密码:"
​
更改用户 tom 的密码 。
​
新的 密码:expect1.3> send "123456\r"
​
expect1.4> expect "新的 密码:"
​
无效的密码: 密码少于 8 个字符
​
重新输入新的 密码:expect1.5> send "123456\r"
​
expect1.6> expect eof
​
passwd:所有的身份验证令牌已经成功更新。
​
expect1.7> exit

3-3、send命令

send命令的主要作用是,在expect命令匹配完指定的字符后,发送指定的字符串给系统程序,在字符中可以支持部分特殊转义符,比如:n(回车)r(换行)t(制表符)等。

案例:

[root@localhost ~]# expect
​
expect1.1> spawn passwd tom
​
spawn passwd tom
​
1437
​
expect1.2> expect "新的 密码:"
​
更改用户 tom 的密码 。
​
新的 密码:expect1.3> send "123456\r"
​
expect1.4> expect "新的 密码:"
​
无效的密码: 密码少于 8 个字符
​
重新输入新的 密码:expect1.5> send "123456\r"
​
expect1.6> expect eof
​
passwd:所有的身份验证令牌已经成功更新。
​
expect1.7> exit

下面是上述实验的expect脚本案例:

修改密码案例1:

[root@localhost ~]# vim change_pass1.exp
​
#!/usr/bin/expect
​
# Filename : change_pass1.exp
​
spawn passwd tom
​
expect "密码:"
​
send "123456\r"
​
expect "新的 密码:"
​
send "123456\r"
​
expect eof
​
[root@localhost ~]# chmod +x change_pass1.exp
​
[root@localhost ~]# ./change_pass1.exp
​
spawn passwd tom
​
更改用户 tom 的密码 。
​
新的 密码:
​
无效的密码: 密码少于 8 个字符
​
重新输入新的 密码:
​
passwd:所有的身份验证令牌已经成功更新。

修改密码案例2:

[root@localhost ~]# vim chang_pass2.exp
​
#!/usr/bin/expect
​
# Filename : change_pass2.exp
​
spawn passwd tom
​
expect {
​
"新的 密码:" { send "123456\r"; exp_continue }
​
"新的 密码:" { send "123456\r" }
​
eof
​
}
​
[root@localhost ~]# chmod +x chang_pass2.exp
​
[root@localhost ~]# ./chang_pass2.exp
​
spawn passwd tom
​
更改用户 tom 的密码 。
​
新的 密码:
​
无效的密码: 密码少于 8 个字符
​
重新输入新的 密码:
​
passwd:所有的身份验证令牌已经成功更新。

3-4、exp_continue命令

exp_continue命令的主要作用是,如果需要一次匹配多个字符串,那么多次匹配字符串并执行不同的动作中,可以让expect程序实现继续匹配的效果。

案例:

[root@localhost ~]# vim chang_pass2.exp
​
#!/usr/bin/expect
​
# Filename : change_pass2.exp
​
spawn passwd tom
​
expect {
​
"新的 密码:" { send "123456\r"; exp_continue }
​
"新的 密码:" { send "123456\r" }
​
eof
​
}
​
[root@localhost ~]# chmod +x chang_pass2.exp
​
[root@localhost ~]# ./chang_pass2.exp
​
spawn passwd tom
​
更改用户 tom 的密码 。
​
新的 密码:
​
无效的密码: 密码少于 8 个字符
​
重新输入新的 密码:
​
passwd:所有的身份验证令牌已经成功更新。

3-5、send_user命令

send_user命令的作用是:用来打印expect脚本信息,类似shell里的echo命令。

[root@localhost ~]# vim send_user.exp
​
#!/usr/bin/expect
​
#Filename: send_user.exp
​
send_user "beijingn"
​
send_user "shanghait"
​
send_user "guangzhoun"
​
[root@localhost ~]# chmod +x send_user.exp
​
[root@localhost ~]# ./send_user.exp
​
beijing
​
shanghai guangzhou

3-6、expect变量

3-6-1、普通变量

expect中定义普通变量的语法如下:

set 变量名 变量值

例如:

set chengshi "beijing"

调取变量的方法是:

puts $变量名
​
#或者
​
send_user "$变量名"

案例:

[root@localhost ~]# vim expect_var.exp
​
#!/usr/bin/expect
​
# Filename: expect_var.exp
​
set name "crushlinux"
​
set address "beijing"
​
puts $name
​
send_user "$name t $addressn"
​
[root@localhost ~]# chmod +x expect_var.exp
​
[root@localhost ~]# ./expect_var.exp
​
crushlinux
​
crushlinux beijing

3-6-2、expect中位置参数变量

如何向expect脚本中像shell一样传递类似于$0、$1等位置参数,用于接收及控制expect脚本传递位置参数变量呢?

expect是通过如下语法来进行的:

set <变量名称> [lindex $argv <param index> ]

当然,除了基本的位置参数外,expect还支持其它的特殊参数,比如:

$argc 表示传入参数的个数

$argv0 表示当前执行脚本的名称。

案例:

[root@localhost ~]# vim expect_var2.exp
​
#!/usr/bin/expect
​
# Filename: expect_var2.exp
​
set file1 [lindex $argv 0]
​
set file2 [lindex $argv 1]
​
set file3 [lindex $argv 2]
​
puts "The files are : $file1 $file2 $file3"
​
puts "The number of files are: $argc"
​
puts "The expect script name is: $argv0"
​
[root@localhost ~]# chmod +x expect_var2.exp
​
[root@localhost ~]# ./expect_var2.exp 1.txt 2.txt 3.txt
​
The files are : 1.txt 2.txt 3.txt
​
The number of files are: 3
​
The expect script name is: ./expect_var2.exp

3-7、expect中if条件语句

expect中if 条件语句的语法结构如下:

if {条件表达式} {
​
commands;
​
}
​
if {条件表达式} {
​
commands;
​
} else {
​
commands;
​
}

注意事项:上面条件语句中每个"{"前要至少保证有一个空格。

案例:

[root@localhost ~]# vim expect-if.exp
​
#!/usr/bin/expect
​
# Filename: expect-if.exp
​
if {$argc <= 3} {
​
puts "The IP numbers <= 3"
​
} else {
​
puts "The IP numbers > 3"
​
}
​
[root@localhost ~]# chmod +x expect-if.sh
​
[root@localhost ~]# ./expect-if.sh 192.168.200.{1..3}
​
The IP numbers <= 3
​
[root@localhost ~]# ./expect-if.sh 192.168.200.{1..5}
​
The IP numbers > 3

3-8、expect中常用关键字

3-8-1 eof关键字

eof是和spawn对应的,当spawn发送指令到终端执行起始会有一个eof,等指令在终端完毕后,在返回时eof被expect捕捉,就好比在shell中cat >>file <<OEFrr content rrEOF一样,在结束时也要有EOF,这样是对应的。因前面案例中已有举例,这里就不再举例说明。

Interact允许用户交互,由管理员结束进程。

3-8-2 timeout关键字

expect脚本我们都知道,首先spawn我们要执行的命令,然后就给出一堆expect的屏幕输出,如果输出匹配了我们的expect的正则匹配内容,我们就会send一个命令上去,模拟用户输入。

但是expect中等待命令的输出信息是有一个timeout的设定的,默认是10秒。这个特性是防止那些执行死机的命令的。一旦到了这个timeout,还是没有屏幕输出的话,expect脚本中下面的代码就会执行。或者我们在expect脚本中如果定义了timeout的响应代码的话,这些代码就会被执行。

解决这样的问题非常简单,最简单的办法就是在expect脚本的开头定义:

set timeout -1 -- 永久不超时
​
set timeout 0 -- 立即执行
​
set timeout XX -- 设定具体的timeout时间(秒),默认是10秒。

案例1:

[root@localhost ~]# vim expect-timeout.exp
​
#!/usr/bin/expect
​
# Filename: expect-timeout.exp
​
spawn ssh root@192.168.200.112 ifconfig ens32
​
set timeout 15
​
expect {
​
"yes/no" { send "yesn"; exp_continue }
​
timeout { puts "request timeout 15s r" }
​
}
​
[root@localhost ~]# chmod +x expect-timeout.exp
​
[root@localhost ~]# ./expect-timeout.exp
​
spawn ssh root@192.168.200.112 ifconfig ens32
​
root@192.168.200.112's password: request timeout 15s
<--此处等待15秒后显示。

案例2:

[root@localhost ~]# vim expect-timeout2.exp
​
#!/usr/bin/expect
​
# Filename: expect-timeout2.exp
​
spawn ssh root@192.168.200.112 ifconfig ens32
​
set timeout 15
​
expect {
​
"yes/no" { send "yesn"; exp_continue }
​
"*password*" { send "123456\r" }
​
}
​
expect eof
​
#interact
​
[root@localhost ~]# chmod +x expect-timeout.exp
​
[root@localhost ~]# ./expect-timeout.exp
​
spawn ssh root@192.168.200.112 ifconfig ens32
​
root@192.168.200.112's password:
​
ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
​
inet 192.168.200.112 netmask 255.255.255.0 broadcast 192.168.200.255
​
inet6 fe80::20c:29ff:fe8c:f2d9 prefixlen 64 scopeid 0x20<link>
​
ether 00:0c:29:8c:f2:d9 txqueuelen 1000 (Ethernet)
​
RX packets 2472 bytes 1511193 (1.4 MiB)
​
RX errors 0 dropped 0 overruns 0 frame 0
​
TX packets 1714 bytes 211891 (206.9 KiB)
​
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

在上面的这个案例中没有等待15秒才输出结果,是因为中间没有屏幕输出需要交互等待15秒钟的动作。

3-9、expect中for循环语句

{ set i 1 } 定义i的值为1
​
{ $i <= 10 }循环的条件
​
{ incr i 1} 制定$i的增量值,必须写在这行的末尾处,默认增量值为1
[root@localhost ~]# cat expect-for.exp
​
#!/usr/bin/expect
​
for { set i 1 } { $i <= 10 } { incr i 1 } {
​
puts "$i"
​
}
​
[root@localhost ~]# expect expect-for.exp
​
1
​
2
​
3
​
4
​
5
​
6
​
7
​
8
​
9
​
10

3-10、expect中while循环语句

[root@localhost ~]# vim expect-while.exp
​
#!/usr/bin/expect
​
set i 1
​
while {$i <= 10} {
​
puts "$i"
​
sleep 1
​
incr i 1
​
}[root@localhost ~]# expect expect-while.exp
​
1
​
2
​
3
​
4
​
5
​
6
​
7
​
8
​
9
​
10

四、Shell脚本调用expect 的方法

  1. 在shell脚本中使用expect --c "..."可以在shell中调用expect编程语言;

[root@localhost ~]# cat shell-expect1.sh
​
#!/bin/bash
​
for i in 192.168.200.{112..113}
​
do
​
expect -c "
​
spawn ssh root@$i ifconfig ens32
​
expect {
​
"yes/no" { send "yes\r";exp_continue }
​
"password" { send "123456\n" }
​
}
​
expect eof
​
"
​
done
​
[root@localhost ~]# bash shell-expect1.sh
​
spawn ssh root@192.168.200.112 ifconfig ens32
​
root@192.168.200.112's password:
​
ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
​
inet 192.168.200.112 netmask 255.255.255.0 broadcast 192.168.200.255
​
inet6 fe80::20c:29ff:fe8c:f2d9 prefixlen 64 scopeid 0x20<link>
​
ether 00:0c:29:8c:f2:d9 txqueuelen 1000 (Ethernet)
​
RX packets 3512 bytes 1656192 (1.5 MiB)
​
RX errors 0 dropped 0 overruns 0 frame 0
​
TX packets 2808 bytes 365154 (356.5 KiB)
​
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
​
spawn ssh root@192.168.200.113 ifconfig ens32
​
root@192.168.200.113's password:
​
ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
​
inet 192.168.200.113 netmask 255.255.255.0 broadcast 192.168.200.255
​
inet6 fe80::20c:29ff:fe0b:7eab prefixlen 64 scopeid 0x20<link>
​
ether 00:0c:29:0b:7e:ab txqueuelen 1000 (Ethernet)
​
RX packets 2722 bytes 1511052 (1.4 MiB)
​
RX errors 0 dropped 0 overruns 0 frame 0
​
TX packets 2196 bytes 258197 (252.1 KiB)
​
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

2.在shell脚本中使用/usr/bin/expect <<-EOF ...EOF的方式可以调用绝大多数的其它脚本语言,这种方式执行命令建议使用绝对路径,而且要严格遵守expect的脚本格式;

[root@localhost ~]# cat shell-expect2.sh
​
#!/bin/bash
​
for i in 192.168.200.{112..113}
​
do
​
/usr/bin/expect << EOF
​
spawn ssh root@$i ifconfig ens32
​
expect {
​
"yes/no" { send "yes\r";exp_continue }
​
"password" { send "123456\n" }
​
}
​
expect eof
​
EOF
​
done
​
[root@localhost ~]# bash shell-expect2.sh
​
spawn ssh root@192.168.200.112 ifconfig ens32
​
root@192.168.200.112's password:
​
ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
​
inet 192.168.200.112 netmask 255.255.255.0 broadcast 192.168.200.255
​
inet6 fe80::20c:29ff:fe8c:f2d9 prefixlen 64 scopeid 0x20<link>
​
ether 00:0c:29:8c:f2:d9 txqueuelen 1000 (Ethernet)
​
RX packets 3329 bytes 1632688 (1.5 MiB)
​
RX errors 0 dropped 0 overruns 0 frame 0
​
TX packets 2609 bytes 340022 (332.0 KiB)
​
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
​
spawn ssh root@192.168.200.113 ifconfig ens32
​
root@192.168.200.113's password:
​
ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
​
inet 192.168.200.113 netmask 255.255.255.0 broadcast 192.168.200.255
​
inet6 fe80::20c:29ff:fe0b:7eab prefixlen 64 scopeid 0x20<link>
​
ether 00:0c:29:0b:7e:ab txqueuelen 1000 (Ethernet)
​
RX packets 2561 bytes 1488208 (1.4 MiB)
​
RX errors 0 dropped 0 overruns 0 frame 0
​
TX packets 1997 bytes 233005 (227.5 KiB)
​
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
​
[root@localhost ~]# cat zhixing.sh
​
#!/bin/bash
​
for i in 192.168.200.{112..113}
​
do
​
/usr/bin/expect << EOF
​
spawn ssh root@$i wget http://192.168.200.111/user20.sh -O
/tmp/user20.sh && bash /tmp/user20.sh
​
expect {
​
"yes/no" { send "yesr";exp_continue }
​
"password" { send "123456n" }
​
}
​
expect eof
​
EOF
​
done

五、expect在生产环境中的案例

案例要求:

手边现在有一个统计CPU信息的脚本(cpuinfo.sh),该脚本在单独的物理服务器上或虚拟主机上执行后,能统计该机器CPU的五个相关信息,比如:

[root@localhost ~]# sh cpuinfo.sh
​
logical CPU number in total: 32 //当前机器中逻辑CPU个数为:32
​
phycical CPU number in total: 2 //当前机器中物理CPU颗数为: 2
​
core number in a physical CPU: 8 //机器中每颗物理CPU有8核心
​
logical CPU number in a phycical CPU:16 //机器中每颗物理CPU 有16
个逻辑CPU
​
Hyper threading is enabled. //每颗CPU的超线程已经打开

具体代码如下:

[root@localhost ~]# cat cpuinfo.sh
​
#!/bin/bash
​
# filename: cpu-info.sh
​
#this script only works in a Linux system which has one or more
indentical physical CPU(S)
​
echo -n "logical CPU number in total: "
​
#逻辑CPU个数
​
cat /proc/cpuinfo |grep "^processor" |wc -l
​
#有些系统没有多核也没有打开超线程,就直接退出脚本
​
cat /proc/cpuinfo |grep -qi "core id"
​
if [ $? -ne 0 ]; then
​
echo "Warning. No multi-core or hyper-threading is enabled."
​
exit 0;
​
fi
​
echo -n "phycical CPU number in total: "
​
#物理CPU个数
​
cat /proc/cpuinfo |grep "physical id" |sort |uniq |wc -l
​
echo -n "core number in a physical CPU: "
​
#每个物理CPU上的core的个数(未计入超线程)
​
core_per_phy_cpu=$(cat /proc/cpuinfo |grep "core id"|sort |uniq
|wc -l)
​
echo $core_per_phy_cpu
​
echo -n "logical CPU number in a phycical CPU:"
​
#每个物理CPU中逻辑CPU(可能是core,threads或both)的个数
​
logical_cpu_per_phy_cpu=$(cat /proc/cpuinfo |grep "siblings"|sort
|uniq |awk -F: '{print $2}')
​
echo $logical_cpu_per_phy_cpu
​
#是否打开有超线程
​
#如果在同一个物理CPU上有两个逻辑CPU具有相同的"core
id",那么超线程是打开的
​
#此处根据前面计算的 core_per_phy_cpu和
logical_cpu_per_phy_cpu的比较来查看超线程
​
if [ $logical_cpu_per_phy_cpu -gt $core_per_phy_cpu ]; then
​
echo "Hyper threading is enabled."
​
elif [ $logical_cpu_per_phy_cpu -eq $core_per_phy_cpu ]; then
​
echo "Hyper threading is NOT enabled."
​
else
​
echo "Error. There's something wrong."
​
fi

下面我们要做的是将这个脚本通过expect批量分发到指定范围的机器上进行自动化跑批量,以便能通过shell脚本自动统计批量采集所有被指定机器上的CPU相关信息。具体脚本实现思路如下:

1)通过expect实现将单一脚本自动免交互上传至指定客户机。

2)通过shell脚本中的循环语句,调取expect单机自动免交互上传文件脚本。从而实现将cpuinfo.sh脚本批量上传到客户机指定目录下。

3)通过expect实现自动免交互方式在指定单台客户机上执行指定命令或脚本程序。

4)通过shell脚本中的循环语句,调取单机免交互自动执行脚本程序的expect脚本。从而实现在多台机器上免交互自动执行shell脚本。

脚本案例如下:

1)通过expect实现将单一脚本自动免交互上传至指定客户机。

[root@localhost ~]# vim auto_upload_file.exp
​
#!/usr/bin/expect
​
# Filename: auto_upload_file.exp
​
if { $argc != 3 } {
​
puts "usage: expect $argv0 file host dir"
​
exit
​
}
​
set file [lindex $argv 0]
​
set host [lindex $argv 1]
​
set dir [lindex $argv 2]
​
set password "123456"
​
spawn scp -P22 -rp $file root@$host:$dir
​
expect {
​
"yes/no" {send "yes\r"; exp_continue}
​
"*password*" {send "$password\r"}
​
}
​
expect eof
​
[root@localhost ~]# chmod +x auto_upload_file.exp
​
[root@localhost ~]# ./auto_upload_file.exp
​
usage: expect ./auto_upload_file.exp file host dir
​
[root@localhost ~]# ./auto_upload_file.exp cpuinfo.sh
192.168.200.112 /opt
​
spawn scp -P22 -rp cpuinfo.sh root@192.168.200.112:/opt
​
root@192.168.200.112's password:
​
cpuinfo.sh 100% 1470 393.6KB/s 00:00

2)通过shell脚本中的循环语句,调取expect单机自动免交互上传文件脚本。从而实现将cpuinfo.sh脚本批量上传到客户机指定目录下。

[root@localhost ~]# vim auto_upload_files.sh
​
#!/bin/bash
​
# Filename : auto_upload_files.sh
​
if [ $# -ne 2 ]; then
​
echo "useage : sh $0 file dir"
​
exit 1
​
fi
​
file=$1
​
dir=$2
​
for IP in 192.168.200.{112..113} ; do
​
expect auto_upload_file.exp $file $IP $dir
​
done
​
[root@localhost ~]# chmod +x auto_upload_files.sh
​
[root@localhost ~]# ./auto_upload_files.sh
​
useage : sh ./auto_upload_files.sh file dir
​
[root@localhost ~]# ./auto_upload_files.sh cpuinfo.sh /opt/
​
spawn scp -P22 -rp cpuinfo.sh root@192.168.200.112:/opt/
​
root@192.168.200.112's password:
​
cpuinfo.sh 100% 1470 783.5KB/s 00:00
​
spawn scp -P22 -rp cpuinfo.sh root@192.168.200.113:/opt/
​
root@192.168.200.113's password:
​
cpuinfo.sh 100% 1470 662.7KB/s 00:00

3)通过expect实现自动免交互方式在指定单台客户机上执行指定命令或脚本程序。

[root@localhost ~]# vim auto_run_host.exp
​
#!/usr/bin/expect
​
# Filename: auto_run_1host.sh
​
if { $argc !=2 } {
​
puts "usage: expect $argv0 ip command"
​
exit
​
}
​
set ip [lindex $argv 0]
​
set cmd [lindex $argv 1]
​
set password "123456"
​
spawn ssh root@$ip $cmd
​
expect {
​
"yes/no" {send "yes\r"; exp_continue}
​
"*password*" {send "$password\r"}
​
}
​
expect eof
​
[root@localhost ~]# chmod +x auto_run_host.exp
​
[root@localhost ~]# ./auto_run_host.exp 192.168.200.112 "ifconfig
ens32"
​
spawn ssh root@192.168.200.112 ifconfig ens32
​
root@192.168.200.112's password:
​
ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
​
inet 192.168.200.112 netmask 255.255.255.0 broadcast 192.168.200.255
​
inet6 fe80::20c:29ff:fe8c:f2d9 prefixlen 64 scopeid 0x20<link>
​
ether 00:0c:29:8c:f2:d9 txqueuelen 1000 (Ethernet)
​
RX packets 2650 bytes 1541675 (1.4 MiB)
​
RX errors 0 dropped 0 overruns 0 frame 0
​
TX packets 1874 bytes 236034 (230.5 KiB)
​
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
​
[root@localhost ~]# ./auto_run_host.exp 192.168.200.112 "source
/opt/cpuinfo.sh"
​
spawn ssh root@192.168.200.112 source /opt/cpuinfo.sh
​
root@192.168.200.112's password:
​
logical CPU number in total: 1
​
phycical CPU number in total: 1
​
core number in a physical CPU: 1
​
logical CPU number in a phycical CPU:1
​
Hyper threading is NOT enabled.

4)通过shell脚本中的循环语句,调取单机免交互自动执行脚本程序的expect脚本。从而实现在多台机器上免交互自动执行shell脚本。

[root@localhost ~]# vim auto_run_hosts.sh
​
#!/bin/bash
​
# Filename: auto_run_hosts.sh
​
if [ $# -ne 1 ]; then
​
echo "useage: $0 cmd"
​
exit 3
​
fi
​
cmd=$1
​
for IP in 192.168.200.{112..113}; do
​
expect auto_run_host.exp $IP "$cmd"
​
done
​
[root@localhost ~]# chmod +x auto_run_hosts.sh
​
[root@localhost ~]# ./auto_run_hosts.sh
​
useage: ./auto_run_hosts.sh cmd
​
[root@localhost ~]# ./auto_run_hosts.sh "source
/opt/cpuinfo.sh"
​
spawn ssh root@192.168.200.112 source /opt/cpuinfo.sh
​
root@192.168.200.112's password:
​
logical CPU number in total: 1
​
phycical CPU number in total: 1
​
core number in a physical CPU: 1
​
logical CPU number in a phycical CPU:1
​
Hyper threading is NOT enabled.
​
spawn ssh root@192.168.200.113 source /opt/cpuinfo.sh
​
root@192.168.200.113's password:
​
logical CPU number in total: 1
​
phycical CPU number in total: 1
​
core number in a physical CPU: 1
​
logical CPU number in a phycical CPU:1
​
Hyper threading is NOT enabled.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/98697.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98697.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第4讲 机器学习基础概念

机器学习作为人工智能的子领域&#xff0c;专注于训练计算机算法自动发现数据中的模式与关联关系。以下是其核心基础概念&#xff1a;4.1 数据数据是机器学习的基石。缺乏数据&#xff0c;算法将无从学习。数据可呈现为结构化数据&#xff08;如电子表格、数据库&#xff09;和…

Go组合式继承:灵活替代方案

Go 语言没有传统面向对象编程中的继承机制&#xff0c;但通过组合和接口实现类似功能。Go 更提倡组合优于继承的设计原则&#xff0c;这种设计方式更灵活且易于维护。结构体组合&#xff08;伪继承&#xff09;通过嵌套结构体实现类似继承的效果。子结构体可以直接访问父结构体…

Verilog三段式FSM,实现十字路口红绿灯

运行环境&#xff1a;VCS verdi状态说明&#xff1a;S0 &#xff1a; 初始状态 S1 &#xff1a; 东西方向绿灯亮&#xff0c;南北方向红灯亮&#xff1b;点亮30周期 S2 &#xff1a; 东西方向黄灯亮&#xff0c;南北方向红灯亮&#xff1b;点亮2 周期 S3 &#xff1a; 东西方向…

java 将pdf转图片

如何将pdf文件转为图片 import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.PDFRenderer; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; public class Pdf2Png {/**…

手搓Spring

目录 两种方法创建Spring容器 自定义Spring容器及前置操作 Spring扫描逻辑实现 createBean()方法 getBean()方法 依赖注入&#xff08;DI&#xff09; BeanNameAware接口 InitializingBean接口 BeanPostProcessor接口 AOP的实现 Spring 是一个轻量级的 Java 开发框架…

.NET 单文件程序详解:从原理到实践

C# 混淆加密大师在最新版本中, 提供了.NET单文件解包打包功能, 它可以快速解包官方打包的单文件程序&#xff0c;恢复为原始的多文件结构。也可以对解包后的程序集进行混淆与加密&#xff0c;有效提升逆向门槛。最后还能重新打包成单文件程序&#xff0c;保持对用户友好的分发形…

Spring面试题记录?

请简述 Spring 框架的核心是什么&#xff1f;它主要包含了哪些核心模块&#xff1f; spring的核心模块主要有spring-core&#xff08;工具类&#xff0c;资源加载&#xff09;&#xff0c;spring-bean&#xff08;bean的定义&#xff0c;创建&#xff0c;封装&#xff09;&…

一次缓存引发的文件系统数据不一致问题排查与深度解析

01 起因EFC&#xff08;Elastic File Client&#xff09;是 NAS 自研的分布式文件系统客户端&#xff0c;最近完成了对缓存架构的更新&#xff0c;现在支持多个客户端之间构成分布式缓存&#xff0c;底层支持 NAS、CPFS 和 OSS。由于开发时间较短&#xff0c;一直没有做 NAS 场…

Spring Boot Gateway 教程:从入门到精通

一、Spring Cloud Gateway 简介Spring Cloud Gateway 是基于 Spring 5、Project Reactor 和 Spring Boot 2 构建的 API 网关&#xff0c;旨在为微服务架构提供一种简单而有效的路由管理方式。它取代了 Netflix Zuul&#xff0c;提供了更高效和更强大的网关解决方案。核心特点&a…

防火墙 只允许信任的几台服务器访问

1. 首先&#xff0c;确保 firewalld 服务正在运行&#xff1a;systemctl start firewalld systemctl enable firewall2. 设置默认拒绝规则&#xff1a;设置默认拒绝所有流量&#xff08;拒绝所有的入站流量&#xff09;&#xff1a;firewall-cmd --zonepublic --add-rejectal…

十三,数据结构-树

定义树也是基于节点的数据结构&#xff0c;和链表不同的是&#xff0c;树的节点可以指向多个节点。首先对树的一些常用术语进行说明&#xff1a;最上面的节点叫做根节点&#xff0c;根位于树顶&#xff0c;如图中的节点A&#xff1b;和族谱一样&#xff0c;节点有后代和祖先&am…

JVM-默背版

1.JVM对sychronized的优化&#xff1a;锁膨胀、锁消除、锁粗化、自适应自旋锁 &#xff08;1&#xff09;锁膨胀&#xff1a;从无锁、偏向锁、轻量级锁、重量级锁的过程叫做锁膨胀。在JDK1.6以前&#xff0c;sychronized是由重量级锁实现的&#xff0c;加锁和解锁的过程需要从用…

Mac M 系列芯片 YOLOv8 部署教程(CPU/Metal 后端一键安装)

在 Mac M 系列芯片&#xff08;Apple Silicon/ARM 架构&#xff09;上部署 YOLOv8&#xff0c;有一些注意事项&#xff1a;PyTorch 需要安装 ARM 原生版本&#xff0c;推理可利用 Metal 后端加速 CPU。本文教你一步步完成环境配置、模型下载、依赖安装和验证推理。1️⃣ 环境准…

Python爬虫实战:研究Units模块,构建气象数据采集和分析系统

1. 引言 1.1 研究背景 随着信息技术的飞速发展,互联网已成为全球最大的信息库,涵盖气象、金融、医疗、农业等多个领域的海量数据。这些数据蕴含着巨大的潜在价值,如何有效获取并深入分析这些数据成为当下研究的热点。Python 作为一种功能强大的编程语言,凭借其丰富的库资…

网页设计模板 HTML源码网站模板下载

互联网已成为现代社会不可或缺的一部分&#xff0c;网站则是连接线上与线下世界的桥梁。无论是用于展示个人作品集、推广商业产品还是提供公共服务信息&#xff0c;一个设计精良且功能完善的网站都能发挥巨大作用。然而&#xff0c;传统的手工编码方式不仅耗时费力&#xff0c;…

Flink KeyedProcessFunction为什么能为每个key定义State和Timer?

问题描述 一个常见的开窗逻辑&#xff08;12H 或者 500条&#xff09;&#xff1a; import org.apache.flink.api.common.state.ValueState; import org.apache.flink.api.common.state.ValueStateDescriptor; import org.apache.flink.api.common.typeinfo.Types; import or…

【C++】模版初阶---函数模版、类模版

&#x1f31f;个人主页&#xff1a;第七序章 &#x1f308;专栏系列&#xff1a;C&#xff0b;&#xff0b; 目录 ❄️前言&#xff1a; &#x1f308;1.泛型编程&#xff1a; &#x1f308;2.函数模板 &#x1f36d;2.1函数模板概念 &#x1f36d;2.2函数模板格式 &am…

查找算法(Java)

目录 一.定义 二.分类 三.线性查找 原理&#xff1a; 思路分析 代码实现 例题实践 1.两数之和 方法一&#xff1a;暴力穷举法 思路分析 代码实现 方法二&#xff1a;创建哈希表 思路分析 代码实现 2.移动零 思路分析 代码实现 四.二分查找 原理&#xff1a; …

计算机网络--四层模型,IP地址和MAC地址

四层模型&#xff1a;分别是应用层&#xff0c;传输层&#xff0c;网络层和链路层。应用层&#xff1a;提供了应用程序之间相互通信的接口&#xff0c;允许用户访问网络服务。这一层定义了应用程序如何与底层网络进行交互。例如HTTP协议。传输层&#xff1a;它处理数据的分段、…

解析、创建Excel文件的开源库OpenXLSX介绍

OpenXLSX是一个C库&#xff0c;用于读取、写入、创建和修改.xlsx格式的Microsoft Excel文件&#xff0c;源码地址&#xff1a;https://github.com/troldal/OpenXLSX &#xff0c;License为BSD-3-Clause&#xff0c;可在Windows、Linux、MaCOS平台上使用。最新发布版本为v0.3.2&…