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 实现实时进度条
扩展阅读