expect_out(buffer)中包含send的数据

本文揭示了一个在使用Expect脚本时容易忽视的问题:expect_out(buffer)不会自动清空,可能导致expect匹配到旧数据。通过修改send和expect语句或利用正则表达式改进,可以有效解决该问题。

expect_out(buffer)中包含send的数据

我一直以为在Expect中一旦执行send之后,expect_out(buffer)就会被清空,直到有新的数据被填入,而恰恰就是这些数据被用在expect语句中。而今天在调试时发现的问题却令我大吃一惊,原来expect_out(buffer)不会被自动清空,而expect到的数据很可能不是你真正想要的,非常有可能是历史数据。"expect_out(buffer) has the content of the previous send"一文也指出了这个问题。

比如在我的代码中,有如下片段:
set prompt "#" set lockfile "/root/tmp/.cool.lock" send " if /[ -e $lockfile ]; then cat $lockfile else echo /"COOLID: $coolid/" > $lockfile echo DONE! fi /r" sleep 1 ;# necessary expect { timeout { puts stderr "WARNING: $ME: cannot verify if the Linux AP is locked by other COOL session!"; exit 2; } -re "COOLID: (/[1-9](/[0-9])*).*$prompt" { puts stdout "expect_out(buffer)=$expect_out(buffer)" puts stdout "expect_out(0,string)=$expect_out(0,string)" puts stdout "expect_out(1,string)=$expect_out(1,string)" puts stdout "expect_out(2,string)=$expect_out(2,string)" puts stdout "expect_out(3,string)=$expect_out(3,string)" } "DONE!" ;# do nothing }
无论$ lockfile是否存在,expect总会得到COOLID: 68,而expect_out(buffer)的出如下:

[root@bjvsmcl05 ~]# expect_out(buffer)= Last login: Fri Jul 23 09:32:13 2010 from bjbldd [root@bjvsmcl05 ~]# [root@bjvsmcl05 ~]# if [ -e /root/tmp/.cool.lock ]; then > cat /root/tmp/.cool.lock > else > echo "COOLID: 68" > /root/tmp/.cool.lock > echo DONE! > fi COOLID: 1234 [root@bjvsmcl05 ~]# [root@bjvsmcl05 ~]# expect_out(0,string)=23 09:32:13 2010 from bjbldd [root@bjvsmcl05 ~]# [root@bjvsmcl05 ~]# if [ -e /root/tmp/.cool.lock ]; then > cat /root/tmp/.cool.lock > else > echo "COOLID: 68" > /root/tmp/.cool.lock > echo DONE! > fi COOLID: 1234

由此可见,由于 expect_out(buffer)中含有send的 echo "COOLID: 68" ,因此永远都能匹配到这个数据。

由于没有找到一种可以清空 expect_out(buffer)的方法,无法实时地将 expect_out(buffer)清空。 我能想到的解决方法有两种:一是更改send和expect的内容或方式,避免send的内容中出现expect的模式(pattern)。比如在我的程序中把上述代码段改为:
set lockfile "/root/tmp/.cool.lock" send "cat $lockfile/r" expect { -re "COOLID: (/[1-9](/[0-9])*)" { puts stdout "expect_out(buffer)=$expect_out(buffer)" puts stdout "expect_out(0,string)=$expect_out(0,string)" puts stdout "expect_out(1,string)=$expect_out(1,string)" if { [string compare $expect_out(1,string) $coolid] } { puts stdout "ERROR: $ME: $pcname has been reserverd by COOL session $expect_out(1,string), please choose another Linux PC instead./n" exit 2; } } "cat: $lockfile: No such file or directory*" { send "echo /"COOLID: $coolid/" > $lockfile/r" } }
就可以正常工作了。

另一种方法是按照 "expect_out(buffer) has the content of the previous send" 的例子中的方法,set prompt,保持原始代码中send的内容不变,将expect的正则表达式改为 -re "fi( .*) $prompt",如此一来希望的输出就在expect_out(1,string)中了,然后利用string或者lsearch命令对匹配到的数据进行分析。

结论:今后在编写Expect脚本时,不仅要考虑每一步的send/expect,也还要考虑到这一步的expect的内容有可能出现在之前的历史数据中。

#include "main.h" #include "RS485.h" #include "read_param.h" #include <string.h> #include <stdio.h> #include <stdlib.h> #include "tjc_usart_hmi.h" // 全局变量 volatile ComCtrl com_ctrl = {0}; volatile uint16_t param_values[NUM_PARAMS] = {0}; uint8_t rx_char = 0; // 串口接收字符缓存 volatile uint8_t parsing = 0; // 解析状态标志 volatile uint8_t rx_index = 0; // 接收缓冲区索引 volatile uint8_t param_index = 0; // 当前参数索引 // 参数数组 const char* param_cmds[NUM_PARAMS] = { "get wendushezhi.x0.val" CMD_END, "get wendushezhi.x1.val" CMD_END, "get wendushezhi.x2.val" CMD_END, "get wendushezhi.x3.val" CMD_END, "get wendushezhi.x4.val" CMD_END, "get wendushezhi.x5.val" CMD_END, "get wendushezhi.x6.val" CMD_END, "get wendushezhi.x7.val" CMD_END, "get wendushezhi.x8.val" CMD_END, "get wendushezhi.x9.val" CMD_END, "get wendushezhi.x10.val" CMD_END, "get wendushezhi.x11.val" CMD_END, "get rtc0" CMD_END, //年 "get rtc1" CMD_END, //月 "get rtc2" CMD_END, //日 "get rtc3" CMD_END, //时 "get rtc4" CMD_END, //分 "get rtc5" CMD_END, //秒 }; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM1){ // 超时重传检测(300ms) if((HAL_GetTick() - com_ctrl.last_send_time) > 300){ com_ctrl.retry_count = 0; com_ctrl.expect_index = com_ctrl.current_index; } // 状态机控制发送流程 if(com_ctrl.retry_count == 0){ HAL_UART_Transmit(&TJC_UART, (uint8_t*)param_cmds[com_ctrl.current_index], strlen(param_cmds[com_ctrl.current_index]), 100); com_ctrl.last_send_time = HAL_GetTick(); com_ctrl.retry_count = 1; com_ctrl.current_index = (com_ctrl.current_index + 1) % NUM_PARAMS; } } } /* 接收回调函数*/ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == TJC_UART_INS) { //串口1 uint8_t data = rx_char; switch(rx_state.state){ case RX_STATE_IDLE: if(data == RESP_HEADER){ rx_state.buffer[0] = data; rx_state.index = 1; rx_state.state = RX_STATE_HEADER; } break; case RX_STATE_HEADER: case RX_STATE_DATA: if(rx_state.index < PACKET_SIZE){ rx_state.buffer[rx_state.index++] = data; } // 检测结束标志 if(rx_state.index >= 5 && rx_state.buffer[rx_state.index-3] == 0xFF && rx_state.buffer[rx_state.index-2] == 0xFF && rx_state.buffer[rx_state.index-1] == 0xFF){ // 完整数据包验证 if(rx_state.index == PACKET_SIZE){ // TJC_Packet packet; memcpy(&packet, rx_state.buffer, sizeof(packet)); // 校验协议格式 if(packet.header == RESP_HEADER && packet.end[0] == 0xFF && packet.end[1] == 0xFF && packet.end[2] == 0xFF){ // 提取普通参数值 uint16_t extracted_value = (packet.data[1] << 8) | packet.data[0]; // 存储到对应参数位置 if(com_ctrl.expect_index < NUM_PARAMS){ param_values[com_ctrl.expect_index] = extracted_value; com_ctrl.retry_count = 0; // 重置重试计数器 } } } rx_state.state = RX_STATE_IDLE; rx_state.index = 0; } else { rx_state.state = RX_STATE_DATA; } break; } HAL_UART_Receive_IT(&TJC_UART, &rx_char, 1); } } 我写了串口接收函数,但是通过先向屏幕发送指令,屏幕返回来的数据,从而读取屏幕上的一些参数,但现在是屏幕那边发送过来数据,单片机接收,改怎么写代码,上面的代码对我还有用,报警就是靠这些函数读取屏幕上的设定值才能报警,所以你不能把这些代码原本的功能改了
最新发布
11-04
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值