期望:从输出中提取特定字符串

时间:2014-11-23 13:59:16

标签: tcl expect

我在远程计算机上导航基于Java的CLI菜单,期望在bash脚本中,我试图从输出中提取一些东西而不离开期望会话。

我的脚本中的Expect命令是:

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脚本中进一步使用。所以这是第3行,第一个字段是使用双空格分隔符。 awk等价物是:awk -F ' ' 'NR==3 {$1}'

最大的问题是,我正在使用Expect导航的环境是,如上所述,基于Java CLI的菜单,所以我不能只使用awk或其他任何可以从bash shell获得的东西。 / p>

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

1 个答案:

答案 0 :(得分:8)

您可以使用regexp标志直接在expect中使用-re。感谢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}。这里的括号可以防止任何变量替换。在您的输出中,我们有第二行充满连字符。然后换行。然后你的第3行内容。让我们解码使用的正则表达式。

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

你可能想知道为什么我需要用于获得子匹配模式的括号。

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

enter image description here

正如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的效果,如下所示。

enter image description here

*匹配任意数量的字符。 更确切地说,它匹配可能的最长字符串,同时仍然允许模式本身匹配。因此,由于此,模式中的.*与字符Stackoverflow is already over和{{匹配模式中的1}}与字符串中的文本flow匹配。

现在,为了防止flow仅匹配字符串.*的第一个匹配项,我们正在向其添加flow。它将有助于模式表现为非贪婪的方式。

enter image description here

现在,再次回到你的问题。如果我们使用了?,那么它将匹配整行,因为它尽可能地匹配。这是正则表达式的常见行为。

更新版本3:

以下列方式使用您的代码。

.*\s\s

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

请查看here以了解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; 命令。