【问题标题】:How can I access a file in bash and compare its contents with stdout when running a program to make sure they are identical?如何在运行程序时访问 bash 中的文件并将其内容与 stdout 进行比较以确保它们相同?
【发布时间】:2017-10-15 01:28:41
【问题描述】:

如何将程序标准输出中的输出与输出文件中的模型输出进行比较?我问是因为我正在尝试制作评分脚本。另外,我正在考虑使用 -q grep,但我仍然不确定如何使用它。

请简单回答,因为我是 bash 的菜鸟。

重要修改:

我想在 if 语句中使用它。例如:

if(modle_file.txt is identical to stdout when running program); then
    echo "Great!"
else
    echo "Wrong output. You loose 1 point."

编辑:

程序接受一个输入。例如,如果我们这样做:

%Python3 Program.py
Enter a number: 5
The first 5 (arbitrary) things are:
2, 5, etc  (program output)

%

【问题讨论】:

    标签: bash shell file grep piping


    【解决方案1】:

    如果您的文件名为example.txt,请执行

    diff example.txt <(program with all its options)
    

    &lt;() 语法将程序的输出放在括号中,并将其传递给diff 命令,就像它是一个文本文件一样。

    编辑:

    如果您只是想在if-clause 中检查文本文件和程序的输出是否相同,您可以这样做:

    if [ "$(diff example.txt <(program with all its options))" == "" ]; then
      echo 'the outputs are identical'
    else
      echo 'the outputs differ'
    fi
    

    diff 仅在文件不同时才生成输出,因此空字符串作为答案意味着文件相同。

    编辑 2

    原则上您可以将标准输入重定向到文件,如下所示:

    program < input.txt
    

    现在,无需进一步测试,我不知道这是否适用于您的 python 脚本,但假设您可以将程序期望的所有输入放入这样的文件中,您可以这样做

    if [ "$(diff example.txt <(program < input.txt))" == "" ]; then
      echo 'Great!'
    else
      echo 'Wrong output. You loose 1 point.'
    fi
    

    编辑 3:

    我用python写了一个简单的测试程序(我们称之为program.py):

    x = input('type a number: ')
    print(x)
    y = input('type another number: ')
    print(y)
    

    如果您在 shell 中使用python program.py 以交互方式运行它,并给出57 作为答案,您会得到以下输出:

    type a number: 5
    5
    type another number: 7
    7
    

    如果您创建一个文件,例如 input.txt,其中包含所有所需的输入,

    5
    7
    

    并像这样将其通过管道传输到您的文件中:

    python program.py < input.txt
    

    你会得到以下输出:

    type a number: 5
    type another number: 7
    

    差异的原因是 python(和许多其他 shell 程序)根据输入是来自交互式 shell、管道还是重定向的标准输入而对输入进行不同的处理。在这种情况下,输入不会回显,因为输入来自input.txt。但是,如果您使用 input.txt 同时运行您的代码和学生的代码,则这两个输出应该仍然具有可比性。

    编辑 4:

    正如下面的 cmets 所述,如果您只想知道它们是否不同,则无需将 diff 命令的整个输出与空字符串 ("") 进行比较,返回状态就足够了。最好在bash写一个小测试脚本(姑且称之为code_checker.sh),

    if diff example.txt <(python program.py < input.txt) > /dev/null; then 
        echo "Great!"
    else
        echo "Wrong output. You loose 1 point."
    fi
    

    if 子句中的&gt;/dev/null 部分将diff 的输出重定向到一个特殊设备,有效地忽略它。如果你有很多输出,最好使用cmp,就像 user1934428 提到的那样。

    【讨论】:

    • 我怎样才能在 if 语句中使用它?例如:if(diff example.txt
    • 你能举个例子说明你想要达到的目标吗?
    • 如果程序接受输入呢?我在(你的程序放在这里)部分放了什么?
    • 你的意思是文件作为输入吗?你能写下在没有diffif 语句的情况下键入的命令吗?
    • 我会写:Python3 Program.py #(然后程序会要求输入)输入一个数字:5 前5个东西是:等等......(程序输出)
    【解决方案2】:

    执行此操作的一种方法是将stdout 输出重定向到文件,如下所示:

    myCommand > /folder/file.txt
    

    然后运行diff命令比较两个文件

    diff /folder/file.txt /folder/model_output.txt
    

    编辑:要在if 语句中使用它,您可以执行以下操作:

    if [ -z "$(diff /folder/file.txt /folder/model_output.txt 2>&1)" ]; then echo "Great!"; else echo "Wrong output. You loose 1 point."; fi
    

    如果文件相等,则打印Great!,否则打印Wrong output. You loose 1 point.

    【讨论】:

    • 如果/folder/file.txt' already exists before you run myCommand, the output of myCommand` 将附加到/folder/file.txt'. This may or may not be the intended behaviour. I would first make sure that the file really does not exist, and then use only one >, i.e. myCommand > /folder/file.txt。在不创建单独文件的情况下查看我的答案。
    • 是的,我在发布后也有同样的想法(使用&gt; 而不是&gt;&gt;),并在您发表评论之前进行了更新。无论如何,谢谢。
    • 一般来说,您不应该使用... &amp;&amp; ... || ... 作为if 语句的快捷方式。如果第一个命令成功但下一个命令失败,则第三个命令仍然执行。
    • @chepner 感谢您的洞察力,我没有想到这一点。我稍后用正确的if 声明更新了我的帖子。
    【解决方案3】:

    由于您对实际差异不感兴趣,而只对它们是否相同感兴趣,我认为cmp 是最好的选择(如果文件很大,速度会更快):

    if cmp -s example.txt <(your program goes here)
    then
      echo identical
    fi
    

    请注意,这仅比较标准输出(根据您的要求),而不是标准错误。

    【讨论】:

    • @muru:我忘了diff 有一个-q 选项。当然,diff -q 的性能应该与cmp -s 大致相同。但是,如果您省略 -q,并且文件不同,则 diff 必须 遍历文件直到最后,而 diff -qcmp -s 将停止在它们的第一个差异处找。当然,这只有在相当大的文件中才会很明显,并且只有在第一个差异不在文件末尾附近时才会注意到。
    • 如果程序接受输入呢?我在(你的程序放在这里)部分放了什么?
    • 你的意思是,从标准输入输入?就像您直接调用程序一样:输入重定向。括号之间的所有内容都是一个完整的命令,在子 shell 中执行。你可以放你喜欢的。
    • @user1934428 diff 可能有一个-q选项; POSIX 不需要它。
    最近更新 更多