shell脚本之Expect免交互的实现

一、 Shell Here Document(内嵌文档/嵌入文档)

Shell 还有一种特殊形式的重定向叫做“Here Document”,目前没有统一的翻译,你可以将它理解为“嵌入文档”“内嵌文档”“立即文档”。

所谓文档,就是命令需要处理的数据或者字符串;所谓嵌入,就是把数据和代码放在一起,而不是分开存放(比如将数据放在一个单独的文件中)。有时候命令需要处理的数据量很小,将它放在一个单独的文件中有点“大动干戈”,不如直接放在代码中来得方便。

1.1 Here Document 的基本用法为:

command <<END
  document
END

command是 Shell 命令,<<END是开始标志,END是结束标志,document是输入的文档(也就是一行一行的字符串)。

这种写法告诉 Shell 把 document 部分作为命令需要处理的数据,直到遇见终止符END为止(终止符END不会被读取)。

注意,终止符END必须独占一行,并且要定顶格写。

分界符(终止符)可以是任意的字符串,由用户自己定义,比如 END、MARKER 等。分界符可以出现在正常的数据流中,只要它不是顶格写的独立的一行,就不会被作为结束标志。

1.2 注意事项

  • 标记可以使用任意合法字符

  • 结尾的标记一定要顶格写,前面不能有任何字符!!

  • 结尾的标记后面也不能有任何字符(包括空格)

  • 开头标记前后的空格会被省略掉

【实例1】cat 命令一般是从文件中读取内容,并将内容输出到显示器上,借助 Here Document,cat 命令可以从键盘上读取内容。

[mozhiyan@localhost ~]$ cat <<END
> Shell教程
> http://c.biancheng.net/shell/
> 已经进行了三次改版
> END
Shell教程
http://c.biancheng.net/shell/
已经进行了三次改版

<是第二层命令提示符。

正文中也可以出现结束标志END,只要它不是独立的一行,并且不顶格写,就没问题。

[mozhiyan@localhost ~]$ cat <<END
> END可以出现在行首
> 出现在行尾的END
> 出现在中间的END也是允许的
> END
END可以出现在行首
出现在行尾的END
出现在中间的END也是允许的

1.3 通过read命令接受输入并打印

[root@hua opt]# read i <<EOF
> hello boy
> my name is lisi
> EOF
[root@hua opt]# echo $i
hello boy
#只能赋予第一行的内容

1.4 通过passwd给用户设置密码

[root@shuai ~]# vim shuai.sh
#!/bin/bash
passwd liu <<EOF
123123                    '每一行对应每一次的交互'
123123
EOF
[root@shuai ~]# bash shuai.sh 
更改用户 liu 的密码 。
新的 密码:无效的密码: 密码少于 8 个字符
重新输入新的 密码:passwd:所有的身份验证令牌已经成功更新。

1.5 变量替换

[root@hua opt]# vim txt.sh
#!/bin/bash
shuai="1.txt"
lisi="kgc"
cat > $shuai <<EOF
this is $lisi .com 
EOF
[root@hua opt]# ls
1.txt  EOF  hao.sh  httpd.txt  rh  ss.sh  txt.sh  xx.txt
[root@hua opt]# cat 1.txt 
this is kgc .com 

1.6 变量设定

[root@hua opt]# num1=20
[root@hua opt]# echo $num1 
20
[root@hua opt]# num2=$(cat <<EOF
> 100
> $num1
> EOF
> )
[root@hua opt]# echo $num2 
100 20

1.7 关闭变量的替换功能

[root@hua opt]# echo $num1
20
[root@hua opt]# cat <<'EOF'
> 100
> $num1
> EOF
100
$num1

1.8 去除每行之前的TAB字符

[root@hua opt]# vim b.sh
#!/bin/bash
cat <<-'EOF'                  '-表示抑制行首的TAB作用'
        this is
        $shuai .com
EOF

[root@hua opt]# bash b.sh 
this is
$shuai .com

1.9 通过Here Document方式是Bash支持多行注释

[root@hua opt]# vim n.sh
#!/bin/bash
echo "hello"
echo "girl"
cat <<-'EOF'
        this is 
        $school .ch
EOF
[root@hua opt]# bash n.sh 
hello
girl
this is 
$school .ch
###注释后
[root@hua opt]# vim n.sh
#!/bin/bash
: <<DD
echo "hello"
echo "girl"i
DD
cat <<-'EOF'
        this is 
        $school .ch
EOF
[root@hua opt]# bash n.sh 
this is 
$school .ch

总结

如果你尝试在脚本嵌入一小块多行数据,使用 Here Document 是很有用的,而嵌入很大的数据块是一个不好的习惯。你应该保持你的逻辑(你的代码)和你的输入(你的数据)分离,最好是在不同的文件中,除非是输入一个很小的数据集。

Here Document 最常用的功能还是向用户显示命令或者脚本的用法信息,例如类似下面的函数:

usage(){
    cat <<-END
        usage: command [-x] [-v] [-z] [file ...]
        A short explanation of the operation goes here.
        It might be a few lines long, but shouldn't be excessive.
END
}

二、Expect 介绍与用法

expect是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。

2.1 expect自动交互流程:

spawn启动指定进程—expect获取指定关键字—send向指定程序发送指定字符—执行完成退出.

注意该脚本能够执行的前提是安装了expect

yum install -y expect

2.2 基本命令(expect流程命令)

  • spawn:启动进程,并跟踪后续交互信息

  • send:向进程发送字符串,用于模拟用户的输入

    该命令不能自动回车换行,一般要加、r(回车)

  • expect

    expect的一个内部命令,判断上次输出结果里是否包含指定的字符串,如果有则立即返回,否则就等待超时时间后返回

    只能捕捉有spawn启动的进程的输出

  • interact:执行完成后保持交互状态,把控制权交给控制台

2.3 基本命令(expect内容命令)

  • Timeout:指定超时时间,过期则继续执行后续指令
    • 单位是:秒
    • timeout -1为永不超时
    • 默认情况下,timeout是10秒
  • exp_continue
    • 允许expect继续向下执行指令
  • send_user
    • 回显命令,相当于echo

2.4 基本命令(expect其他命令)

  • $argv参数数组

    Expect脚本可以接受从bash传递的参数,可以使用[lindex $argc n]获得,n从0开始,分别表示第一个,第二个,第三个…参数

    arg:参数

    v:value

  • Expect脚本的结尾

    expect脚本必须以interact或expect eof结束,执行自动化任务通常expect eof就够了

    expect eof是在等待结束标志。由spawn启动的命令在结束时会产生一个eof标记,expect eof就是在等待这个标记

2.5 Expect执行方式

单一分支语法

expect "password:" {send "mypassword\r"}

多分支模式语法第一种

expect "aaa" {send "AAA\r"}
expect "aaa" {send "AAA\r"}
expect "aaa" {send "AAA\r"}
'send命令不具备回车换行功能,所以需要自己添加\r 或 \n'

多分支模式语法第二种

expect {
"aaa" {send "AAA\r"}
"bbb" {send "BBB\r"}
"ccc" {send "CCC\r"}
}
'只要匹配了aaa 或bbb或ccc中的任何一个,执行相应的send语句后就会退出该expect语句'

expect {
"aaa" {send "AAA\r";exp_continue}
"bbb" {send "BBB\r";exp_continue}
"ccc" {send "CCC\r"}
}
'exp_continue表示继续后面的匹配,如果匹配了aaa,执行完send语句后还会继续向下匹配bbb'
'捕捉内容要用双引号引起来'
'send要写在{}中,输出信息也要用双引号引起来,分号“;”要写在}里面'

-re参数表示匹配正则表达式

2.6 Expect直接执行(以ssh为例)

  • 其中,$argv 0 代表位置变量$1

    $argv 1 代表位置变量$2

  • #!/usr/bin/expect 是Expect二进制文件的路径 预加载环境


#!/usr/bin/expect
#超时时间
set timeout 10
#开启日志
log_file test.log
#显示信息
log_user 1

#定义变量
set hostname [lindex $argv 0]
set password [lindex $argv 1]
#追踪指令
spawn ssh root@${hostname}
#捕捉提示信息
expect {
    "connecting (yes/no)"
    {send "yes\r";exp_continue}
    "*password:"
    {send "${password}\r";}
}
#转交控制台
interact
-----------执行----------------
[root@hua opt]# chmod +x 2.sh 
[root@hua opt]# ./2.sh 192.168.1.20 123123
spawn ssh root@192.168.1.20
root@192.168.1.20's password: 
Last login: Tue Jul 28 22:01:09 2020 from 192.168.1.21

2.7 Expect嵌入执行(以ssh为例

[root@hua opt]# vim xx.sh
#!/bin/bash
hostname=$1
password=$2
#expect嵌入
/usr/bin/expect <<-EOF
spawn ssh root@${hostname}

#捕捉提示信息
expect {
  "connecting (yes/no)"
  {send "yes\r";exp_continue}
  "*password:"
  {send "${password}\r";}
}
expect "*]#"
send "exit\r"
expect eof
EOF
#执行
[root@hua opt]# bash xx.sh 192.168.1.20 123123
spawn ssh root@192.168.1.20
root@192.168.1.20's password: 
Last login: Tue Jul 28 22:10:07 2020 from 192.168.1.21
[root@shuai ~]# exit
登出
Connection to 192.168.1.20 closed.

2.8 Expect实操

创建用户lisi6 密码123123

[root@promote ~]# vim ss.sh
#!/bin/bash
user=$1
password=$2
useradd $user
/usr/bin/expect <<-EOF
spawn passwd $user
expect {
      "新的 密码:"
      {send "$password\r";exp_continue}
      "重新输入新的 密码:"
      {send "${password}\r";}
}
expect "成功"
send "exit\r"
expect eof
EOF
[root@promote ~]# bash ss.sh lisi6 123123
spawn passwd lisi6
更改用户 lisi6 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
expect: spawn id exp6 not open
    while executing
"expect "成功""

2.9 SSH登录

  • 首次登录
  • 正常登录
  • 连接被拒绝,可能是ssh没开,或者端口不对,或者防火墙限制
#!/bin/bash
hostname=$1
password=$2
/usr/bin/expect <<-EOF
  spawn ssh $hostname
  expect {
        "connection refused" exit
        "Name or service not known" exit
        "to continue" {send "yes\r";exp_continue}
        "password:" {send "$password\r"}
}
expect "*]#"
send "exit\r"
expect eof
EOF
#正常登录
[root@hua opt]# ./ssh.sh 192.168.1.20 123123
spawn ssh 192.168.1.20
root@192.168.1.20's password: 
Last failed login: Tue Jul 28 22:56:10 CST 2020 from 192.168.1.21 on ssh:notty
There was 1 failed login attempt since the last successful login.
Last login: Tue Jul 28 22:30:52 2020 from 192.168.1.21
[root@shuai ~]# exit
登出
Connection to 192.168.1.20 closed.
[root@hua opt]# vim ssh.sh

猜你喜欢

转载自blog.csdn.net/weixin_44733021/article/details/108204980