我有这个使用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)行为再次如预期那样。
答案 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;
。因此-MCPAN
为use 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