2014-11-23 74 views
0

我在远程计算机上浏览基于Java的CLI菜单,期望在bash脚本中运行,我试图在不离开期望会话的情况下从输出中提取某些内容。期望:从输出中提取特定的字符串

期待在我的脚本命令是:

expect -c " 
spawn ssh [email protected] 
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到另一所期望的期望脚本中进一步使用变量。所以这是第三行,第一个字段是使用双空格分隔符。 awk相当于:awk -F ' ' 'NR==3 {$1}'

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

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

+0

问题的核心在于你试图将期望脚本放入双引号的shell文本中。这会迫使你使用_Lots_的反斜杠引用。虽然您可以这样做,但将期望脚本放在自己的文件中(例如'mydostuff.exp')会使一切变得简单。然后你可以用'expect mydostuff.exp'运行它... – 2014-11-23 23:21:47

回答

4

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

我已经创建了内容的文件,如下所示,

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

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

双引号:

#!/bin/bash 
expect -c " 
spawn ssh [email protected] 
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 [email protected] 
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)等等。

enter image description here

正如多纳尔指出的那样,最好使用单引号的方法,因为它看起来不太凌乱。 :)

如果是双引号,则不需要用反斜杠来转义\r

更新:

我从-\r\n(\w+\s+\w+)\s\s改变regexp-\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+将不匹配空格字符。你的输出中有一些空格,直到双空格。

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

更新版2:

什么是.*?意义。如果您另外提问,我将重复您所评论的内容。在正则表达式中,*是一个贪婪的运算符,?是我们的救命。让我们考虑字符串作为

Stackoverflow is already overflowing with number of users. 

现在,看到正则表达式.*flow如下的效果。

enter image description here

*匹配任何数目的字符。 更确切地说,它匹配的字符串,最长的可能,同时仍允许模式本身相匹配。因此,由于这个原因,.*在模式匹配的字符Stackoverflow is already over和模式flow匹配的字符串文本flow

现在,为了防止.*仅匹配字符串flow的第一个匹配项,我们将?添加到它。这将有助于该模式表现为非贪婪的态度。

enter image description here

现在,再来回到你的问题。如果我们使用.*\s\s,那么它将整条生产线,因为它正试图尽可能地匹配匹配。这是正则表达式的共同行为。

更新版本3:

在下列方式你的代码。

x=$(expect -c " 
spawn ssh [email protected] 
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脚本的返回值。

看看here了解info exists命令。

+0

你需要反斜杠引用'$',否则unix shell会破坏它;使用'expect -c“...”“只是增加了所有事情的烦人混乱层次。 '\ r's可能还需要一个额外的反斜杠。 – 2014-11-23 23:23:28

+0

@linux_newbie:我已经更新了我的答案。请检查。 – Dinesh 2014-11-24 12:39:38

+0

@DonalFellows:非常感谢Donal帮助我了解更多信息。我已更正以及测试我的答案。让我知道任何其他变化。 – Dinesh 2014-11-24 12:40:43