Here Document免交互以及Expect工具

Here Document免交互

Here Document免交互概述

  • Here Document免交互使用I/O重定向的方式将命令列表提供给交互式程序或命令(ftp、cat、read等)
  • Here Document免交互是标准输入的一种替代品,可以帮助脚本开发人员不必使用临时文件来构建输入信息,直接就地产出一个“文件”并用作“命令”的标准输入。Here Document也可以与非交互式程序和命令一起使用

Here Document语法格式与注意事项

注意事项

  • 标记可以使用任意合法字符(通常用EOF)
  • 结尾的标记一定要顶格写,前面不能有任何字符
  • 结尾的标记后面也不能有任何字符(包括空格)
  • 开头标记前后的空格会被省略掉

语法格式及详细注释

  • 命令 <<标记

    内容

    标记
  • 第一个标记为传入内容的开始,第二个标记为传入内容的结束

基本格式

[root@localhost mail]# cat > 10.txt <<EOF
> QWE123
> qwe321
> qweqwe
> EOF
[root@localhost mail]# cat 10.txt 
QWE123
qwe321
qweqwe

免交互方式实现对行数的统计

[root@localhost mail]# wc -l <<EOF
> 456
> 789
> 123
> 444
> EOF
4
【将要统计的内容置于标记"EOF"之间,直接将内容传给wc -l进行统计】

将免交互输入的内容通过read命令打印出来

[root@localhost mail]# read i <<          EOF
【开头的EOF前后可以有空行,会被自动省略,但结尾的EOF不能有空行】
> take
> easy
> EOF
[root@localhost mail]# echo $i
take
【read只能接受一行变量】

使用免交互方式通过passwd给用户设置密码

[root@localhost mail]# passwd qz <<QQQ
【标记可以使用任意合法字符(通常为EOF),但前后标记使用的字符需一致】
> 111222
> 111222         【两行一样的代表输入的新密码和确认密码】
> QQQ
更改用户 qz 的密码 。
新的 密码:无效的密码: 密码少于 8 个字符
重新输入新的 密码:passwd:所有的身份验证令牌已经成功更新。

通过免交互方式实现变量的替换

[root@localhost /]# cat EOF1.txt 
I am going go
[root@localhost /]# vim 1.sh
#!/bin/bash
file="EOF1.txt"                          【中间有符号时最好用引号引起来】
i=go
cat > $file <<EOF
I am going to $i
EOF
cat EOF1.txt

通过免交互实现整体赋值给变量

[root@localhost /]# ./1.sh 
This is Line 1
You are very good!
yes! I am going to home!
[root@localhost /]# vim 1.sh
#!/bin/bash
qz="yes! I am going to home!"
qz1=$(cat <<EOF            【qz1=$(cat <<'EOF'   对标记加单引号,即可关闭变量替换】
This is Line 1
You are very good!
$qz
EOF
)
echo "$qz1"                【这里给变量加上引号即可换行输出】

通过免交互去除每行之前的TAB字符

[root@localhost /]# ./1.sh 
   This is Line 1
      You are very good!
yes! I am going to home!
[root@localhost /]# vim 1.sh 

#!/bin/bash


qz="yes! I am going to home!"
qz1=$(cat <<-EOF                  【在标记前加"-"即可抑制各行首的TAB键的输出但不能抑制空格】
   This is Line 1
      You are very good!
$qz
EOF
)
echo "$qz1"

通过免交互实现多行注释

  • Bash的默认注释是"#",该注释方法只支持单行注释;Hero Document 的引入解决了多行注释的问题
  • ":"代表一个不执行任何操作的空命令,所以中间标记区域的内容不会被执行,会被bash忽略掉,因此即可达到批量注释的效果
[root@localhost /]# ./1.sh 
yes! I am going to home!
[root@localhost /]# vim 1.sh 

#!/bin/bash


qz="yes! I am going to home!"
:<<EOF
   This is Line 1
      You are very good!
$qz
EOF

echo "$qz"

Expect概述

  • expect工具是建立在tcl语言基础之上的
  • expect是一个免费的编程工具,用来实现自动的交互式任务,而无需人为干预。说白了,expect就是一套用来实现自动交互功能的软件工具
  • 在实际工作中,我们运行命令、脚本或程序时,这些命令、脚本或程序都需要从终端输入某些继续运行的指令,而这些输入都需要人为的手工进行。而利用expect,则可以根据程序的提示,模拟标准输入提供给程序,从而实现自动化交互执行。这就是expect

Expect基本命令

在这里插入图片描述

1.脚本解释器

  • expect脚本中首先引入文件,表明具体使用的是某一个shell
  • #!/usr/bin/expect
    在这里插入图片描述

2.spawn

  • spawn后面通常跟一个linux执行命令,表示开启一个会话、启动进程,并跟踪后续交互信息
  • 例如:spawn passwd qz

3.expect

  • 用于接收命令执行后的输出,然后与期望的字符串匹配
  • 判断上次输出结果中是否包含指定的字符串,如果有则立即返回,否则就等待超时时间后返回
  • 只能捕捉由spawn启动的进程的输出

4.send

  • 向进程发送字符串,用于模拟用户的输入;该命令不能自动回车执行,需要加\r(回车)或者\n
  • 方式一
    • expect “密码” {send “abc123\r”} 【同一行时,send部分要有{ }】
  • 方式二
    • expect “密码”
    • send “$abc123\r” 【不在同一行,换行时,send部分不需要有{ }】
  • 方式三
    • expect支持多个分支
    • 【只要满足其中一个条件,执行相应的send语句后则退出该expect语句】
      expect
      {
      “密码1” {send “111222\r”}
      “密码2” {send “222333\r”}
      “密码3” {send “333444\r”}
      }

5.结束符

  • expect EOF
    表示交互结束,等待执行结束,退回到原用户,与spawn对应
    例如切换到root用户,expect脚本默认的是等待10s。当执行完命令后,默认停留10s后,自动切回了原用户
  • interact
    执行完成后保持交互状态,把控制权交给控制台,会停留在目标终端而不会退回到原终端,这个时候即可手工操作了,interact后的命令不起作用,比如interact后添加exit,并不会退出root用户。但是如果没有intetact则登录完成后会退出,而不是留在远程终端上
    使用interact会保持在终端而不会退回到原终端。例如切换到root用户时,则会一直在root用户状态下;通过ssh链接到另一台服务器时,会一直在目标服务器终端,而不会切回到原服务器
    *结束符expect EOF和interact只能二选一

6.set

  • expect默认的超时时间为10秒,通过set命令可以设置会话超时时间。当设为-1时,则代表不限制超时时间
    set timeout 60

7.exp_continue

  • exp_continue附加于某个expect判断之后,可以使该项被匹配后,还能匹配该expect判断语句内的其他项
    exp_continue类似于控制语句中的continue语句。表示允许expect继续向下执行指令
  • 例如:判断交互输出中是否存在yes/no或*assword。如果匹配yes/no则输出yes并再次执行判断
    如果匹配*password则输出abc123并结束该段expect语句
    在这里插入图片描述
  • 注意事项:当使用exp_continue时,如果跟踪的是passwd这样的输入密码后就结束进程的命令,expect{ }外不用再加上expect EOF。因为spawn进程结束后会默认向expect发送EOF,所以再手动加上则会导致后面的expect EOF执行报错

8.send_user

  • send_user表示回显命令,类似于echo

9.接收参数

  • expect脚本可以接受从bash命令传递的参数,使用[lindex $argv n]获得。其中n从0开始,分别表示第一个,第二个…参数
  • 例如
    set hostname [lindex $argv 0] 【相当于hosename=$1的意思】
    set password [lindex $argv 1] 【相当于password=$2的意思】

使用expect实现su切换用户

  • expect直接执行,需要使用expect命令去执行脚本
[root@localhost /]# exit
exit
[qz@localhost /]$ ./expect.sh root 5514
spawn su root
密码:
[root@localhost /]# su ok
[root@localhost /]# vim expect.sh 

#!/usr/bin/expect

set timeout 60                         【设置超时时间】

set username [lindex $argv 0]          【传入参数,argv代表参数列表】
set password [lindex $argv 1]

spawn su $username                     【开始追踪命令】

expect "密码:"                         【免交互执行,捕捉信息并匹配。这里使用的是中文“:”】
send "$password\r"

expect "*]#"                           【这列的“*]#”相当于“[root@localhost /]#”】
send_user "su ok"

interact                               【把控制权交给控制台】
#expect EOF                            【2个结束符都可,但不能同时执行】

使用expect实现创建用户并设置密码

  • 嵌入执行模式,将expect过程融入Shell中,方便执行和处理
[root@localhost /]# ./expect-1.sh qz1 5514
spawn passwd qz1
更改用户 qz1 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
[root@localhost /]# su qz
[qz@localhost /]$ su qz1
密码:
[qz1@localhost /]$
[qz1@localhost /]$ vim expect-1.sh 

#!/bin/bash

user=$1
password=$2                 【非交互的命令放在expect外面】

useradd $user                 
                            
/usr/bin/expect <<EOF      【开始免交互执行】
spawn passwd $user         【开启一个进程跟踪passwd命令,expect只能捕获该进程信息】

expect "新" 
send "${password}\r"
expect "重"
send "${password}\r"
expect EOF
EOF

使用expect实现ssh自动登录

[root@localhost ~]# ./expect-2.sh 192.168.131.10 5514
spawn ssh 192.168.131.10
root@192.168.131.10's password: 
Last login: Fri Mar 19 11:59:10 2021 from 192.168.131.9
[root@localhost ~]# [root@localhost ~]# ifconfig 
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.131.9  netmask 255.255.255.0  broadcast 192.168.131.255
        inet6 fe80::3c3b:eafc:aeef:6a67  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:e3:f1:cd  txqueuelen 1000  (Ethernet)
        RX packets 13468  bytes 1176230 (1.1 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 6055  bytes 776947 (758.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@localhost ~]# vim expect-2.sh

#!/usr/bin/expect
set timeout 10
set host [lindex $argv 0]
set passwd [lindex $argv 1]

spawn ssh $host
expect {
    
    
     "Connection refused" exit                    【连接失败的情况,比如对方ssh服务关闭】
     "Name or service not know" exit              【找不到服务器,比如输入的IP地址不正确】
     "(yes/no)" {
    
    send "yse\r"; exp_continue}
     "*password:" {
    
    send "${passwd}\r"}
}

#interact                                        
expect EOF                                     【以expect为结束符。则执行完命令后会自动切回原用户】
  • 以interact为结束符,会一直在目标服务器终端,而不会切回到原服务器。
[root@localhost ~]# ./expect-2.sh 192.168.131.10 5514
spawn ssh 192.168.131.10
root@192.168.131.10's password: 
Last login: Fri Mar 19 11:57:34 2021 from 192.168.131.9
[root@localhost ~]# ifconfig 
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.131.10  netmask 255.255.255.0  broadcast 192.168.131.255
        inet6 fe80::f8fc:7087:375:efee  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:14:03:40  txqueuelen 1000  (Ethernet)
        RX packets 1272  bytes 147039 (143.5 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 502  bytes 79602 (77.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
[root@localhost ~]# vim expect-2.sh

#!/usr/bin/expect
set timeout 10
set host [lindex $argv 0]
set passwd [lindex $argv 1]

spawn ssh $host
expect {
    
    
     "Connection refused" exit
     "Name or service not know" exit
     "(yes/no)" {
    
    send "yse\r"; exp_continue}
     "*password:" {
    
    send "${passwd}\r"}
}

interact
#expect EOF

猜你喜欢

转载自blog.csdn.net/TaKe___Easy/article/details/114879931