简体   繁体   English

在Perl中,如何通过菱形运算符对命令行中指定的所有文件进行两次传递?

[英]In Perl, how can I make two passes over all the files specified on the command line via the diamond operator?

If i have a text file and i want to run two types of operations, but each operation must read each line of the text separately from the other. 如果我有一个文本文件,我想运行两种类型的操作,但每个操作必须分别读取另一个文本的每一行。 The only way i know how to do it is 我知道如何做的唯一方法是

open out,(">>out.txt");
while (<>){
    #operation one
}
while (<>){
    #operation two
}
close out;

but this will run only on the first while , in which the operation runs fine, but the second one will not be complete because the second while(<>) does not actually re-read the file but tries to continue from where the first while left. 但是这将在第一只运行while ,在操作运行正常,但第二个将是不完整的,因为第二while(<>)实际上并不重新读取该文件,但会尝试从其中第一个,同时继续剩下。 Which is at the end of the file. 这是在文件的末尾。 So is there another way? 那还有另一种方式吗? Or is there a way to tell the second while to start again at the beginning? 或者有没有办法告诉第二个在开始时再次开始?

Given you mention in a comment: 鉴于你在评论中提到:

 perl example.pl text.txt 

The answer is - don't use <> and instead open a filehandle. 答案是 - 不要使用<>而是打开文件句柄。

my ( $filename ) = @ARVG;

open ( my $input, "<", $filename ) or die $!;

while ( <$input> ) { 
    print; 
}

seek ( $input, 0, 0 ); 

while (  <$input> ) { 
    #something else
}

Alternatively, you can - assuming test.txt isn't particularly large - just read the whole thing into an array. 或者,您可以 - 假设test.txt不是特别大 - 只需将整个内容读入数组即可。

my @input_lines = <$input>;
foreach ( @input_lines ) { 
   #something
}

If you want to specify multiple files on the command line, you can wrap the whole thing in a foreach loop: 如果要在命令行上指定多个文件,可以将整个事件包装在foreach循环中:

foreach my $filename ( @ARVG ) { 
    ## open; while; seek; while etc. 
}

If the data fits into memory: 如果数据适合内存:

my @lines = <>;

for ( @lines ){
  # operation one
}

for ( @lines ){
  # operation two
}

Couldn't you simply use the following? 难道你不能简单地使用以下?

while (<>) {
   operation1($_);
   operation2($_);
}

If not, then I'm assuming you need to process the content of all the files using one operation before it's process by the other. 如果没有,那么我假设您需要在另一个操作处理之前使用一个操作处理所有文件的内容。

<> reads from the files listed in @ARGV , removing them as it opens them, so the simplest solution is to backup @ARGV and repopulate it. <>@ARGV列出的文件中读取,在打开它们时将其删除,因此最简单的解决方案是备份@ARGV并重新填充它。

my @argv = @ARGV;
while (<>) { operation1($_); }
@ARGV = @argv;
while (<>) { operation2($_); }

Of course, it will fail if <> reads from something other than a plain file or a symlink to a plain file. 当然,如果<>从普通文件或符号链接以外的内容读取到普通文件,它将失败。 (Same goes for any solution using seek .) The only to make that work would be to load the entire file into temporary storage (eg memory or a temporary file). (使用seek任何解决方案都是如此。)唯一能够完成这项工作的是将整个文件加载到临时存储器(例如内存或临时文件)中。 The following is the simplest example of that: 以下是最简单的例子:

my @lines = <>;
for (@lines) { operation1($_); }
for (@lines) { operation2($_); }

You can localize @ARGV before the first pass. 您可以在第一次传递之前本地化@ARGV

#!/usr/bin/env perl

use strict;
use warnings;


{
    local @ARGV = @ARGV;
    while (<>){
        print "Pass 1: $_";
    }
}

while (<>){
    print "Pass 2: $_";
}

If no file handle is used with the diamond operator, Perl will examine the @ARGV special variable. 如果没有文件句柄与菱形运算符一起使用,Perl将检查@ARGV特殊变量。 If @ARGV has no elements, then the diamond operator will read from STDIN . 如果@ARGV没有元素,那么菱形运算符将从STDIN读取。

This is other way of achieve your requirements: 这是实现您的要求的另一种方式:

my @stdin=<>;

foreach my $item( @stdin ) {
   # ...
}

foreach my $item( @stdin ) {
   # ...
}

If you need to run the operation line by line, why not try something like this 如果你需要逐行运行操作,为什么不尝试这样的事情

sub operation_1 {
   my $line = shift;
   #processing for operation 1
}

sub operation_2 {
   my $line = shift;
   #processing for operation 2
} 

while(<>) {
   my $line = $_;
   chomp($line);
   operation_1($line);
   operation_2($line); 
}

If you were reading from an actual file, you could use 如果您正在阅读实际文件,则可以使用

seek FILEHANDLE,0,0;

However, you are using stdin and I don't think that it's possible to rewind stdin and start over. 但是,你正在使用stdin ,我认为不可能倒退stdin并重新开始。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM