使用Perl编程从STDIN或输入文件中读取数据

84

在Perl中,以编程方式从标准输入或输入文件(如果提供)读取最流畅的方法是什么?

8个回答

101
while (<>) {
print;
}

如果没有指定文件,则从标准输入读取,否则将从命令行指定的文件中读取。

如果您需要在命令行中使用此循环结构,则可以使用-n选项:

$ perl -ne 'print;'

在这里,你只需将第一个示例中的代码放在第二个示例中的''之间即可。


24
+1 +挑刺: "将依次从命令行指定的一个或多个文件中读取" - msw
5
你只需要写@ARGV ="/path/to/some/file.ext";,它就可以读取文件——因此你甚至可以在特定条件下编程默认文件。 - Axeman
3
如果您的脚本非常短,您可以使用Perl的-n或-p选项,并在命令行上指定处理方式: perl -n -e '$_ = uc($_); print;' yourfile。使用-p选项而不是-n,Perl会自动在末尾打印$_。 - mivk
3
当然,你可以一次性“吸取”所有内容:my @slurp = <>; foreach my $line (@slurp) { ... } - David Tonhofer
你为什么不给读取的行命名,例如 while (my $line = <>) {... - David Mertens

52

这提供了一个可操作的命名变量:

foreach my $line ( <STDIN> ) {
    chomp( $line );
    print "$line\n";
}

要读取一个文件,可以像这样将其输入到管道中:

program.pl < inputfile

11
避免使用常见的缩略不可读的 Perl 代码,这很好,给你点赞(+1)。 - MikeKulls
7
因为 foreach 会读取整个文件,所以最好在 while 循环中给行进行赋值。此外,Perl 对裸角括号有内置的神奇行为,所以你应该使用 while(my $line = <>)。这样就不需要重定向了。 - David Mertens
3
我同意@MikeKulls的观点。如果Perl脚本难以阅读,那并不是Perl语言的错,而是程序员的责任!第一行应该写成foreach my $line ( <STDIN> ) { - tiktak
3
重新阅读问题,这个回答是不正确的,因为它只从标准输入(stdin)中读取,而没有读取在命令行上指定的文件。ennuikiller的答案是正确的,尽管我会写成 while(my $line = <>) { print $line; } - MikeKulls
3
@MikeKulls 这句话应该是 while (my $line = <>, defined $line) { ... } 或者 while (<>) { my $line = $_; },以避免在空白行处停止。请注意,这两种写法不会改变原来的意思。 - Greg Nisbet
显示剩余2条评论

18
你需要使用<>运算符:
while (<>) {
    print $_; # or simply "print;"
}

这可以被压缩为:

print while (<>);

任意文件:

open my $F, "<file.txt" or die $!;
while (<$F>) {
    print $_;
}
close $F;

我试图在Windows上运行这个脚本,但没有任何动作或错误,有什么想法吗? - Higinio Fuentes
我在尝试在Windows上运行这个脚本,但没有任何动静或错误,有什么想法吗? - undefined

17
在某些情况下,最聪明的方法是利用-n开关。它会隐式地将你的代码包装在一个while(<>)循环中,并灵活处理输入。
在文件中:
 #!/usr/bin/perl -n
BEGIN: { # 在此执行一次操作 }
# 实现单行输入逻辑 print $result;
在命令行中:
chmod +x slickestWay.pl
现在,根据您的输入执行以下操作之一:
  1. 等待用户输入

./slickestWay.pl
  • 从参数中命名的文件中读取(无需重定向)

  • ./slickestWay.pl input.txt
    ./slickestWay.pl input.txt moreInput.txt
    
  • 使用管道

    someOtherScript | ./slickestWay.pl 
    
  • 如果您需要初始化某种面向对象接口(例如Text::CSV或类似的接口),则BEGIN块是必要的,您可以使用-M将其添加到shebang中。

    -l-p也是您的好朋友。


    10
    如果有理由无法使用ennuikiller提供的简单解决方案,那么您将不得不使用Typeglobs来操纵文件句柄。这样做需要更多的工作。该示例从$ARGV[0]中的文件复制到$ARGV[1]中的文件。如果未指定文件,则默认为STDINSTDOUT
    use English;
    
    my $in;
    my $out;
    
    if ($#ARGV >= 0){
        unless (open($in,  "<", $ARGV[0])){
          die "could not open $ARGV[0] for reading.";
        }
    }
    else {
        $in  = *STDIN;
    }
    
    if ($#ARGV >= 1){
        unless (open($out, ">", $ARGV[1])){
          die "could not open $ARGV[1] for writing.";
        }
    }
    else {
        $out  = *STDOUT;
    }
    
    while ($_ = <$in>){
        $out->print($_);
    }
    

    1
    如果命令行没有提供要读取的文件名,而是在其他地方(变量、配置文件等)中读取,则可以使用以下方法:将 $ARGV[0] 替换为其他变量。这种方法可以在其他答案失败时使用。 - Matija Nalis
    或者,为了读取文件,只需将文件名unshift@ARGV中并使用钻石操作符<> - David Mertens

    6

    Do

    $userinput =  <STDIN>; #read stdin and put it in $userinput
    chomp ($userinput);    #cut the return / line feed character
    

    如果您只想读取一行


    仅从 STDIN 读取,不从指定文件读取。钻石操作符 正是 OP 所寻找的。 - David Mertens

    -1
    这是我编写的一个脚本,它可以接受命令行输入或重定向文本文件。
    if ($#ARGV < 1) {
        @ARGV = ();
        @ARGV = <>;
        chomp(@ARGV);
    }
    


    这将重新分配文件的内容到@ARGV,然后您只需像包含命令行选项一样处理@ARGV。

    警告

    如果没有重定向文件,则程序将因为等待STDIN输入而闲置。

    我还没有找到一种方法来检测是否正在重定向文件以消除STDIN问题。


    这是一种很酷的方法,但不是OP所要求的。这使得可以将单个文件名作为参数传递,其中的内容用作命令行参数。OP正在寻找不同的东西。另外,为什么要使用神秘的$#ARGV < 1而不是(我认为更清晰的)@ARGV == 1 - David Mertens

    -2
    if(my $file = shift) { # if file is specified, read from that
      open(my $fh, '<', $file) or die($!);
      while(my $line = <$fh>) {
        print $line;
      }
    }
    else { # otherwise, read from STDIN
      print while(<>);
    }
    

    8
    <> 运算符会自动查找并从命令行指定的任何文件中读取内容,无需使用 if - Dave Sherohman
    您也没有描述 shift 在这里的作用。 - Eugen Konkov

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接