open FILEHANDLE, '/etc/hosts' or die $!;
my $string = do { local $/; <FILEHANDLE> };
print($string);
该 perl 代码运行的输出结果大概是:
127.0.0.1 localhost
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
即,/etc/hosts 的全部文件内容。
Q&A
其中,难理解的是第二行,在 perl 的常规写法中,读取文件的完整内容应该是用一个 while 循环逐行读取:
open FILEHANDLE, '/etc/hosts' or die $!;
my $string;
while (<FILEHANDLE>) {
$string .= $_;
}
这里详细的来解释一下第二行:
do { <block> }是 perl 中的代码块,函数的返回结果即<block>最后一个语句。<FILEHANDLE>是上述do { <block> }中的最后一个语句,即返回结果。而在 perl 中,<FILEHANDLE>的返回结果根据不同的上下文,有两种不同的返回结果,即:
my $scalar = <FILEHANDLE>; # 等号左边为标量时,返回文件的单行内容
my @array = <FILEHANDLE>; # 等号左边为数组时,返回文件的全部内容(文件的每行对应数组的每个元素)
$/变量的设置为该语法糖的关键。$/变量是 输入的分隔符 (input record seperator),默认情况下是换行符。即<FILEHANDLE>在标量上下文时,返回单行文件内容的默认表现是由$/变量决定的。而local $/相当于local $/ = undeflocal和my的区别:my是创建一个新变量,而local则是临时改变一个变量的值(在作用域内)
整体解析下来就是,通过 do {} 创建一个临时的作用域,在作用域中改变 $\ 的值,改变了 <FILEHANDLE> 在标量上下文中的分隔符(由 \n 变为 undef),达到了读取整个文件内容的目的。
参考文章
- The difference between my and local https://www.perlmonks.org/?node_id=94007
- Perl Idioms Explained - my $string = do { local $/; }; https://www.perlmonks.org/?node_id=287647