【问题标题】:STDOUT issue with perlperl 的 STDOUT 问题
【发布时间】:2014-12-15 16:19:26
【问题描述】:

为了在 Windows 上使用 XMLLINT 递归处理一些 XML 文件,我编写了一小段代码

#!C:\Perl64\bin\perl
$ENV{PATH} = 'C:\Users\NLP-Lingua\Desktop\echos';
my $racine = $ENV{PATH};
&recurse($racine);

sub recurse {
    my $dossier = shift(@_);
    opendir(DOSSIER, $dossier);
    my @files = readdir (DOSSIER);
    #print "liste des fichier de $dossier : @files\n"; 
    foreach my $file (@files) {
        next if $file =~ /^\.\.?$/;
        my $file = $dossier."/".$file;
        if (-f $file) {
            #print $file, "\n";
            if (($file =~ /\.xml$/)) {
system("C://toolslibxml//xmllint.exe --dropdtd --noent $file > $file\_out");
open (OUT, ">$file\_translated.xml");
                }
                close(IN);
            }
        }
        if (-d $file) {
            &recurse($file);
        }
    }

close IN;
close OUT; 

此代码在 Ubuntu 上完美运行,但我在重定向输出时遇到了问题

C://toolslibxml//xmllint.exe --dropdtd --noent $file 到一个文件中,如果我使用运算符 ">" 所有输出都打印到屏幕上,但如果我把它放回去,文件是空的,我也尝试过使用反引号和变量的变体结果是一样的

在提示命令下命令 C://toolslibxml//xmllint.exe --dropdtd --noent file > out_putfile 工作完美,所以很奇怪

提前致谢

【问题讨论】:

  • 我对windows真的不是很坚定,但是你写的代码看起来像是直接从90年代开始的:使用strictwarnings,三个参数open&子程序调用不需要,您应该在打开时检查错误(use autodie 或成语open … or die $!。您是否考虑过使用ICP::Run?这可能会解决您的重定向问题。另外,您应该使用@987654332 @(这是一个核心模块)来连接路径(因为你在那里也有问题)。
  • 你到底为什么要换$ENV{PATH}
  • close(IN)(两次),但你从不打开它。 $file\_out 也是一个 hack,我很惊讶它可以工作。正确的方法是${file}_out。或者更好的是,my $file_out = $file . "_out";,然后插入该变量。然后你打开一个OUT 文件,但你从来没有向它打印任何东西。当你说这“完美”时,我倾向于不相信你。

标签: xml windows perl stdout


【解决方案1】:

TL;DR:我对解决方案的猜测跳到最后。

我会检查你的脚本并评论你在做什么。有很多有问题的代码。

#!C:\Perl64\bin\perl

在 Windows 中会忽略 shebang,但会解析所有开关。你应该使用use strict; use warnings;——如果你不使用它们,问题不会消失,它们只是被隐藏了。

$ENV{PATH} = 'C:\Users\NLP-Lingua\Desktop\echos';
my $racine = $ENV{PATH};
&recurse($racine);

据我所知,这里根本没有理由让$ENV{PATH} 参与进来。这仅控制运行 Perl 程序的 shell 可以访问的程序。通过将其设置到此文件夹,您基本上限制了您的临时 shell 访问 Windows PATH 中的程序,我怀疑这就是您想要做的。

另外,你不应该使用& 给子程序加上前缀,这是旧式的,它被用来覆盖原型。如果您不知道这意味着什么,请查看perldoc perlsub,或者按照我的建议进行操作。

整个代码可以简单地写出来:

recurse('C:\Users\NLP-Lingua\Desktop\echos');

sub recurse {
    my $dossier = shift(@_);
    opendir(DOSSIER, $dossier);
    my @files = readdir (DOSSIER);
    #print "liste des fichier de $dossier : @files\n"; 
    foreach my $file (@files) {
        next if $file =~ /^\.\.?$/;
        my $file = $dossier."/".$file;
        if (-f $file) {
            #print $file, "\n";
            if (($file =~ /\.xml$/)) {
system("C://toolslibxml//xmllint.exe --dropdtd --noent $file > $file\_out");

正如我在 cmets 中所说,$file\_out 是一种防止 $file_out 被内插为变量的奇怪方法。您应该使用${file}_out,或者更好的是,创建一个新变量:my $file_out = $file . "_out";

open (OUT, ">$file\_translated.xml");

您从不使用此文件句柄,因此您将用空文件填充目录。而且每次运行此脚本时,您可能会创建更多的空文件。

                close(IN);

你从未打开过这个文件句柄。

至于你的程序为什么不工作,我猜是这一行:

    my $file = $dossier."/".$file;

当您对此进行插值时,您会得到类似foo/bar.xml 的内容,并且/ 被Windows cmd shell 使用。使用反斜杠,或者更好的是,使用File::Spec::catfile

use File::Spec;
my $file = File::Spec->catfile($dossier, $file);

这将为运行程序的系统创建一个带有合适目录分隔符的路径。

【讨论】:

  • 我还要补充一点 - 滚动你自己的遍历例程会带来痛苦 - File::Find 是内置的并且易于使用,并且没有很多陷阱。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多