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 |
$^X | Perl 解释器路径 | |
$^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 | 模块搜索路径 |
@F | autosplit 结果 (-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 | 可读别名 |
练习
- 编写使用
@ARGV 和 Getopt::Long 的命令行工具 - 使用
%ENV 检测当前用户、HOME 目录和 SHELL - 演示
$/ 的不同设置对文件读取的影响 - 编写脚本使用
$. 输出带行号的文件内容 - 使用
$| 和 STDOUT->autoflush 实现实时进度条
扩展阅读