强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Perl 完全指南 / 第 20 章:特殊变量与全局符号

第 20 章:特殊变量与全局符号

“Perl 的特殊变量是其简洁性的秘密武器”

Perl 拥有大量特殊变量(magic variables),理解它们是读懂 Perl 惯用法的关键。本章系统介绍最常用和最重要的特殊变量。


20.1 $_ — 默认变量

$_ 是 Perl 中最著名的特殊变量,是大量操作的默认变量。

# 循环中的默认变量
for (1..5) {
    print "$_\n";    # $_ 是当前元素
}

# 文件读取
while (<STDIN>) {
    chomp;           # 默认操作 $_
    print "$_\n";    # 打印 $_
}

# grep 和 map
my @evens = grep { $_ % 2 == 0 } 1..10;
my @doubled = map { $_ * 2 } 1..10;

# 正则匹配
while (<STDIN>) {
    print if /pattern/;    # 隐式匹配 $_
}

# s/// 替换
while (<STDIN>) {
    s/old/new/g;           # 替换 $_
    print;
}

使用 $_ 的函数

# 这些函数默认操作 $_
print;          chomp;          chop;
length;         lc;             uc;
study;          pos;            reset;
split;          undef;          pos;

最佳实践:在短脚本和 one-liner 中使用 $_,在长函数中显式命名变量。


20.2 @ARGV — 命令行参数

#!/usr/bin/env perl
use strict;
use warnings;

# @ARGV 包含命令行参数
print "参数个数: ", scalar @ARGV, "\n";

for my $i (0..$#ARGV) {
    print "ARGV[$i] = $ARGV[$i]\n";
}

# 常见模式:文件名来自参数
while (<>) {    # 等价于 while (<STDIN>) 或逐文件读取 @ARGV
    print;
}

Getopt::Long — 参数解析

use strict;
use warnings;
use Getopt::Long;

my $verbose = 0;
my $output  = "default.txt";
my $count   = 1;

GetOptions(
    "verbose|v"  => \$verbose,      # -v 或 --verbose
    "output|o=s" => \$output,        # -o file 或 --output=file
    "count|c=i"  => \$count,         # -c 5 或 --count=5
    "help|h"     => sub { usage(); exit },
) or die "参数错误\n";

print "verbose: $verbose\n";
print "output: $output\n";
print "count: $count\n";
print "剩余参数: @ARGV\n";

sub usage {
    print <<'USAGE';
用法: script.pl [选项] [文件...]

选项:
  -v, --verbose      详细输出
  -o, --output FILE  输出文件(默认: default.txt)
  -c, --count N      计数(默认: 1)
  -h, --help         显示帮助
USAGE
}

Getopt::Long 选项类型

类型说明示例
!布尔值"verbose!" => \$v
=s字符串"name=s" => \$n
=i整数"count=i" => \$c
=f浮点数"rate=f" => \$r
:s可选字符串"color:s" => \$c
@列表"tags=s@" => \@t
%哈希"define=s%" => \%d

20.3 %ENV — 环境变量

# 读取环境变量
print "HOME: $ENV{HOME}\n";
print "PATH: $ENV{PATH}\n";
print "USER: $ENV{USER}\n";

# 设置环境变量(子进程可见)
$ENV{MY_VAR} = "hello";

# 列出所有环境变量
for my $key (sort keys %ENV) {
    print "$key = $ENV{$key}\n";
}

20.4 文件句柄相关变量

变量含义示例
$.当前文件行号while (<>) { print "$.: $_" }
$!系统错误 (errno)open(...) or die $!
$@eval 错误信息eval { ... }; warn $@ if $@
$^E扩展错误信息$! 更详细的系统错误
$/输入分隔符$/ = undef; # slurp 模式
$\输出分隔符$\ = "\n"; # 自动加换行
$,print 分隔符$, = ", ";
$"列表分隔符$" = ", "; print "@array"
$#数字输出格式不常用
`$`自动刷新缓冲区
# $| 自动刷新(常用于管道和实时输出)
$| = 1;
print "处理中...";
sleep 1;
print "完成\n";

# $/ — 输入记录分隔符
# 逐行(默认)
while (<>) { chomp; print "$_\n"; }

# 逐段落
{ local $/ = ""; while (<>) { print; } }

# 一次读完
{ local $/ = undef; my $all = <>; }

# $| — 自动刷新
$| = 1;   # 设置当前选中的文件句柄自动刷新
# 或者显式
STDOUT->autoflush(1);

20.5 正则相关变量

变量含义
$&整个匹配 ($MATCH)
$`匹配前的部分 ($PREMATCH)
$'匹配后的部分 ($POSTMATCH)
$1, $2, ...捕获组
$+最后一个捕获组
@-匹配起始位置 (@LAST_MATCH_START)
@+匹配结束位置 (@LAST_MATCH_END)
my $text = "Hello, Perl World!";

if ($text =~ /Perl/) {
    print "匹配: $&\n";      # Perl
    print "之前: $`\n";      # Hello, 
    print "之后: $'\n";       #  World!
}

# 使用 English 模块(可读性更好)
use English qw(-no_match_vars);

if ($text =~ /Perl/) {
    print "匹配: $MATCH\n";
    print "之前: $PREMATCH\n";
    print "之后: $POSTMATCH\n";
}

注意$&$`$' 会降低正则性能。使用 English 模块或 Perl 5.10+ 的 ${^MATCH} 并配合 /p 修饰符可以避免性能问题。


20.6 进程相关变量

变量含义说明
$0脚本名称可修改以改变 ps 显示
$$当前进程 PID
$^O操作系统名linux, darwin, MSWin32
$^XPerl 解释器路径
$^V$]Perl 版本
$^T脚本启动时间 (epoch)
$^W-w 开关状态
$^A格式化输出累加器
# 获取进程信息
print "脚本: $0\n";
print "PID: $$\n";
print "操作系统: $^O\n";
print "Perl: $^X\n";
print "版本: $^V\n";

# 修改进程名(Linux ps 可见)
$0 = "my_custom_process";

20.7 数组相关特殊变量

变量含义
@_子程序参数
@ARGV命令行参数
@INC模块搜索路径
@Fautosplit 结果 (-a 模式)
@EXPORT模块默认导出
@EXPORT_OK模块可选导出
# @F — 自动分割(-a 模式)
# perl -lane 'print $F[1]' file.txt
# 等价于 awk '{print $2}'

20.8 格式变量

变量含义
$~当前格式名
$^页眉格式名
$%当前页码
$=每页行数
$-剩余行数
$^L换页符
$:断行字符集

20.9 $a 和 $b — sort 专用变量

# $a 和 $b 是 sort 块中的特殊变量
my @sorted = sort { $a <=> $b } @numbers;
my @sorted2 = sort { $a cmp $b } @strings;

# 注意:$a 和 $b 不需要 my 声明
# 也不受 strict 'vars' 约束

20.10 English 模块 — 可读别名

use English qw(-no_match_vars);

# 用可读名称代替符号
$_         $ARG
$.         $INPUT_LINE_NUMBER (或 $NR)
$/         $INPUT_RECORD_SEPARATOR (或 $RS)
$|         $OUTPUT_AUTOFLUSH
$!         $ERRNO (或 $OS_ERROR)
$@         $EVAL_ERROR
@ARGV      @ARGV   # 已经可读
@_         @_
$$         $PID (或 $PROCESS_ID)
$^O        $OSNAME
$0         $PROGRAM_NAME (或 $0)

注意-no_match_vars 避免 $MATCH 等变量的性能惩罚。


20.11 业务场景:系统监控脚本

#!/usr/bin/env perl
use strict;
use warnings;
use POSIX qw(strftime);

$| = 1;  # 自动刷新

print "系统监控启动 (PID $$, OS: $^O)\n";
print "=" x 50, "\n";

while (1) {
    my $timestamp = strftime("%Y-%m-%d %H:%M:%S", localtime);
    
    # 获取负载
    my @load = split /\s+/, `cat /proc/loadavg`;
    
    # 获取内存
    open my $mem_fh, '<', '/proc/meminfo' or die $!;
    my %mem;
    while (<$mem_fh>) {
        if (/^(\w+):\s+(\d+)/) {
            $mem{$1} = $2;
        }
    }
    close $mem_fh;
    
    my $mem_total = $mem{MemTotal} / 1024;
    my $mem_free  = $mem{MemAvailable} // $mem{MemFree};
    $mem_free /= 1024;
    my $mem_used_pct = sprintf "%.1f", (1 - $mem_free / $mem_total) * 100;
    
    printf "[%s] Load: %s | Memory: %.0f MB / %.0f MB (%s%%)\n",
        $timestamp, $load[0], 
        $mem_total - $mem_free, $mem_total, $mem_used_pct;
    
    sleep 5;
}

本章小结

要点内容
$_默认变量,大量操作的隐式目标
@ARGV命令行参数
%ENV环境变量
$/输入分隔符,设置 undef 可 slurp
$|自动刷新输出缓冲区
$!系统错误信息
$@eval 错误信息
$&正则匹配(注意性能)
English可读别名

练习

  1. 编写使用 @ARGVGetopt::Long 的命令行工具
  2. 使用 %ENV 检测当前用户、HOME 目录和 SHELL
  3. 演示 $/ 的不同设置对文件读取的影响
  4. 编写脚本使用 $. 输出带行号的文件内容
  5. 使用 $|STDOUT->autoflush 实现实时进度条

扩展阅读