Perl:Weird Tie :: Windows中的文件行为,而不是Unix

时间:2011-11-13 12:08:42

标签: windows linux perl bash powershell

我有这个使用Tie::File的perl脚本。
在Linux(Ubuntu)中,当我通过Bash调用脚本时,它按预期工作,但在Windows中,当我通过Powershell调用脚本时,它表现得很奇怪(请参阅下面的P.S.)。


#!/usr/bin/perl -T

use strict;
use warnings;

use Tie::File;
use CommonStringTasks;

if ( @ARGV != 4 ) {
   print "ERROR:Inadequate/Redundant arguments.\n";
   print "Usage: perl <pl_executable> <path/to/peer_main.java> <peer_main.java>\n";
   print "       <score_file_index> <port_step_index>\n";
   print $ARGV[0], "\n";
   print $ARGV[1], "\n";
   print $ARGV[2], "\n";
   print $ARGV[3], "\n";
   exit 1;
}

my $PEER_DIR = $ARGV[0];
my $PEER_FILE = $ARGV[1];
my $PEER_PACKAGE = "src/planetlab/app";
my $PEER_PATH = "${PEER_DIR}/${PEER_PACKAGE}/${PEER_FILE}";

# Check if args are tainted ...

# Check $PEER_PATH file permissions ...

open(my $file, "+<", "$PEER_PATH")
   or
die("File ", $PEER_FILE, " could not be opened for editing:$!");

# Edit the file and change variables for debugging/deployment setup.
# Number demanglers:
# -flock -> arg2 -> 2 stands for FILE_EX
# Options (critical!):
# -Memory: Inhibit caching as this will allow record changes on the fly.
tie my @fileLines, 
    'Tie::File', 
    $file,
    memory => 0
      or 
    die("File ", $PEER_FILE, " could not be tied with Tie::File:$!");

flock $file, 2;

my $i = 0;
my $scoreLine = "int FILE_INDEX = " . $SCORE . ";";
my $portLine = "int SERVER_PORT = " . $PORT . ";";
my $originalScoreLine = "int FILE_INDEX =";
my $originalPortLine = "int SERVER_PORT =";

(tied @fileLines)->defer;

while (my $line = <$file>) {
   if ( ($line =~ m/($scoreLine)/) && ($SCORE+1 > 0) ) {
      print "Original line (score): ", "\n", $scoreLine, "\n";
      chomp $line;
      $line = substr($line, 0, -($scoreDigits+1));
      $line = $line . (++$SCORE) . ";";
      print "Editing line (score): ", $i, "\n",  trimLeadSpaces($fileLines[$i]), "\n";
      $fileLines[$i] = $line;
      print "Line replaced with:\n", trimLeadSpaces($line), "\n";
      next;
   }
   if ( ($line =~ m/($portLine)/) && ($PORT > 0) ) {
      print "Original line (port): ", "\n", $portLine, "\n";
      chomp $line;
      $line = substr($line, 0, -($portDigits+1));
      $line = $line . (++$PORT) . ";";
      print "Editing line (port): ", $i, "\n",  trimLeadSpaces($fileLines[$i]), "\n";
      $fileLines[$i] = $line;
      print "Line replaced with:\n", trimLeadSpaces($line), "\n";
      last;
   }

   # Restore original settings.
   if ( ($line =~ m/($originalScoreLine)/) && ($SCORE < 0) ) {
      print "Restoring line (score) - FROM: ", "\n", $fileLines[$i], "\n";
      $fileLines[$i] = "    private static final int FILE_INDEX = 0;";
      print "Restoring line (score) - TO: ", "\n", $fileLines[$i], "\n";
      next;
   }
   if ( ($line =~ m/($originalPortLine)/) && ($PORT < 0) ) {
      print "Restoring line (port) - FROM: ", "\n", $fileLines[$i], "\n";
      $PORT = abs($PORT);
      $fileLines[$i] = "    private static final int SERVER_PORT = " . $PORT . ";";
      print "Restoring line (port) - TO: ", "\n", $fileLines[$i], "\n";
      last;
   }
} continue {
   $i++;
}

(tied @fileLines)->flush;

untie @fileLines;
close $file;


两个操作系统中的perl版本为5+(在带有CPAN模块的Windows Active-State Perl中) 这可能是我打开文件句柄的方式吗?任何人的想法?

PS:第一个版本有一个while (<$file>)而不是$line我使用了$_变量,但是当我这样做时,我有一种行为,其中不会编辑特定的行,而是该文件将附加一百个换行符,然后是(正确)编辑的行,依此类推。我还警告$fileLines[$i]未被初始化!显然Windows中的Tie::File结构或我不知道的其他内容有问题。同样的不稳定行为随着更改而发生,并且Linux(Ubuntu)行为再次如预期那样。

2 个答案:

答案 0 :(得分:2)

OP的问题含糊不清,缺乏投入和预期的产出。因此,我只会注意到我的一些担忧:

首先,在同一个句柄上使用Tie::File<$file>以及flock似乎既过分又危险。我建议只使用Tie::File进行迭代和编辑,例如:

#!/usr/bin/env perl

use strict;
use warnings;

use Tie::File;

tie my @lines, 'Tie::File', 'filename';

foreach my $linenum ( 0..$#lines ) {
  if ($lines[$linenum] =~ /something/) {
    $lines[$linenum] = 'somethingelse';
  }
}

或许比编辑内联更好,因为Tie::File允许,将文件复制到备份,使用<$file>遍历行,然后使用旧名称写入新文件。

#!/usr/bin/env perl

use strict;
use warnings;

use File::Copy 'move';

my $infile = $ARGV[0];

move( $infile, "$infile.bak");

open my $inhandle, '<', "$infile.bak";
open my $outhandle, '>', $infile;

while( my $line = <$inhandle> ) {
  if ($line =~ /something/) {
    $line = 'somethingelse';
  }
  print $outhandle $line; 
}

其次,-MModule标志只是转换为脚本顶部的use Module;。因此-MCPANuse CPAN;,但加载CPAN模块不会对脚本执行任何操作。 CPAN.pm为脚本提供了安装模块的能力。

第三,如果您提供示例输入,预期输出和精简脚本,我们将能够提供更好的帮助,这些脚本清楚地显示了此操作如何执行,同时仍然以与实际脚本相同的方式失败。

答案 1 :(得分:0)

我找到了问题的根源。
原因是记录分隔符!
Tie::File在Windows中预期/r/n记录分隔符,因此它只需一次传递即可读取整个文件。我的文件在 UTF-8 中,带有 Unix行结尾
这就是为什么当我遍历$fileLines并访问超过0的任何索引时,我从perl得到一个警告,表明该字符串未初始化。解决了这个问题,现在我准备继续了! :d

P.S。:Joel Berger先生我将你的答案标记为有效/适当,因为你真的试过帮助我,我按照你对文件句柄的第一个建议:)。 谢谢大家帮助我xD xD xD