【问题标题】:Capturing output and input of interactive program with bash使用 bash 捕获交互式程序的输出和输入
【发布时间】:2016-05-18 17:02:56
【问题描述】:

我正在致力于在 bash 中自动化一个交互式命令行 Java 程序,以验证该程序是否为输入生成正确的输出(基本上是 bash 中的穷人单元测试)。

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

Enter your name: John Doe
John

用户输入“John Doe”的位置。

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

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

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

这两者的问题在于 $OUTPUT 现在包含以下内容:

Enter your name: John

因为发送到标准输入的文本(及其伴随的换行符)没有像我们在控制台中看到的那样在程序的输出中重现。

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

Enter your name: John Doe
John

但我可以忍受这个:

Enter your name: 
John

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

在 bash 中是否有一种方法(不改变底层的 java 程序)来获取正在通过管道传输到标准输入的文本,以便在 java 程序读取它的“时间”时也通过管道传输到标准输出,所以完整的交互式会话是抓到了吗?

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

【问题讨论】:

  • 那么当你说echo "John Doe" | java NameReader(没有反引号)时,你在控制台上看到了什么?
  • @n.m.,我看到了同样的结果:Enter your name: John(都在一行上)
  • 您希望您的脚本向您展示其他内容,但具体基于什么?它应该如何知道在哪里插入换行符?
  • 我希望输出与我以交互方式使用程序时所看到的完全相同,其中包括我在控制台上输入的内容(当我点击“输入”时,其中包括一个换行符)。
  • 交互式?好的。您以交互方式启动程序。你怎么知道它问了你一个问题,是时候开始输入你的答案了?您希望脚本复制该逻辑,对吗?现在回答同样的问题,但假设程序每分钟打印一个字符。

标签: linux bash shell


【解决方案1】:

您可以使用script 命令

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

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

【讨论】:

    【解决方案2】:

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

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

    然后你可以构建一个 bash 脚本来解析表单的输入

    Enter your name: John
    John Doe
    

    把它变成

    Enter your name: John Doe
    John
    

    这是一个可能的 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 :)
    

    像这样使用它:

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

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

    如果您有任何问题,请不要犹豫。

    【讨论】:

      【解决方案3】:

      这就是我最终要做的。这不是一个完整的解决方案,但它适用于这种情况,并且可能对以后面临类似问题的其他人有用:

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

      这会导致脚本并行执行两件事:

      1. 等待 3 秒,然后将文本“John Doe”写入管道和文件 out.txt
      2. 运行java程序。

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

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

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

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

      1. 只要 java 程序正在运行并在 3 秒睡眠时间内等待用户输入,它就可以工作。如果不是,那么输出将是乱码。虽然(在这种情况下)期望程序在 3 秒后等待输入是合理的,但这当然不能保证。
      2. 3 秒的延迟使进程比必要的慢。如果 java 程序仅在 1 秒后就可以输入,那么我们将花费 2 秒的额外时间什么都不做。如果要运行多次,可能会累加。
      3. 它只真正适用于采用单一输入的交互式程序。可以使用具有不同睡眠长度的多个“睡眠/回声/三通”结构来响应多行,但这似乎会很快变得既复杂又缓慢。

      ...所以这是我目前最好的。当然,我们仍会邀请更好的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-21
        • 1970-01-01
        • 2013-05-06
        • 2020-11-14
        • 2019-08-27
        • 1970-01-01
        相关资源
        最近更新 更多