使用bash捕获交互式程序的输出和输入

时间:2021-10-26 15:41:43

I'm working on automating an interactive command line Java program in bash to validate that the program produces proper output for the input (basically poor-man's unit testing in bash).

我正在使用bash自动化交互式命令行Java程序来验证程序是否为输入生成了正确的输出(基本上是穷人在bash中的单元测试)。

For example, if I have a java program that asks the user to enter their full name, and then outputs only their first name, it should look something like this:

例如,如果我有一个java程序要求用户输入他们的全名,然后只输出他们的名字,它应该是这样的:

Enter your name: John Doe
John

Where "John Doe" was typed in by the user.

用户输入“John Doe”的地方。

A simple bash command to run this might look like this:

运行此命令的简单bash命令可能如下所示:

OUTPUT=`echo "John Doe" | java NameReader`

or

要么

OUTPUT=`java NameReader <<< "John Doe"`

The trouble with both of these is that $OUTPUT now contains the following:

这两个问题都是$ OUTPUT现在包含以下内容:

Enter your name: John

Because the text that was sent to stdin (and its newline accompanying) isn't reproduced in the output of the program the way we would see it in the console.

因为发送到stdin的文本(及其附带的新行)不会在程序的输出中再现,就像我们在控制台中看到它一样。

Ideally, $OUTPUT would contain this:

理想情况下,$ OUTPUT将包含以下内容:

Enter your name: John Doe
John

But I could live with this:

但我可以忍受这个:

Enter your name: 
John

(The input is omitted entirely, but the output is on a new line, as expected)

(输入完全省略,但输出在新行上,如预期的那样)

Is there a way in bash (without altering the underlying java program) to get the text that is being piped to stdin to also pipe to stdout at the "time" it's read by the java program, so the full interactive session is captured?

在bash中是否存在一种方式(不改变底层的java程序)以获取正在通过管道传输到stdin的文本,以便在java程序读取的“时间”内输出到stdout,从而捕获完整的交互式会话?

(One more note: Some searching indicated that the spawn/expect commands might be helpful, but the system this will run on does not appear to have them available)

(还有一点需要注意:有些搜索表明spawn / expect命令可能会有所帮助,但是这个运行的系统似乎没有它们可用)

3 个解决方案

#1


2  

You can use the script command

您可以使用script命令

script -q -c "java NameReader" log.txt

This will record the input and ouput of the java NameReader command in the log.txt file.

这将在log.txt文件中记录java NameReader命令的输入和输出。

#2


1  

If your java program is like this:

如果你的java程序是这样的:

import java.util.Scanner;
class A {
        public static void main(String[] args) {
                Scanner sc = new Scanner(System.in);
                System.out.println(sc.next());
        }
}

then you could build a bash script that would parse an input of the form

那么你可以构建一个解析表单输入的bash脚本

Enter your name: John
John Doe

to transform it into

将其转化为

Enter your name: John Doe
John

Here is one possible bash script:

这是一个可能的bash脚本:

#!/bin/bash
arg1Outpt=`eval "$1"`
javaOut=`echo "$arg1Outpt" | eval "$2"`
#prints Enter your name: John\nJohn Doe      
echo "${javaOut}"$'\n'"${arg1Outpt}" |
                        sed 'N; s/\(Enter your name: \)\(.*\)\(\n\)\(.*\)/\1\4\3\2/'
                        #and this is a multiline sed(man sed) that transforms your 
                        #input into what you want :)

use it like this:

像这样用它:

bash pipeCheat.bash 'echo "john doe"' 'java A'

where pipeCheat.bash is the name of the file where you saved the script above.

其中pipeCheat.bash是您保存上述脚本的文件的名称。

If you have questions don't hesitate to ask.

如果您有疑问,请不要犹豫。

#3


0  

Here's what I ended up doing. It's not a complete solution, but it works in this case and may be useful to someone else facing something similar later:

这就是我最终做的事情。它不是一个完整的解决方案,但它适用于这种情况,对于后来遇到类似事情的其他人可能会有用:

{ sleep 3; echo "John Doe" | tee -a out.txt; } | java  NameReader >> out.txt
OUTPUT=`cat out.txt`
rm out.txt

This causes the script to do two things in parallel:

这会导致脚本并行执行两项操作:

  1. Wait 3 seconds, then write the text "John Doe" to both the pipe and the file out.txt
  2. 等待3秒钟,然后将文本“John Doe”写入管道和文件out.txt
  3. Run the java program.
  4. 运行java程序。

By using tee, the text gets written to both the pipe (which the java program is waiting to read), and to the file. The -a option appends the text to the file instead of overwriting it.

通过使用tee,文本将被写入管道(java程序正在等待读取)和文件。 -a选项将文本附加到文件而不是覆盖它。

The java program also appends its content to out.txt (using >>).

java程序还将其内容附加到out.txt(使用>>)。

Then we read the file contents into the OUTPUT variable and delete the file.

然后我们将文件内容读入OUTPUT变量并删除该文件。

This isn't really a complete solution, because...

这不是一个完整的解决方案,因为......

  1. It works as long as the java program is running and waiting for the user input within the 3 second sleep. If it's not, then the output will be garbled. While it's reasonable (in this case) to expect the program to be waiting for input after 3 seconds, it's certainly not guaranteed.
  2. 只要java程序正在运行并在3秒睡眠期间等待用户输入,它就可以工作。如果不是,那么输出将是乱码。虽然合理(在这种情况下)期望程序在3秒后等待输入是合理的,但肯定不能保证。
  3. The 3 second delay makes the process slower than necessary. If the java program was ready for input after only 1 second, then we're spending 2 extra seconds doing nothing. If this were to be run many times, that could add up.
  4. 3秒延迟使得过程慢于必要。如果java程序在1秒之后就可以输入了,那么我们将花费额外的2秒钟无所事事。如果要多次运行,那可能会增加。
  5. It's only really applicable to interactive programs that take a single piece of input. It might be possible to use multiple "sleep/echo/tee" constructs with different sleep lengths to respond to multiple lines, but that seems like it would get both complex and slow very quickly.
  6. 它只适用于采用单一输入的交互式程序。有可能使用具有不同睡眠长度的多个“睡眠/回声/ T形”构造来响应多行,但这似乎会很快变得复杂和缓慢。

...So this is the best I've got for now. Better solutions are still invited, of course.

......所以这是我现在所拥有的最好的。当然,仍然会邀请更好的解决方案。

#1


2  

You can use the script command

您可以使用script命令

script -q -c "java NameReader" log.txt

This will record the input and ouput of the java NameReader command in the log.txt file.

这将在log.txt文件中记录java NameReader命令的输入和输出。

#2


1  

If your java program is like this:

如果你的java程序是这样的:

import java.util.Scanner;
class A {
        public static void main(String[] args) {
                Scanner sc = new Scanner(System.in);
                System.out.println(sc.next());
        }
}

then you could build a bash script that would parse an input of the form

那么你可以构建一个解析表单输入的bash脚本

Enter your name: John
John Doe

to transform it into

将其转化为

Enter your name: John Doe
John

Here is one possible bash script:

这是一个可能的bash脚本:

#!/bin/bash
arg1Outpt=`eval "$1"`
javaOut=`echo "$arg1Outpt" | eval "$2"`
#prints Enter your name: John\nJohn Doe      
echo "${javaOut}"$'\n'"${arg1Outpt}" |
                        sed 'N; s/\(Enter your name: \)\(.*\)\(\n\)\(.*\)/\1\4\3\2/'
                        #and this is a multiline sed(man sed) that transforms your 
                        #input into what you want :)

use it like this:

像这样用它:

bash pipeCheat.bash 'echo "john doe"' 'java A'

where pipeCheat.bash is the name of the file where you saved the script above.

其中pipeCheat.bash是您保存上述脚本的文件的名称。

If you have questions don't hesitate to ask.

如果您有疑问,请不要犹豫。

#3


0  

Here's what I ended up doing. It's not a complete solution, but it works in this case and may be useful to someone else facing something similar later:

这就是我最终做的事情。它不是一个完整的解决方案,但它适用于这种情况,对于后来遇到类似事情的其他人可能会有用:

{ sleep 3; echo "John Doe" | tee -a out.txt; } | java  NameReader >> out.txt
OUTPUT=`cat out.txt`
rm out.txt

This causes the script to do two things in parallel:

这会导致脚本并行执行两项操作:

  1. Wait 3 seconds, then write the text "John Doe" to both the pipe and the file out.txt
  2. 等待3秒钟,然后将文本“John Doe”写入管道和文件out.txt
  3. Run the java program.
  4. 运行java程序。

By using tee, the text gets written to both the pipe (which the java program is waiting to read), and to the file. The -a option appends the text to the file instead of overwriting it.

通过使用tee,文本将被写入管道(java程序正在等待读取)和文件。 -a选项将文本附加到文件而不是覆盖它。

The java program also appends its content to out.txt (using >>).

java程序还将其内容附加到out.txt(使用>>)。

Then we read the file contents into the OUTPUT variable and delete the file.

然后我们将文件内容读入OUTPUT变量并删除该文件。

This isn't really a complete solution, because...

这不是一个完整的解决方案,因为......

  1. It works as long as the java program is running and waiting for the user input within the 3 second sleep. If it's not, then the output will be garbled. While it's reasonable (in this case) to expect the program to be waiting for input after 3 seconds, it's certainly not guaranteed.
  2. 只要java程序正在运行并在3秒睡眠期间等待用户输入,它就可以工作。如果不是,那么输出将是乱码。虽然合理(在这种情况下)期望程序在3秒后等待输入是合理的,但肯定不能保证。
  3. The 3 second delay makes the process slower than necessary. If the java program was ready for input after only 1 second, then we're spending 2 extra seconds doing nothing. If this were to be run many times, that could add up.
  4. 3秒延迟使得过程慢于必要。如果java程序在1秒之后就可以输入了,那么我们将花费额外的2秒钟无所事事。如果要多次运行,那可能会增加。
  5. It's only really applicable to interactive programs that take a single piece of input. It might be possible to use multiple "sleep/echo/tee" constructs with different sleep lengths to respond to multiple lines, but that seems like it would get both complex and slow very quickly.
  6. 它只适用于采用单一输入的交互式程序。有可能使用具有不同睡眠长度的多个“睡眠/回声/ T形”构造来响应多行,但这似乎会很快变得复杂和缓慢。

...So this is the best I've got for now. Better solutions are still invited, of course.

......所以这是我现在所拥有的最好的。当然,仍然会邀请更好的解决方案。

相关文章