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

编程入门 行业动态 更新时间:2024-10-27 14:22:08
期望:从输出中提取特定的字符串(Expect: extract specific string from output)

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

期望在我的脚本命令是:

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变量中,以便在期望脚本中进一步使用。 所以这是第三行,第一个字段是使用双空格分隔符。 awk相当于: awk -F ' ' 'NR==3 {$1}'

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

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

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.

最满意答案

你可以直接使用-re标志来使用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(0,string)所有匹配/不匹配的输入expect_out(buffer)到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 over并且模式中的flow匹配字符串中的文本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脚本中的返回值。

看看这里了解info exists命令。

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.

enter image description here

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.

enter image description here

* 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.

enter image description here

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.

更多推荐

本文发布于:2023-04-28 00:26:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1329229.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:字符串   Expect   extract   output   string

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!