佩尔 $! returns "Bad file descriptor" 一次 "No such file or directory" 当 运行 在调试模式下(不是 Perl 的调试模式)

Perl $! returns "Bad file descriptor" at once and "No such file or directory" when run in debug mode (not Perl's debug mode)

我有这个 Perl 代码:

foreach my $eachFile (@FileList)
{
    $cmd = "xcopy /I /F /V /Y /R \"$ProjectPath\$eachFile\" \"$NewPath\\"";
    print "\n\t$cmd\n";
    my $result = system($cmd);
    die "ERROR: Could not copy from $viewPath to $DLPath: $!" if ($result > 0);
}

在这段代码中,我放置了如下行:system("pause") if ($debug) 当 $debug 变量设置时暂停执行。

现在,上面的 xcopy 死掉了,因为一个文件不存在。但是,$!打印 : "Bad file descriptor" 当 运行 正常时,它打印 : "No such file or directory" 当我设置 $debug 变量时。

知道为什么 $!为两个实例提供不同的消息?

请参阅 system 的文档。 $! 仅在 system 本身失败并且 returns -1 时才有意义。否则,您必须通过拆分 $? 来自行弄清楚错误的含义。文档中的示例:

if ($? == -1) {
    print "failed to execute: $!\n";
}
elsif ($? & 127) {
    printf "child died with signal %d, %s coredump\n",
        ($? & 127),  ($? & 128) ? 'with' : 'without';
}
else {
    printf "child exited with value %d\n", $? >> 8;
}

因此您将 $? 右移 8 位,这就是 xcopy 编辑的错误代码 return。有 xcopy 个错误代码列表 here

无论如何,这就是它应该的行为方式。在我的测试中:

my $ProjectPath = "c:\temp";
my @FileList    = qw(aaa.txt asdf bbb.txt empty_dir beans.txt foo.pl);
my $NewPath     = "c:\temp2";

foreach my $eachFile (@FileList) {
    print "Copying $eachFile\n";
    my $result = system(
        qw(xcopy /I /F /V /Y /R), "$ProjectPath\$eachFile", "$NewPath\"
    );

    if ($? == -1) {
        die "failed to execute: $!";
    }
    elsif ($? > 0) {
        my $foo = $? >> 8;
        print "Error code: $foo\n";
        die "no files found to copy"          if $foo == 1;
        die "user pressed ctrl-c during copy" if $foo == 2;
        die "initialization error"            if $foo == 4;
        die "disk write error writing"        if $foo == 5;
        die "unknown error";
    }
}

当它发现我的临时目录中没有beans.txt时报告"initialization error"。我不知道我需要做什么才能使 xcopy return 成为 1。 C'est la vie.

'\' 作为分隔符可能是问题所在,Unix 风格是'/'。

可以使用 Perl 时不要使用系统命令。否则,为什么要用 Perl 呢?

Perl 中有一个文件 copy 命令。它不是基本命令的一部分,但可以通过 File::Copy 模块获得。不要误以为这不是 Standard Perl 的一部分,因此不应使用。 File::Copy 模块已经存在很长时间了,它是您将 运行 安装的每个 Perl 安装的一部分。学习各种标准 Perl 模块,并将它们当作基本 Perl 的一部分来使用,因为它们是。

而且,不要害怕安装 CPAN Perl 模块。这些模块可以以奇妙的方式扩展 Perl(例如,解析 JSON、YAML 和 XML 文件或与万维网交互)。请记住,最好的 Perl 程序员非常懒惰,如果别人已经为他们编写代码,他们将不会编写代码。

要获得 copy 命令,我所要做的就是在我的 Perl 脚本的顶部添加 use File::Copy;

请注意,我的循环包含三行:

  • 我通过 -f 命令验证文件是否存在。
  • 我告诉你我在做什么(你的 xcopy 命令中有 /V
  • 我复制文件。

另请注意,我更改了 您的变量名称。在 Perl 中,标准是使用下划线和小写字母作为变量名。此外,现在简单地说 for 而不是 foreach 是标准,这样可以节省您输入四个字符。

我使用启用 say 命令的 use feature qw(say); pragma。我比 print 更喜欢它,因为 say 会自动将 \n 添加到行尾。这听起来像是一件小事,但是在你忘记 \n 几次之后,发现放置 \n 以你没有预料到的方式改变你的输出的情况,你会欣赏 say 命令.

我使用了另一个名为 File::Path 的模块,它为我提供了 make_path 命令。此命令是内置 mkdir 命令的扩展。但是,它使目录和所有父目录成为您想要的目录。如果你不需要担心制作整个路径,你可以简单地使用 mkdir 而不是包括 File::Path.

use strict;
use warnings;
use feature qw(say);

use File::Copy;
use File::Path qw(make_path);

...

if ( not -d $project_path ) {
    make_path $project_path
            or die qq(Can't create directory "$project_path");
    }
}

for my $file in ( @files ) {
    if not ( -f "$project_path/$file" ) {
         die qq(Can't copy non-existant file "$project_path/$file".);
    }
    say qq(Copying "$project_path/$file" to "$new_path");
    copy "$project_path/$file", $new_path
         or die qq(Can't copy "$file": $!);
}

是的,这没有回答您的直接问题。但是,我希望您了解 Perl 方式 做您想做的事,这样您就可以成为更好的 Perl 程序员。