我用 LinuxLinux学习|Gentoo/Arch/FreeBSDLinux运维进阶-Python,Docker,Shell

Here Documents 结合expect的使用--(3)

2019-10-13  本文已影响0人  My熊猫眼

在上一节中,我们学习了 expect_out,但是还没有演示到底如何使用expect_out来获得spawn出来的sub-process中执行的命令的结果,这里我们来演示如下:

#我们这里来获取top命令的前10行结果,脚本内容如下:
[root@instance-6p8qz002 ~]# cat m.sh
#!/bin/sh
echo "" >./exp_log
for i in `cat t.txt`;do
        ofile=$i
        expect <<-EOF
        log_file ./exp_log
        set output [open $ofile w]
        spawn ssh test_user@$i
        expect "assword"
        send "$1\r"               
        expect "~]"
        send "top -bn 1 | head \r"
        send "\r"    #如果没有这一行,那么获取到的结果只有9行,而不是10行
        #如果不想用 send "\r"这一行,还想获得10行的输出怎么办?这时候,我们可以通过修改expect的表达式为: expect -re "~]"  来实现
        expect -re "(.*)\r\n(.*)\r\n(.*)~]"  
        set outcome \$expect_out(1,string)
        puts \$output \$outcome
        send "exit\r"
        expect eof
        exit
EOF
done
[root@instance-6p8qz002 ~]#
#top -bn1 |head 的结果保存在文件localhost中,查看localhost的结果:
[root@instance-6p8qz002 ~]# cat localhost
$ top -bn 1 | head
top - 12:11:54 up 7 days, 23:50,  8 users,  load average: 0.00, 0.01, 0.05
Tasks: 109 total,   2 running, 107 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1014876 total,    81292 free,   190392 used,   743192 buff/cache
KiB Swap:        0 total,        0 free,        0 used.   591152 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    1 root      20   0  125476   3584   2236 S  0.0  0.4   0:08.30 systemd
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.00 kthreadd
    3 root      20   0       0      0      0 S  0.0  0.0   0:54.61 ksoftirqd/0

在上面的脚本中, send "\r" 这一句非常重要,否则得到的结果只有9行,而不是10行,原因是什么呢?
如果没有send "\r" 这一行,我们可以看到exp_log的内容如下:

  1
  2 spawn ssh test_user@localhost^M
  3 test_user@localhost's password: ^M
  4 Last login: Sun Oct 13 12:11:54 2019 from ::1^M^M
  5 ^[[?1034h[test_user@instance-6p8qz002 ~]$ top -bn 1 | head ^M
  6 top - 12:21:44 up 8 days, 0 min,  9 users,  load average: 0.00, 0.01, 0.05^M
  7 Tasks: 111 total,   1 running, 110 sleeping,   0 stopped,   0 zombie^M
  8 %Cpu(s):  0.0 us,  0.0 sy,  0.0 ni, 93.3 id,  6.7 wa,  0.0 hi,  0.0 si,  0.0 st^M
  9 KiB Mem :  1014876 total,    77276 free,   193380 used,   744220 buff/cache^M
 10 KiB Swap:        0 total,        0 free,        0 used.   587960 avail Mem ^M
 11 ^M
 12   PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND^M
 13     1 root      20   0  125476   3584   2236 S  0.0  0.4   0:08.31 systemd^M
 14     2 root      20   0       0      0      0 S  0.0  0.0   0:00.00 kthreadd^M
 15     3 root      20   0       0      0      0 S  0.0  0.0   0:54.67 ksoftirqd/0^M
 16 [test_user@instance-6p8qz002 ~]$ exit^M
 17 logout^M
 18 Connection to localhost closed.^M^M

在脚本中的正则表达式为: (.*)\r\n(.*)\r\n(.*)~], 其匹配的内容分析如下:
1). 表达式的最后为 ~], 最大只能匹配到 16行的 ~]
2). 表达式匹配的开始位置为:第5行的$符号处
3). 在上述的5到16行之间,需要两个 换行符好进行分割,而实际上我们可以看到每一行都有一个换行符号,但是根据从前向后,每个(.*)都是尽可能的最大程度去匹配的原则,所以,第一个 (.*)匹配的就是 第5行的$到14行的末尾,而第二个(.*)匹配的就是15行,最后一个(.*)匹配的就是16行的开始到字符串“8qz002"
到这里,相信你已经get到了吧!!!是不是so easy ?

如果我们修改上述脚本,尝试发送的命令是: top -bn2 , 而不是top -bn1|head, 我们是否还可以获得期望的输出呢? 答案是否定的,因为 expect_out 并不是无限大的,当expect_out满了以后,就会丢弃匹配结果中前面的部分;那非要获得输出结果的全部应该怎么办呢?
当expect_out满的时候,会触发 full_buffer 的匹配,我们只需要对这个full_buffer的匹配进行处理,把expect_out的内容取出,然后再继续进行expect就可以了(通过exp_continue),代码如下:

#!/bin/bash
echo "" >./exp_log
for i in `cat t.txt`;do
        ofile=$i
        expect <<-EOF
        set outcome ""      #设置自定义的变量outcome.
        log_file ./exp_log
        set output [open $ofile w]
        spawn ssh test_user@$i
        expect "assword"
        send "$1\r"
        expect "~]"
        send "top -bn2\r"
        expect {                    #在此expect中可能触发 full_buffer,所以修改该段expect的代码
        full_buffer {              #添加对full_buffer匹配的处理;
                append outcome \$expect_out(buffer)  #在匹配full_buffer的时候,把结果追加到outcome中;
                exp_continue       #为了保证expect结构可以正常运行,这里需要用exp_continue确保full_buffer处理之后可以继续进行expect的处理;
        }
        -re "~]" {    #这里不建议采用分组的方式,因为分组不合适可能导致部分输出的内容丢失;
        append outcome \$expect_out(buffer)  #注意这里的append, 不能使用set ,否则会清空outcome中已经有的内容;  
        }
                }
        puts \$output \$outcome
        send "exit\r"
        expect eof
        exit
EOF
done

上面代码中,主要是通过 在原来的expect中添加 full_buffer 匹配的处理分支,在full_buffer匹配的时候,把结果追加到自定义的变量中,如果没有遇到full_buffer,那么也会把expect_out的内容转到 自定义变量中;从而通过自定义变量存储所有的输出结果;

上面这段代码获取命令结果的方法才更具有通用性,毕竟很多情况下,我们是无法知道输出大小的,希望可以帮到你哦!

本文原创,转载请注明出处

上一篇下一篇

猜你喜欢

热点阅读