shell脚本向终端输入指令(expect免交互编程)

背景

老规矩还是先写背景,我为什么要起这个标题,是因为我就是用“shell向终端输入指令”作为关键词来检索教程的,结果肯定是没有任何发现,因为实际搜索的应该是括号内的内容。

这个项目需要自动化复现客户现场的一个问题,通过某个测试脚本来不停地执行上下电操作,但是问题是,这个测试脚本需要从终端输出一些命令,他再把对应的命令发下去,例如下面这个界面:

他要求我输出t或者f,来选择模式,这个时候不管你在shell脚本里echo,还是send,都不会有任何响应,我个人的理解是这些数据被输入到终端去了而不是输入到了我们需要的这个测试程序中

(当然这只是我个人的理解,有懂的大佬可以在下面指正我)

(但是我的博客浏览量很低,你们大概率是看不到大佬在下面指正我了)

直接上代码

#!/usr/bin/expect

set timeout 60
	
spawn ./power_control_test_0718 eno1

expect "f:   Soem Firmware Update" {send "t\r"}
expect "card_pulltime default : 200" {send "1000\r"}
sleep 2
# choose card: 1,2,3
expect "please input slave_n:" {send "1\r"}
expect "r:   begin cyclic reading" {send "c\r"}

# choose chanel: 1,3,4
expect "such as: 1" {send "1\r"}
# default: 1
expect "such as: 1" {send "1\r"}

expect "r:   begin cyclic reading" {send "r\r"}
expect "such as: 300000 1000" {send "300000 100\r"}
puts "---Card1:Now get voltage 100 times."
expect "r:   begin cyclic reading" {send "\0\x3\r"}
puts "---Card1:Now exit."
exit

1. 头文件

这里一定要用

#!/usr/bin/expect

而不是

#!/bin/bash

这直接决定了你这个脚本的解释器是什么,我这里使用的是纯粹的expect语法,那么这两种有什么区别呢?区别就是:

shell的打印命令是        echo

expect的打印命令是     puts

shell的赋值命令是        a=1

expect的赋值命令是     set a 1

这只是一个简单的例子,具体区别可以找其他大佬的博文研究一下,其实艺高人胆大的程序猿可以选择混写(就像Python脚本里写shell操作一样),但是我不会,这里就不展开了

2. 设置超时等待时间

set timeout 60 # 设置超时等待时间为60秒

set timeout -1 # 设置永不超时,死等

这个很好理解吧,默认超时时间是10秒,一般这种脚本都是循环起来整夜整夜跑的,尽量不要设置永不超时,不然很容易把环境挂住

3. spawn(跟踪)

spawn ./power_control_test_0718 eno1

spawn后一般跟一个Linux执行命令,表示开启一个会话,启动进程,并跟踪后续交互信息,所以问题就在这里!

我在网口(eno1)上执行了我的测试脚本(power_control_test_0718),一般shell脚本内执行了这一句之后就接着发送其他命令了,而不是“进入”这个测试程序,继续跟踪做输入交互

4. expect/send(捕捉/发送)

expect "f:   Soem Firmware Update" {send "t\r"} # 第一次选择t模式
expect "card_pulltime default : 200" {send "1000\r"}

expect "f:   Soem Firmware Update" {send "f\r"} #第二次选择f模式

所以现在你可以再看一下我开头发的,第一句的意思就是当检测到"f:   Soem Firmware Update"的时候,向屏幕发送"t"

【注】这个\r表示敲击回车,有些程序你输入后就自动跳转了就没有必要敲回车,然鹅有些程序也不要你输入任何东西,你直接敲回车就表示选择默认配置了,自行调整即可

还有一点值得注意的是,这一段程序可以像Python里面的match-case一样进行分支选择

match subject:
    case <pattern_1>:
        <action_1>
    case <pattern_2>:
        <action_2>
    case <pattern_3>:
        <action_3>
    case _:
        <action_wildcard>

所以上面的那三句话也可以写成

expect {
     "f:   Soem Firmware Update" {send "t\r"};exp_continue;}
     expect "card_pulltime default : 200" {send "1000\r"}
}

exp_continue紧跟在某个判断分支的后面,表示匹配完分支1之后还可以匹配分支2,分支3…

但是你们有没有发现少了一句,因为这个确实和分支选择类似,你走到了这个分支,只能有一个操作,如果你想在第一次选板卡的时候选板卡1,第二次同样选择的时候选择板卡2,那你只能采用第一种写法,而不是后面这种

5. exit/expect eof/interact(退出)

  • exit:退出expect脚本
  • expect eof:spawn进程结束后会向expect发送eof,接收到eof代表该进程结束
  • interact :执行完代码后保持交互状态,将控制权交给用户。没有该命令的话,执行完后自动退出而不是留在远程终端上

【例】我们以一个远程登录为例,PC1以aaa用户权限执行expect脚本,功能是切换到root模式后登录PC2的bbb账户,脚本执行结束,选择以下三种方式退出

① exit

仍在PC1上,为aaa用户

② expect eof

仍在PC1上,为aaa用户

③ interact

在PC2上,可以使用bbb用户操作

【注】interact后的命令不起作用,比如interact后再执行exit,并不会退出root用户

【附】shell和expect混写

最后附加一段,shell和expect混写的代码,有兴趣的同学可以去参考Linux学习之expect操作详解

#!/bin/bash

user="mrswhite"

host="192.168.37.9"

password="test20221007"

/usr/bin/expect << EOF

set time 20

spawn ssh $user@$host

expect {

"*yes/no" { send "yes\r"; exp_continue }

"*password:" { send "$password\r" }

}

expect "*#"

send "pwd\r"

expect "*#"

send "df -h\r"

expect "*#"

send "exit\r"

interact

expect eof

EOF

总结

expect是一个很强大的命令,但我只是临时定位问题用到了一下,感觉编程逻辑不是很复杂,和shell基本类似,但是语法是真的太恶心了,if,while,for,包括条件比较都必须严格遵循规定,不如Python简洁,建议大家多用Python
 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值