1

所以我正在上 Perl 编程课,我们的老师给了我们第一个作业,几乎没有谈论如何实际编程 perl。这正是我们老师布置的:

“你应该编写一个脚本(你可以为你认为合适的脚本命名),它接受 3 个文件名作为参数。第一个文件名对应于用 C++ 编写的程序源代码。第二个文件名对应于要使用的输入文件由 C++ 程序列为第一个文件名。第三个文件名对应于一个文本文件,其中包含所讨论程序的预期正确输出。目录路径可以与任何文件名一起提供。

如果程序不需要输入文件,命令行的第二个参数应该是文件名“/dev/null”。

所有文件——程序源文件、输入文件和预期的输出文件——在脚本使用它们之前都应该复制到临时测试目录,这样原始文件被测试修改的可能性很小程序。如果暂存目录不存在,您的脚本应该创建一个作为当前工作目录的子目录。

然后,您的脚本应该使用 GNU g++ 编译器编译和链接源代码的临时副本。然后脚本应该运行程序——将输出保存到临时文件中存储在临时测试目录中。

运行程序后,您的脚本应使用 UNIX 命令 diff 将上一步中生成的实际输出与预期的输出文件进行比较,并报告输出符合规范或报告 diff 程序报告的任何差异。

完成后,您的脚本应删除所有临时副本和暂存文件。不要删除原始程序、原始输入文件、原始预期输出文件或暂存目录本身。”

到目前为止我有这个:

#!/usr/bin/perl -w

use strict;

my ($line, $program, $input, $output);

print "Give the program, input, and standart output for testing. ";

$line = <>;
chomp $line;

($program, $input, $output) = split/\s+/, $line; # split/\s+/ is to separate spaces from the input

my($o_test) = $output + "_test";

print "$program ";
print "$input ";
print "$output ";

system("mkdir test_scratch") == 0
or die "failed to create test_scratch. exiting...."

system("cp $program, /test_scratch/"); # error
system("cp $input, /test_scratch/");
system("cp $output, /test_scratch/");

system("cd test_scratch");

system("g++ $program");
system("chmod +x a.out");

system("./a.out < $input > $o_test");

my($DIFF) = system("diff $output $o_test") # error

if[ $DIFF != ""]
    print ("Output conforms to specifications."); # error
then
    print ("$DIFF");
system("cd ..");

我在代码中的 # 处遇到错误。我什至不知道如何执行“/dev/null”。我花了很多时间在网上查找东西并搜索stackoverflow,但我只是不知道还能做什么。我意识到这是一个非常长的问题,但我不知道还能做什么。谢谢你能给我的任何帮助。

4

2 回答 2

1

这里有几个模块可以为您提供帮助。我推荐的第一个是ExtUtils::CBuilder,它可以为您管理构建过程。然后,您还可以使用File::Copy将内容移动到临时文件夹中,甚至使用File::chdir来管理工作目录。由于 prof 指定您应该使用diff也许应该使用,但是有些模块可以执行该任务,或者您可以使用Test::More来检查输出是否符合预期。


仅供将来参考,这就是我完成类似任务的方式(不要保留临时目录,不需要diff):

#!/usr/bin/env perl

use strict;
use warnings;

use File::chdir;
use File::Temp;
use File::Copy;
use File::Basename;
use File::Slurp;

use ExtUtils::CBuilder;
use Test::More tests => 1;

die "Not enough inputs\n" unless @ARGV >= 3;

# create a temporary directory
my $temp = File::Temp->newdir;

# copy all arguments to that temporary directory
copy $_, $temp for @ARGV;

# store only the filename (not path) of each argument
my ($cpp_file, $in_file, $expected_file) = map { scalar basename $_ } @ARGV;

# change working directory to temporary one (via File::chdir)
local $CWD = $temp;

# build the executable
my $builder = ExtUtils::CBuilder->new(config => {cc => 'g++'});

my $obj = $builder->compile( source => $cpp_file, 'C++' => 1 );
my $exe = $builder->link_executable( objects => 'hello.o' );

# run the executable
my $output = `./$exe $in_file`;

# read in the expected file
my $expected = read_file $expected_file; 

# test the resulting output
is $output, $expected, 'Got expected output from executable';

请注意,您可能需要小心输出和预期文件中的换行符。

于 2012-10-06T22:13:30.937 回答
0

我在代码中的 # 处遇到错误。

解决此错误的简单方法是删除逗号。标准的 Linux cp 命令在源和目标之间不使用逗号。您也可以考虑使用File::Copy在 Perl 中复制文件。

我什至不知道如何执行“/dev/null”。

/dev/null 是一个标准的 Linux 文件(*),它指向空。您可以将字节写入此文件,它们会消失。您可以尝试从此文件中读取字节,但那里什么也没有。

这使您有两个选择:

  1. 直接使用 /dev/null ,程序将无法从空字节流中读取任何内容。
  2. 检查输入文件是否等于/dev/null,如果输入为/dev/null,则不要给C++程序输入值。

任何一种方法都行得通。如果您将 /dev/null 复制到一个目录,您将获得一个名为“null”的新的零长度文件,它对于 C++ 程序来说是一个完全有效的空输入文件。

(*) 不,如果您来自 Windows 世界,/dev/null 不是您习惯的文件。它在磁盘上不存在 - 从来没有也永远不会。但是经典的 Unix 哲学是让每个数据源成为一个文件,即使该文件在磁盘上不存在。例如,查看 Linux /proc 文件系统,它允许您在目录树结构中查看有关 CPU、进程等的信息。在套接字 API 采用完全不同的路线之前,这种理念在一定程度上得到了支持。如果您正在寻找一个基本上将所有内容(包括网络连接和屏幕)写入文件的操作系统,请查看计划 9。不过,我不建议您在计划 9 上完成家庭作业并期望它们在 Linux 上工作。


注意不要忘记检查来自 的返回码system,因为如果 g++ 无法编译 C++ 程序,将无法运行不存在的已编译程序。

于 2012-10-06T21:56:41.480 回答