期望:从输出中提取特定的字符串 [英] Expect: extract specific string from output

查看:62
本文介绍了期望:从输出中提取特定的字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在bash脚本中的带有期望的远程计算机上导航基于Java的CLI菜单,并且试图从输出中提取某些内容而不离开期望会话.

我的脚本中的预期命令是:

expect -c "
spawn ssh user@host
expect \"#\"
send \"java cli menu command here\r\"
expect \"java cli prompt\"
send \"java menu command\"
"
###I want to extract a specific string from the above output###

预期输出为:

Id       Name
-------------------
abcd 12  John Smith

我想从上述输出中提取abcd 12到另一个Expect变量中,以便在Expect脚本中进一步使用.这就是第三行,即使用双倍空格定界符的第一个字段. awk等效项为:awk -F ' ' 'NR==3 {$1}'

最大的问题是,如上所述,我使用Expect导航的环境是一个基于Java CLI的菜单,所以我不能只使用awk或bash shell可以提供的其他任何功能. /p>

从Java菜单中退出,处理输出然后再次进入不是一个选择,因为登录过程持续15秒,因此我需要留在里面,仅使用Expect内部命令从输出中提取所需的内容.

解决方案

您可以直接使用-re标志在expect本身中使用regexp.感谢Donal指出单引号和双引号问题.我已经给出了两种方法的解决方案.

我创建了一个文件,其内容如下,

Id       Name
-------------------
abcd 12  John Smith

这只是Java程序的控制台输出.我已经在我的系统中对此进行了测试.即我只是使用cat模拟了您程序的输出.您只需将cat代码替换为程序命令即可.简单的. :)

双引号:

#!/bin/bash
expect -c "
spawn ssh user@domain
expect \"password\"
send \"mypassword\r\"
expect {\\\$} { puts matched_literal_dollar_sign}
send \"cat input_file\r\"; # Replace this code with your java program commands
expect -re {-\r\n(.*?)\s\s}
set output \$expect_out(1,string)
#puts \$expect_out(1,string)
puts \"Result : \$output\"
"

单引号:

#!/bin/bash
expect -c '
spawn ssh user@domain
expect "password"
send "mypasswordhere\r"
expect "\\\$" { puts matched_literal_dollar_sign}
send "cat input_file\r"; # Replace this code with your java program commands
expect -re {-\r\n(.*?)\s\s}
set output $expect_out(1,string)
#puts $expect_out(1,string)
puts "Result : $output"
'

如您所见,我已经使用过{-\r\n(.*?)\s\s}.大括号可以防止任何变量替换.在您的输出中,我们有带有连字符的第二行.然后换行.然后是您的第三行内容.让我们对使用的正则表达式进行解码.

-\r\n是将一个文字连字符和换行符匹配在一起.这将匹配第二行中的最后一个连字符和换行符,从而使它现在变为第三行.因此,.*?将匹配所需的输出(即abcd 12),直到遇到与\s\s匹配的双倍空格.

您可能想知道为什么我需要用括号来获取子匹配模式.

通常,expect将期望的整个匹配字符串保存在expect_out(0,string)中,并将所有匹配/不匹配的输入缓冲到expect_out(buffer)中.每个子匹配项将保存在字符串的后续编号中,例如expect_out(1,string)expect_out(2,string)等.

如Donal所指出的,最好使用单引号的方法,因为它看起来不那么混乱. :)

在双引号的情况下,不需要使用反斜杠对\r进行转义.

更新:

我已将regexp-\r\n(\w+\s+\w+)\s\s更改为-\r\n(.*?)\s\s.

通过这种方式-您的要求-例如match any number of letters and single spaces until you encounter first occurrence of double spaces in the output

现在,让我们谈谈您的问题.您已经提到您尝试过-\r\n(\w+)\s\s.但是,\w+在这里有问题.请记住,\w+将不匹配空格字符.您的输出中有一些空格直到双倍空格.

根据您对将要匹配的输入字符串的要求,使用regexp很重要.您可以根据需要自定义正则表达式.

更新版本2:

.*?的意义是什么.如果您单独询问,我将重复您的评论.在正则表达式中,*是贪婪的运算符,而?是我们的救生器.让我们将字符串视为

Stackoverflow is already overflowing with number of users.

现在,请参见正则表达式.*flow的效果,如下所示.

*匹配任意数量的字符. 更精确地说,它匹配可能的最长字符串,同时仍允许模式本身匹配.因此,由于此原因,模式中的.*匹配了模式匹配中的字符Stackoverflow is already overflow字符串中的文本flow.

现在,为了防止.*仅匹配到字符串flow的第一次出现,我们在其中添加了?.这将有助于模式表现为非贪婪的方式.

现在,再次回到您的问题.如果我们使用了.*\s\s,则它将匹配整行,因为它试图尽可能地匹配.这是正则表达式的常见行为.

更新版本3:

通过以下方式编写代码.

x=$(expect -c "
spawn ssh user@host
expect \"password\"
send \"password\r\"
expect {\\\$} { puts matched_literal_dollar_sign}
send \"cat input\r\"
expect -re {-\r\n(.*?)\s\s}
if {![info exists expect_out(1,string)]} {
        puts \"Match did not happen :(\"
        exit 1
}
set output \$expect_out(1,string)
#puts \$expect_out(1,string)
puts \"Result : \$output\"
")
y=$?

# $x now contains the output from the 'expect' command, and $y contains the
# exit status
echo $x
echo $y;

如果流程正确发生,则退出代码的值将为0.否则,它将为1.这样,您可以在bash脚本中检查返回值.

此处上了解 c37>命令.

I am navigating a Java-based CLI menu on a remote machine with expect inside a bash script and I am trying to extract something from the output without leaving the expect session.

Expect command in my script is:

expect -c "
spawn ssh user@host
expect \"#\"
send \"java cli menu command here\r\"
expect \"java cli prompt\"
send \"java menu command\"
"
###I want to extract a specific string from the above output###

Expect output is:

Id       Name
-------------------
abcd 12  John Smith

I want to extract abcd 12 from the above output into another expect variable for further use within the expect script. So that's the 3rd line, first field by using a double-space delimiter. The awk equivalent would be: awk -F ' ' 'NR==3 {$1}'

The big issue is that the environment through which I am navigating with Expect is, as I stated above, a Java CLI based menu so I can't just use awk or anything else that would be available from a bash shell.

Getting out from the Java menu, processing the output and then getting in again is not an option as the login process lasts for 15 seconds so I need to remain inside and extract what I need from the output using expect internal commands only.

解决方案

You can use regexp in expect itself directly with the use of -re flag. Thanks to Donal on pointing out the single quote and double quote issues. I have given solution using both ways.

I have created a file with the content as follows,

Id       Name
-------------------
abcd 12  John Smith

This is nothing but your java program's console output. I have tested this in my system with this. i.e. I just simulated your program's output with cat. You just replace the cat code with your program commands. Simple. :)

Double Quotes :

#!/bin/bash
expect -c "
spawn ssh user@domain
expect \"password\"
send \"mypassword\r\"
expect {\\\$} { puts matched_literal_dollar_sign}
send \"cat input_file\r\"; # Replace this code with your java program commands
expect -re {-\r\n(.*?)\s\s}
set output \$expect_out(1,string)
#puts \$expect_out(1,string)
puts \"Result : \$output\"
"

Single Quotes :

#!/bin/bash
expect -c '
spawn ssh user@domain
expect "password"
send "mypasswordhere\r"
expect "\\\$" { puts matched_literal_dollar_sign}
send "cat input_file\r"; # Replace this code with your java program commands
expect -re {-\r\n(.*?)\s\s}
set output $expect_out(1,string)
#puts $expect_out(1,string)
puts "Result : $output"
'

As you can see, I have used {-\r\n(.*?)\s\s}. Here the braces prevent any variable substitutions. In your output, we have a 2nd line with full of hyphens. Then a newline. Then your 3rd line content. Let's decode the regex used.

-\r\n is to match one literal hyphen and a new line together. This will match the last hyphen in the 2nd line and the newline which in turn make it to 3rd line now. So, .*? will match the required output (i.e. abcd 12) till it encounters double space which is matched by \s\s.

You might be wondering why I need parenthesis which is used to get the sub-match patterns.

In general, expect will save the expect's whole match string in expect_out(0,string) and buffer all the matched/unmatched input to expect_out(buffer). Each sub match will be saved in subsequent numbering of string such as expect_out(1,string), expect_out(2,string) and so on.

As Donal pointed out, it is better to use single quote's approach since it looks less messy. :)

It is not required to escape the \r with the backslash in case of double quotes.

Update :

I have changed the regexp from -\r\n(\w+\s+\w+)\s\s to -\r\n(.*?)\s\s.

With this way - your requirement - such as match any number of letters and single spaces until you encounter first occurrence of double spaces in the output

Now, let's come to your question. You have mentioned that you have tried -\r\n(\w+)\s\s. But, there is a problem here with \w+. Remember \w+ will not match space character. Your output has some spaces in it till double spaces.

The use of regexp will matter based on your requirements on the input string which is going to get matched. You can customize the regular expressions based on your needs.

Update version 2 :

What is the significance of .*?. If you ask separately, I am going to repeat what you commented. In regular expressions, * is a greedy operator and ? is our life saver. Let us consider the string as

Stackoverflow is already overflowing with number of users.

Now, see the effect of the regular expression .*flow as below.

* matches any number of characters. More precisely, it matches the longest string possible while still allowing the pattern itself to match. So, due to this, .* in the pattern matched the characters Stackoverflow is already over and flow in pattern matched the text flow in the string.

Now, in order to prevent the .* to match only up to the first occurrence of the string flow, we are adding the ? to it. It will help the pattern to behave as non-greedy manner.

Now, again coming back to your question. If we have used .*\s\s, then it will match the whole line since it is trying to match as much as possible. This is common behavior of regular expressions.

Update version 3:

Have your code in the following way.

x=$(expect -c "
spawn ssh user@host
expect \"password\"
send \"password\r\"
expect {\\\$} { puts matched_literal_dollar_sign}
send \"cat input\r\"
expect -re {-\r\n(.*?)\s\s}
if {![info exists expect_out(1,string)]} {
        puts \"Match did not happen :(\"
        exit 1
}
set output \$expect_out(1,string)
#puts \$expect_out(1,string)
puts \"Result : \$output\"
")
y=$?

# $x now contains the output from the 'expect' command, and $y contains the
# exit status
echo $x
echo $y;

If the flow happened properly, then exit code will have value as 0. Else, it will have 1. With this way, you can check the return value in bash script.

Have a look at here to know about the info exists command.

这篇关于期望:从输出中提取特定的字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆