愚蠢的open()技巧

open函数的功能比你想象的要多得多。如果你读足够的Perl,你会开始注意到一个主题,那就是每个人都期望你使用三个参数的openopen能做的还有很多。其中一些“愚蠢的open()技巧”可能很有用,但也可能很危险。这些技巧是在封闭赛道上由专业驾驶员执行的。请勿在家或工作中尝试。而且,为了专注于open,我忽略了所有的错误检查。

没有显式文件名

存在一种只有一个参数的open形式,它只接受裸词文件句柄。在这个例子中,当我不带其他参数打开文件句柄F时,Perl使用与文件名同名的包标量变量

our $F;
while( $F = shift @ARGV ) {
  open F;
  while(  ) { print }
  close F;
  }

这看起来可能有点荒谬,但就像许多这样的快捷方式一样,考虑一下Perl的单行和脚本方面。想象一下,我想遍历命令行上的大量文件,但其中一些我想跳过。我不能简单地使用-n,因为它会为我打开所有文件。我必须自己处理

perl -e 'while($F=shift){next if$F=~/\.jpg/;open F;while(<F>){print;exit if /Perl/}}' *

你可能一生中只需要一次。也许你永远不想使用它。尽管如此,它还是在那里。

创建匿名临时文件

如果我给open提供一个显式的undef文件名和读写模式(+>+<),Perl将打开一个匿名临时文件

open my $fh, '+>', undef;

Perl实际上创建了一个有名称的文件并打开了它,但立即解除了该名称。由于没有人有这个名称,因此没有人能够访问那个文件。如果我已经使用了File::Temp,我可能把临时文件留在那里,或者在我处理它的时候,其他东西可能能够看到它。

如果我的perl是用PerlIO编译的(它很可能已经这样做了),我可以在如果文件名参数是那个变量的引用的情况下,在标量变量上打开一个文件句柄。

open my $fh, '>', \ my $string;

当我想捕获用于期望文件句柄的接口的输出时,这很方便

something_that_prints( $fh );

现在$string包含函数打印的任何内容。我可以通过打印它来检查它

say "I captured:\n$string";

从字符串中读取行

我也可以通过在它上打开一个文件句柄来从标量变量中读取。

open my $fh, '<', \ $string;

现在我可以在不与正则表达式的锚点或行结束符纠缠的情况下逐行处理字符串

while( <$fh> ) { ... }

我在Effective Perl Programming中写了关于这类文件句柄在字符串上的技巧。

创建管道

大多数Unix程序员可能已经知道他们可以读取一个命令的输出作为另一个命令的输入。我可以用Perl的open做同样的事情

use v5.10;

open my $pipe, '-|', 'date';
while( <$pipe> ) {
  say "$_";
  }

这读取了date系统命令的输出并将其打印出来。但是,我可以在管道中放置不止一个命令。我必须放弃旨在防止这种胡闹的三参数形式

open my $pipe, qq(cat '$0' | sort |);
while( <$pipe> ) {
  print "$.: $_";
  }

这捕获了当前程序的文本,将每一行按字母顺序排序,并带有编号的输出打印。我可能因为那个对程序行进行排序的程序而获得一个无用使用cat奖,但这仍然是一个特性。

即时gzip

从Perl直接压缩数据中,我展示了如何通过使用Perl的gzip IO层来即时压缩数据。当磁盘空间有限时,这很方便

open my $fh, '>:gzip', $filename
  or die "Could not write to $filename: $!";

while( $_ = something_interesting() ) {
  print { $fh } $_;
}

我也可以选择相反的方向,在没有足够空间解压它们之前,直接从压缩文件中读取

open my $fh, '<:gzip', $filename
  or die "Could not read from $filename: $!";

while( <$fh> ) {
  print;
  }

更改STDOUT

如果我不喜欢标准输出,可以使用select更改默认输出文件句柄。但我也可以用另一种方式来做。当简单的办法不够有趣时,我可以改变STDOUT。David Farrell在如何重定向和恢复STDOUT中展示了其中的一些内容。

首先,我可以使用特殊的&模式“复制”标准输出文件句柄

use v5.10;

open my $STDOLD, '>&', STDOUT;

只要我在文件模式后添加&,任何文件模式都可以使用。

然后我可以重新打开STDOUT

open STDOUT, '>>', 'log.txt';
say 'This should be logged to log.txt.';

当我准备好将其改回时,我可以做同样的事情

open STDOUT, '>&', $STDOLD;
say 'This should show in the terminal';

如果我只有一个文件描述符,也许是因为我正在与一个认为vi是拐杖的老Unix程序员一起工作,我可以使用这个

open my $fh, "<&=$fd"
  or die "Could not open filehandle on $fd\n";

这个文件描述符也有一个三参数形式

open my $fh, '<&=', $fd
  or die "Could not open filehandle on $fd\n";

由于它们是同一文件描述符的不同名称,我可以有多个指向同一位置的文件句柄

use v5.10;

open my $fh, '>>&=', fileno(STDOUT);

say         'Going to default';
say $fh     'Going to duped version. fileno ' . fileno($fh);
say STDOUT  'Going to STDOUT. fileno ' . fileno($fh);

所有这些都会打印到STDOUT。


本文最初发布在PerlTricks.com

标签

brian d foy

brian d foy是一位Perl培训师和作家,也是Perl.com的高级编辑。他是Mastering Perl、《Mojolicious Web Clients》、《Learning Perl Exercises》的作者,以及《Programming Perl》、《Learning Perl》、《Intermediate Perl》和《Effective Perl Programming》的合著者。

浏览他们的文章

反馈

这篇文章有什么问题吗?请在GitHub上打开一个问题或拉取请求来帮助我们。