Perl内置及特殊变量
2009-05-21 21:10:47 Administrator

这里总结整理了Perl的部分内置变量,可以在程序中直接引用,希望对大家有所帮助。Perl Keywords(functions,syntax,variables,file-handles,Various),据特殊的变量的使用性质,可以分为以下几类:
全局标量特殊变量。
全局数组特殊变量。
全局哈希特殊变量。
全局特殊文件句柄。
全局特殊常量。
正则表达式特殊变量。
文件句柄特殊变量。

$x{} x名字前面是美元符号($),后面是花括号({}),则其为 hash 元素
$- 当前页可打印的行数,属于Perl格式系统的一部分
$! 根据上下文内容返回错误号或者错误串
$” 列表分隔符
$# 打印数字时默认的数字输出格式
$$ Perl解释器的进程ID
$% 当前输出通道的当前页号
$& 与上个格式匹配的字符串
$( 当前进程的组ID
$) 当前进程的有效组ID
$* 设置1表示处理多行格式.现在多以/s和/m修饰符取代之.
$, @array 元素间的连接符
$. 上次阅读的文件的当前输入行号
$\ 改变print输出后的添加符号,当前输出记录的分隔符
$/  改变系统默认的换行符
$: 字符设置,此后的字符串将被分开,以填充连续的字段.
$; 在仿真多维数组时使用的分隔符.
$? 返回上一个外部命令的状态
$@ Perl解释器从eval语句返回的错误消息
$[ 数组中第一个元素的索引号
$ 当前输出记录的分隔符
$] Perl解释器的子版本号
$^ 当前通道最上面的页面输出格式名字
$^A 打印前用于保存格式化数据的变量
$^D 调试标志的值
$^E 在非UNIX环境中的操作系统扩展错误信息
$^F 最大的文件捆述符数值
$^H 由编译器激活的语法检查状态
$^I 内置控制编辑器的值
$^L 发送到输出通道的走纸换页符
$^M 备用内存池的大小
$^O 操作系统名
$^P 指定当前调试值的内部变量
$^R 正则表达式块的上次求值结果
$^S 当前解释器状态
$^T 从新世纪开始算起,脚步本以秒计算的开始运行的时间
$^V/$PERL_VERSION  Perl interpreter解析器的一系列版本信息
$^W 警告开关的当前值
$^X Perl二进制可执行代码的名字
$_ 默认的输入/输出和格式匹配空间
$| 控制对当前选择的输出文件句柄的缓冲
$~ 当前报告格式的名字
$` 在上个格式匹配信息前的字符串
$’ 在上个格式匹配信息后的字符串
$+ 与上个正则表达式搜索格式匹配的最后一个括号
$< 当前执行解释器的用户的真实ID
$<digits> 含有与上个匹配正则表达式对应括号结果
$= 当前页面可打印行的数目
$> 当前进程的有效用户ID
$0 包含正在执行的脚本的文件名
ARGV     命令行参数,循环的特殊文件句柄
$ARGV 从默认的文件句柄中读取时的当前文件名
DATA     内置特殊文件句柄,以_ _END_ _结束
@F     命令行的数组输入,autosplit(-a)开关使用时
%ENV 环境变量列表
%INC 通过do或require包含的文件列表
%SIG 信号列表及其处理方式
@_ 传给子程序的参数列表
@ARGV 传给脚本的命令行参数列表
@INC 在导入模块时需要搜索的目录列表
$-[0]和$+[0] 代表当前匹配的正则表达式在被匹配的字符串中的起始和终止的位置
@EXPORT     默认的数组包含默认导出的变量和函数的名字,当use packagename时就会得到的东西
@EXPORT_OK     其中的变量和函数只有当程序中use语句中特别要求时才会导出
%EXPORT_TAGS     中的键值对允许程序包含那些在@EXPORT和@EXPORT_OK中列出的特定的符号组,如果不想外面的模块导出什么,可以 使用@EXPORT_FAIL来实现

可做如下分类:
一、正则表达式特殊变量
1、$n:包含上次模式匹配的第n个子串
2、$&:前一次成功模式匹配的字符串
3、$`:前次匹配成功的子串之前的内容
4、$’:前次匹配成功的子串之后的内容
5、$+:前一次使用括号的模式匹配的字符串

二、文件句柄特殊变量
1、$|:如果设置为零,在每次调用函数write或print后,自动调用函数fflush,将所写内容写回文件
2、$%:当前输出页号
3、$=:当前每页长度
4、$-:当前页剩余的行数
5、$~:当前报表输出格式的名称.默认值是文件句柄名
6、$^:当前报表输出表头格式的名称.默认值是带后缀”_TOP”的文件句柄名

三、全局特殊变量
1、$_和$ARG:默认输入和模式匹配内容.如果不指定接收输入的变量或执行模式匹配的字符串
2、$.和$NR:前一次读的文件句柄的当前行号
3、$/和$RS:输入记录分隔符,默认是新行字符.如用undef这个变量,将读到文件结尾
4、$,的$OFS:输出域分隔符
5、$\和$ORS:输出记录分隔符
6、$: :断行字符,默认是”-”三个字符
7、$!:这个变量的数字值是errno的值,字符串值是对应的系统错误字符串
8、$@:命令eval的错误消息.如果为空,则表示上一次eval命令执行成功
9、$$:运行当前Perl脚本程序的进程号
10、$<:当前进程的实际用户号
11、$>:当前进程的有效用户号
12、$(:当前进程的实际组用户号
13、$):当前进程的有效组用户号
14、$[:数组的第一个元素的下标,默认是0
15、$]:Perl的版本号
16、$ARGV:当使用”< >”读文件时,保存当前文件名

四、全局特殊数组变量
1、@ARGV:保存命令行参数
2、@INC:保存查找Perl程序的目录
3、@_:在子程序内,这个数组变量包含传递给子程序的参数
4、@F:分割时给出一个命令行开关输入线数组
5、%INC:散列表%INC包含所有用do或require语句包含的文件.关键字是文件名,值是这个文件的路径
6、%ENV:包含当前环境变量
7、%SIG:哈希用来设置信号处理的各种信号

五、全局特殊文件句柄
1、ARGV:遍历@ARGV中的命令行文件名的特殊文件句柄,一般空句柄在写入<>
2、STDERR:标准错误中的任何包的特殊句柄
3、STDIN:标准输入任何包的特殊句柄
4、STDOUT:任何包的标准输出的特殊句柄
5、DATA:特殊的文件句柄,是指任何包含脚本文件后,__END__标志。特殊文件句柄,或为任何所需的文件后的__DATA__记号,只要你读取数据,在同一个包中的__DATA__在里边被发现
6、_ (underscore):使用特殊的文件句柄缓存从最后的stat, lstat,,或文件测试操作的信息

六、趣味符
$ 标量
@ 数组[给值时使用=>,记得不要和引用时的->混了]
% 散列{hash}
& 子例程,子函数
* typeglob
<>|钻石符 数据输入,如果没有指定文件名,则其从标准输入流中自动打开和关闭一系列文件进行读入

七、全局特殊常量
1、__END__:Indicates the logical end of your program. Any following text is ignored, but may be read via the DATA filehandle.
2、__FILE__:Represents the filename at the point in your program where it's used. Not interpolated into strings.
3、__LINE__:Represents the current line number. Not interpolated into strings.
4、__PACKAGE__:Represents the current package name at compile time, or undefined if there is no current package. Not interpolated into strings.

Perl 中特殊字符中的如 __FILE__, __LINE__, 和 __PACKAGE__ 分别表示当前执行脚本的文件名,行号,包名。

注意: __ 是两条下划线。

这些特殊字符是单独的标记,不能写在字符串中,例如:
use v5.20;
say "文件名 ". __FILE__;
say "行号 " . __LINE__ ;
say "包名 " . __PACKAGE__;
 
# 无法解析
say "__FILE__ __LINE__ __PACKAGE__";

执行以上程序,输出结果为:
文件名:/tmp/freeoa.pl
行号:3
包名:main
__FILE__ __LINE__ __PACKAGE__


八、exit与return
exit用于来退出脚本程序,return用于返回并退出函数。return是C语言提供的,exit是操作系统提供的(或者函数库中给出的)。
exit(0):正常运行程序并退出程序;
exit(1):非正常运行导致退出程序;

return():返回函数,若在主函数中,则会退出函数并返回一值。

1. return返回函数值,是关键字;exit 是一个函数。
2. return是语言级别的,它表示了调用堆栈的返回;而exit是系统调用级别的,它表示了一个进程的结束。
3. return是函数的退出(返回);exit是进程的退出。
4. return用于结束一个函数的执行,将函数的执行信息传出个其他调用函数使用;exit函数是退出应用程序,删除进程使用的内存空间,并将应用程序的一个状态返回给操作系统,这个状态标识了应用程序的一些运行信息,这个信息和机器和操作系统有关,一般是 0 为正常退出,非0 为非正常退出。


九、常用变量:@_
@_ 是子程序的一个私有变量;如果有一个全局变量@_,它将在此子程序调用前存储起来,当子程序调用完成后,其早期的值会被重新赋还给@_。这意味着当将参数传递给子程序时不用担心它会影响此程序中其它子程序的@_这个变量的值。嵌套的子程序调用时,@_的值和上述类似。甚至此子程序递归调用时,每一次调用将得到新的@_,因此子程序调用时将得到其自身的参数列表。

除非调用的子程序前有&而后面没有括号(或者没有参数),此时@_从此调用者的上下文(context)得到。这通常不是个好主意,但有时很有用。

Perl - $_ and @_
Perl's a great language for special variables - variables that are set up without the programmer having to intervene and providing information ranging from the number of lines read from the current input file ($.) through the current process ID ($$) and the operating system ($^O). Other special variables effect how certain operations are performed ($| controlling output buffering / flushing, for example), or are fundamental in the operation of certain facilities - no more so than $_ and @_.

Lets clear a misconception. $_ and @_ are different variables. In Perl, you can have a list and a scalar of the same name, and they refer to unrelated pieces of memory.

$_ is known as the "default input and pattern matching space". In other words, if you read in from a file handle at the top of a while loop, or run a foreach loop and don't name a loop variable, $_ is set up for you. Then any regular expression matches, chops (and lcs and many more) without a parameter, and even prints assume you want to work on $_. Thus:
while ($line = ) {
if ($line =~ /Perl/) {
print FHO $line;
}
print uc $line;
}

更可简写为:
while (){
/Perl/ and
print FHO;
print uc;
}

@_ is the list of incoming parameters to a sub. So if you write a sub, you refer to the first parameter in it as $_[0], the second parameter as $_[1] and so on. And you can refer to $#_ as the index number of the last parameter:
sub demo {
print "Called with ",$#_+1," params";
print "First param was $_[0]";
}
Note that the English module adds in the ability to refer to the special variables by other longer, but easier to remember, names such as @ARG for @_ and $PID for $$. But use English; can have a detrimental performance effect if you're matching regular expressions against long incoming strings.


十、最常见特殊变量的介绍

全局数组的特殊变量

@ARGV:命令行参数的数组,其中包含用于脚本。
@INC:数组含有名额(空间)查找Perl脚本的列表计算,评估,需求,或使用建构。
@F:分割时给出一个命令行开关输入线数组。

全局哈希特殊变量

%INC:哈希包含每个文件的文件名项已被列入通过或要求。
%ENV:哈希包含了您当前的环境。
%SIG:哈希用来设置信号处理的各种信号。

文件句柄的特殊变量

$|
$OUTPUT_AUTOFLUSH
如果设置为非零,强制每写入或打印fflush(3)后对当前选定的输出通道。

$%
$FORMAT_PAGE_NUMBER
当前选定的输出通道的当前页码。

$=
$FORMAT_LINES_PER_PAGE
当前页的长度(打印线)当前选中的输出通道。默认值是60。

$-
$FORMAT_LINES_LEFT
当前选中页面的输出通道上留下的行数。

$~
$FORMAT_NAME
当前报表格式为当前选定的输出通道的名称。默认的文件句柄的名字。

$^
当前顶级的页面格式为当前选定的输出通道的名称。默认为名称带_TOP附加句柄。

小结:在上面的文章中,我们大体了解perl的内置变量的值可以给我们带来什么样的效果,如果还想了解地更深入了解,去阅读官方手册吧。不过后文还对这些变量做了一个汇总。接下来将详细介绍10个最常用的特殊变量及其用法:

1、变量 $_

这个$_变量,叫做Perl的缺省变量(default variable),这个变量可以说是Perl里面用的最频繁的变量,这个变量的发音是“美元下划线(也就是dollar underline)”。当使用 -p 和 -n 这两个参数的时候。输入文件的当前行的内容就存储在这个变量当中。同时如果用了操作符和函数,但是没有写输入的变量,那么它们默认的处理对象就是 $_ 这个变量的内容:
perl -le '$_ = "foo"; print'

在这个例子中,将字符串 "foo" 放在了 $_ 这个变量当中,然后使用了print这个函数。不对这个函数设置参数的时候,print就是打印出$_这个变量的内容,也就是字符串"foo"。

同样的,$_ 也被用在正则表达式当中:
perl -ne '/foo/ && print'

这一行命令将会打印出匹配到"foo"字符串的输入行。这个正则表达式/foo/会隐式地作用于$_这个变量,也就是当前的行。可以写出完整的代码行,但是那样的话相比而言需要敲击很多次的键盘:
perl -ne 'if ($_ =~ /foo/) { print $_ }'

如果Perl解释器匹配到了字符串,然后就会打印出来。也可以直接把输入文件中所有行第一次出现的字符串for给替换掉,只需要在命令中简单地调用s/foo/bar/:
perl -pe 's/foo/bar/'

有趣的是,Perl是从sed这个Linux命令中借鉴$_这个变量的。还记得sed 有一个"pattern space"吗?$_这个变量同样也可以叫做Perl的"pattern space"。如果你用sed来实现上述Perl-one-line的功能,那将会是sed 's/foo/bar/'因为sed会把每一行放在"pattern space"当中,然后隐式地调用s命令。Perl从sed当中借鉴了很多的概念和命令。

$_变量配合 -n 参数使用Perl one line

当使用 -n 参数时,Perl 将会把你后面的代码用如下循环包裹进的程序。
while( <> ){
    #代码将会从这里开始执行(当然需要用 -e 参数来声明)
}

代码中的 while ( <> ) 循环将会从标准输入或者文件中一行一行地读入文本并进行处理。每一行的文本将会被放在$_ 这个变量当中,然后可以通过修改$_变量修改这一行,并打印出$_变量的内容。比如可以反转字符串并将其打印出来:
perl -lne 'print scalar reverse'

译者注:如果对上面的语法感觉很疑惑,可以到此网站查询其用法。通过这个例子我们可以看出,Perl有一个独特的语境设置(Perl有list和scalar两者语境),同一个内容在不同的语境中有不同的效果。我得知劳拉其实是一个语言学家,他加上了这些语言学的元素。有人批判说这个是文字游戏,其实你结合生活一想,现实中又何尝不是这样:一个会说话的人和一个不会说话的人,对同一件事情说不来的时候,给人的感觉是很不一样的,对他人的影响能力也是不一样的。

在上面的例子中,因为我用了-n参数,实际上这个程序是这样的:
while(<>){
    print scalar reverse;
}

这个程序也等于:
while(<>){
    print scalar reverse $_
}

因为Perl的函数如果没有参数的话,会自动作用于$_这个变量,所以这会造成reverse和reverse $_ 在功能上是一样的。需要使用scalar函数使reverse函数发挥作用的时候在一个scalar的环境里面。否则如果不加scalar的话,那么它就在list的环境里面(因为print函数会强制变成list的环境),这样就发挥不了reverse函数的作用。

$_变量配合 -p 参数使用Perl one line

当你使用 -p 参数的时候,Perl 会将如下的一个循环包裹你的代码。
while(<>){
    # 你的代码
}continue{
    print or die "-p failed: $!\n";
}

译者注:上面的这个continue有些奇怪,经过不完全地测试,上述代码等同于:
while(<>){
    # 你的代码
    print or die "-p failed: $!\n";
}

使用 -p 参数的结果几乎与-n一模一样,只是使用-p参数后,每次循环后$_的内容都会被打印出来。如果使用-p参数来反转每一行的内容的话,可以这样做:
perl -pe '$_ = reverse $_'

这个语句就会变成:
while(<>){
    $_ = reverse $_;
    print or die "-p failed: $!\n";
}

在这里修改了$_ 变量,使其等于reverse $_,这样就使每一行都反转了一下。

显式使用 $_ 变量

$_经常被显式地使用,这里有一些使用的例子。
perl -le '@vals = map { $_ * 2 } 1..10; print "@vals"'

运行这个指令后输出的结果是:2 4 6 8 10 12 14 16 18 20

在这里用了map这个函数, 去map一个list 中的所有元素然后返回一个新的列表,新列表中的每一个元素是map中的表达式作用后的结果。

译者注:这里有函数式编程的一些影子。

在这个例子中,list 是(1 .. 10) 表达式是 $_*2, 表达式的意思对list中的每个元素都乘以2。正如你看到的一样,我直接显式地运用了$_。当map函数对list进行遍历的时候,每个元素都很方便地可以用$_变量来表示。

那么现在利用便捷的map函数来解决一些问题吧,比如对于文本的每一行都乘以2。
perl -lane 'print " @{[map { $_ * 2 } @F] } " '

这个one-liner 将表达式 $_*2 作用于@F数组的每个元素。"@{[ . . .]} "这个看着很疯狂的写法,只是一种用来执行 引号中代码的一种写法。

另一个显式地利用 $_ 变量的一个函数式 grep,这个函数能够让你过滤一个list中的元素:
perl -le ' @vals = grep { $_ > 5} 1..10;print "@vals" '

这个语句的执行结果是“6 7 8 9 10”。grep函数可以过滤掉1 2 3 4 5这些小于等于5的元素。

下面再来应用一下grep,比如找到并且打印出当前行中所有满足回文结构 (palindromes) 的元素。
perl -alne 'print "@{[grep { $_ eq reverse $_ } @F]}"'

在这里grep函数判定的条件是 $_ eq reverse $_,如果当前元素满足回文结构则被返回,否则就被过滤掉。如果输入的是:
civic foo mom dad
bar baz 1234321 x

那么输出的结果就是:
civic mom dad
1234321 x

所有的这些输出元素都是回文结构,甚至可以通过在终端键入perldoc perlvar学习更多关于 $_ 这个变量的知识。

2、变量 $.

当Perl读取一个文件的时候, $. 这个变量总是会包含当前读取行的内容。例如在下面这个例子,对文件file中的内容标注行号:
perl -lne 'print "$. $_" file

也可以将当前行的内容变成原来行内容并在每一行的末尾加上行号:
perl -pe '$_ = "$. $_"' file

当出入两个文件的时候,变量$.不会被重置,因此如果想对两个文件同时标注行号,可以直接这样写:
perl -pe '$_ = "$. $_"' file1 file2

这样的话Perl会在读完file1这个文件之后继续标注file2这个文件,且行号是连续的(如果file1包括了10行,那么Perl读取file2的第一行的时候,它标注的行号就是11)。如果想重新设定$.这个变量,可以显式地关闭当前的文件句柄 ARGV;
perl -pe '$_ = "$. $_"; close ARGV if eof' file1 file2

ARGV是一个特殊的的文件句柄,它包含了当前打开的文件。通过调用eof,Perl就会检查一下当前处理的行是不是文件的最后一行;如果是最后一行,那close函数就会关闭它,也就是把$.变量重置为0。$.变量是具有记忆性的(从原理上来将,它是一个包变量),因此在关闭文件前它还是有效的;而在关闭文件后,该变量又被重新初始化为0了。即它与关联环境的文件句柄是强相关的,是受作用域和包影响的。

行的终止符号默认是"\n",当然可以手动地通过$/变量改变行的终止符,下面就来讨论一下$/这个内置变量。

3、变量 $/

这个变量告诉Perl 行终止符是什么,也就是Perl认为怎么样才叫做一行:
perl -lne 'print "$. $_"' file

Perl 读写文件的内容知道读到换行符,然后将这些内容放入$_这个变量。然后使$.变量的值增加1,再进一步Perl会调用 print 函数打印出 "$. $_"的内容,也就是打印出行号和当前行的内容。

这里有另外的例子,如果有一个文件的内容如下,你可以将$/变量设置为":",那么Perl就会一个数字一个数字地读取,每一行就是一个数字。
3:9:0:7:1:2:4:3:8:4:1:0:0:1:...

如果将$/变量设置为 undef,Perl会将整个文件视为一行放入$_变量(这个叫做 slurping)。
perl -le '$/ = undef; open $f, "<", "file"; $contents = <$f>'

这个语句会一次性地将 file 这个文件的内容放到$contents这个变量中。你也可以将 $/变量设置为一个整型数字的reference。例如:
$/ = \1024

在这个例子中,Perl一次读取1024字节的内容,将其放入$_变量(这也叫做 record-by-record reading)。

{
  local $/ = \2048;    #read a file 2Kb at a time
  while (<FILE>) {
    # $_ contains the next 2048 bytes from FILE
  }
}

注:这是影响内层的$.

译者注:经过译者的调试,下面的两个结果是不一样的:
perl -lpe ' BEGIN{$/ = \1}' file
#第一次赋值`$_`的时候行分割符还是"\n"
perl -lpe ' $/ = \2;' file

也可以用-0参数去设置分隔符,例如 -0072 等于$/ = :

4、变量 $\

这个变量会在每次调用print函数后加上这个变量的内容。比如可以在每次print后面加上"."这个内容。
perl -e '$\ = ". "; print "hello"; print "world"'

执行这个语句屏幕将会打印出:
hello. world.

当需要再每个print 的内容后面加一些东西的时候,修饰这个变量特别有用。请记住这个变量,例如你想在每次print 之后都加上"\n"分隔符,那么你可以在一开始就将$\变量赋值为"\n"。值得注意的是,在Perl新的版本中有一个say函数,这个函数和print很像,但是它会在每次打印内容后就加上"\n"。

5、变量 $1, $2, $3 等等

这些变量一般用来捕获最终的匹配值,这些匹配值一般都用圆括号来捕获。例如:
perl -nle 'if (/She said: (.*)/) { print $1 }'

Perl 会在当前行中匹配字符串"She said:"和后面的内容然后捕获所有的后面的内容。再把这些内容放入$1变量,最后打印出来。当再用另外一个括号的时候,后来一个捕获的内容就会放在$2这个变量中。$3也是如此。
perl -nle 'if (/(She|He) said: (.*)/) { print "$1: $2" }'

在这个语句中,首先"She"/"He"字符串将会被捕获在$1变量中,然后 "said: "字符串后面的内容也会被捕获并放在$2这个变量当中,最后我们将这两个变量以"$1: $2"这种形式打印出来。使用多少个圆括号就会有多少个捕获的变量,各个变量的名称依次是: $1、$2、$3……

注意:圆括号还有一个功能就是包裹一个连续的字符串,所以有时候不想捕获这些内容,那怎么办呢?可以在圆括号中用?:这样的符号。如将上面One-line语句中的(She|He)变成(?:She|He):
perl -nle 'if (/(?:She|He) said: (.*)/) { print "Someone said: $1" }'

这个语句不会捕获"She"或者"He"字符串。故第二个圆括号中捕获的内容就会在 $1这个变量中。

在Perl 5.10版本之后,可以用写了名字的群(如?<name>...)去捕获你要的字符串。这样做的时候你就不需要用$1 $2这些变量去调用捕获的字符串,而是用 $+{name}去调用这些捕获的字符串。例如,下面的这个语句可以捕获"She"或者"He" 在 gender这个群,"said:" 后面的内容在 text 这个群。
perl -nle 'if (/(?<gender>She|He) said: (?<text>.*)/) {
    print "$+{gender}: $+{text}"
}'

6、变量 $,

当打印很多个值时,$,变量是输出域的分隔符。在默认的情况下,处于未定义状态,也就是undefined,就是说所有输出的变量都是紧密地被打印在一起的。例如执行下面的语句:
perl -le 'print 1, 2, 3'

你将会得到 "123" 这个输出结果,如果你将 $,变量设置为一个分号',':
perl -le '$,=":"; print 1, 2, 3'

将会得到 "1:2:3"

7、变量 $"

请先看下面的两个例子:
perl -le '@data=(1,2,3); print "@data"'

输出的结果是 "1 2 3"
perl -le '@data=(1,2,3); $" = "-"; print "@data"'

输出的结果是 "1-2-3"

my %hash = (one => 1, two => 2, three => 3);
{
  local $" = ' < ';
  print "@hash{qw(one two three)}";
}

:>1 < 2 < 3

这个变量默认的值是一个空白符,是打印数组时的分隔符。

8、变量 @F

当使用-a参数的时候,Perl会根据空白符自动分割每一行然后将所有的元素都放在@F这个数组当中。例如输入的行是"foo bar baz",然后 @F就是一个("foo","bar","baz")的数组。这样的技术就会允许单独操作每一个域(field)。 可以利用$F[2]打印出第三个域的值:
perl -ane 'print $F[2]'

也可以做很多次的运算,比如将第五个域的值乘以2:
perl -ane '$F[4] *= 2; print "@F"'

在这里,第五个域的值乘以了2, print "@F"语句打印出整个数组的值,用一个空格来分割。可以将-a参数和-F参数连用,后者可以声明用哪个字符串作为输入行的分隔符。例如处理每行用分号分割的/etc/passwd 文件时,可以写如下的语句:
perl -a -F: -ne 'print $F[0]' /etc/passwd

9、变量@ARGV

这个变量包含了在运行命令时,传给Perl的参数。例如,下面的语句将会打印出 "foo bar baz" :
perl -le 'print "@ARGV"' foo bar baz

注意:当使用-n或者-p参数时,Perl将会一个接一个地打开文件,然后将@ARGV数组中的元素逐个删去。所以如果要通过这个数组去得到所有传入的文件名的话,那就要在 BEGIN{} 这个代码块中进行赋值:
perl -nle 'BEGIN { @A = @ARGV }; ...' file1 file2

然后可以通过@A数组引用。还有一个相似的变量,$ARGV,这个变量包含了当前读取文件的文件名。如果是从标准输入读取的,那么文件名叫做"-"。

10、变量 %ENV

这个哈希包含了你当前运行Shell的所有环境。下面的这行语句打印出了所有的环境变量:
perl -le 'print "$_: $ENV{$_}" for keys %ENV'

这个语句循环了所有这个哈希中的所有元素,每次都将 key 放入 $_变量然后打印出key对应的value。

-------------------------------------------------------------------------
相关应用举例

取得当前运行脚本的进程号
perl -e "sleep(2);print qq(The PID of process perl is : $$)";

这是一句命令行上使用的perl程序,作用就是打印出一段话:print qq(The PID of process perl is : $$) ,在这里,存在着特殊变量$$; $$是当前perl解析器的进程ID(即,PID),大家可以将在段程序写在命令行上,该程序会在perl解析器运行2秒后打印出perl解析器的进程 ID,大家可以在程序运行期间通过查看进程列表来确定打印结果是否正确。

返回错误信息(或是错误号)的特殊变量
perl -e "opendir FH,'one' or die qq(can't open:$!);";

这句话就是要打开当前下的一个one目录(而实际上并没有此目录,之所以这样写的目的就是为了引起一个程序中的错误信息),如果没有打开的话,会执行后面的die qq(can't open:$!) 。

在这里,$!就指明了错误信息的内容,命令行会返回错误信息:‘can't open:No such file or directory at -e line 1.’ $!在open或是opendir中用的比较多(无论是打开文件还是管道或是其它)。

输入输出控制变量
来个例子,说一个有意思的特殊内置变量
#!/usr/bin/perl
$text = "a.txt";
{
open FH,$text or die "can't open:$!";
my $line =<FH>;
close FH;
print $line;
}

print "#===========================# ";
{
undef $/;
open FH,$text or die "can't open:$!";
my $line =<FH>;
close FH;
print $line;
}

=================================
a.txt的内容是:
aaa
bbb
cc
=================================

好,程序的运行结果是:
aaa
#===========================#
aaa
bbb
cc

程序的作用就是读文件,关键部分在“$line =<FH> ”,尖括号操作符(即,<>)作用是读取一行文件内容(相信大多数教程或是书上都是这么写的),但是,这里所说的一行,实际上是有一个标准的,那就是当perl在遇到了换行符后,边认为是一行,而换行符默认情况下是由于$/这个特殊变量定义的,是缺省值。也就是说,perl 在每次使用<>操作符读文件的时候,会先从$/中获得一个叫做“分隔符”的东西,并以该分隔符为标记来读取文件,如果按默认的$/来说,这个分割符就是一个换行符,所以默认<>操作符才会每次读取一行文本。

在给出的例子中,有这样一句:undef $/,也就是将$/设置为未定义的值,这样一来呢,$/就不再起作用了,因此可以看到,第二次进行同样的操作的时候,<>操作符就不再是只读一行而是将文件内容全部读出来了。

接下来看一个会受到$/影响的内置变量,还是先说实例:
$text = "a.txt";
open FH,$text or die "can't open:$!";
while (<FH>) {
print "line $. is:$_";
}
close FH;

先来看循环中的:print "line $. is:$_"
程序的作用是读文件,并且将文件逐行赋值给$_(因为只用了<>操作符而没有指定内赋值变量,因此默认就是赋值给了$_);但是这里除了$_,还有一个变量“$.”我们来看一下结果:
line 1 is:aaa
line 2 is:bbb
line 3 is:cc

可见,变量$.的作用是一个类似计数器的东西,但为什么刚开始我说$.会受到$/变量的影响呢?

将上面的例子稍做修改,再看一下:
$text = "a.txt";
undef $/; # 注意,多加了这一行
open FH,$text or die "can't open:$!";
while (){
print "line $. is:$_";
}
close FH;

第二行多加了一句undef $/;,上面已经说过它的作用了,来看一下这段程序运行的结果:
line 1 is:aaa
bbb
cc

本来应该是三行的文本,不但被一次性打印出来,而且还指明了“line 1”(看来程序认为这个文本只有一行),为什么?这是因为$.变量并不是一个单纯的行计数器,确切地说,可以说$.是一个对$/计数器,我们再来看一段程序大概就会明白了:
$text = "a.txt";
$/ = ";"; # 注意这里,这里我并将$/设置为未定义,而是给其赋值为分号
open FH,$text or die "can't open:$!";
while (){
print "line $. is:$_";
}
close FH;

再看一下a.txt的内容:
ddd;bbb;ccc;fff;eee;

就这么简单了。看一下运行结果:
line 1 is:ddd; line 2 is:bbb; line 3 is:ccc; line 4 is:fff; line 5 is:eee; line 6 is:

看过这段程序应该就明白了吧。$.已经讲得差不多了,接下来再说一个它的特性,仍然是看程序:
$text = "a.txt";
open FH,$text or die "can't open:$!";
while (){
print "line $. is:$_ ";
}
print " ",$.;
close FH;
print " ",$.;

这个程序要注意的是最后三行中的两个print " ",$.;(一个是在关闭文件前,另外一个则是在关闭文件后)。看一下执行结果:
line 1 is:aaa
line 2 is:bbb
line 3 is:cc
3 0

结果的前三行刚才就已经知道了,这不是我们关心的,我们应该关心的是最后两行结果(即两个print " ",$.;的结果);$.变量是具有记忆性的(从原理上来将,它是一个包变量),因此在关闭文件前打印会发现,它的值是还没有变化的(这时的值和最后一次读完文件的值均为3);而在关闭文件后,该变量又被重新初始化为0了。该特性大家注意一下就可以了,因为其中的原理涉及到了perl的作用域和包的一些知识,不是很容易说清楚,所以就不说了,如果对包和作用域有了解的读者对这部分即使我不讲他们也应该能理解的。

判断操作系统类型
说一个用来简单判断操作系统类型的,看个例子:
C:>perl -e "print $^O;";
MSWin32

注意,这里这个特殊变量$^O 最后一个字符是字母O,且大写。如果是在linux下的话,结果就不是MSWin32了,而是Linux ;这是一个判断环境的变量,简单实用。

行计数
由于某些原因,很多人似乎完全忽略了“$.”的存在。而这个变量的作用就是跟踪当前记录号。因此上面的也可以这样来写:
while (<FILE>){
unless (/some regex/){
warn "Error in line $.";
next;
}
# process the record in some way
}

注:通俗的说,这个内置变量就跟数据库中的记录指针非常相似,它的值就是你当前所读文件中的当前行号。
虽然使用此内置变量并不能让你少打多少字,但重要的是我们可以省去一些不必要的变量声明。另一种利用此内置变量的方法就是与连续操作符(..)一起使用。当用在列表上下文中时,(..)是列表构建操作符。它将从给出的开始和结束元素之间创建所有的元素。例如:
my @numbers = (1 .. 1000);

@numbers 将包含从1到1000之间所有的整数。但是当你在一个表达式上下文中使用此操作符时(比如,作为一个声明的条件),它的作用就完全不一样了。第一个操作数(“..“左侧的表达式)将被求值,如果得出的值为假,此次操作将什么也不做并返回假值。如果得出的值为真,操作返回真值并继续依次返回下面的值直到第二个操作数(“..”操作符右面的表达式)返回真值。

举例子解释一下。假设你有一个文件,只想处理这个文件的某几个部分。这几个部分以"!! START !!"为开始,"!! END !!"为结束。使用连续操作符可以这样写这段:
while (<FILE>){
if (/!! START !!/ .. /!! END !!/){
# process line
}
}

每一次循环,连续操作符就会检查当前行。如果当前行与“/!! START !!/”不匹配,则操作符返回假值并继续循环。当循环到第一个与/!! START !!/”相匹配的行时,连续操作符就会返回真值并执行if语句块中的。在while语句后面的循环中,连续操作符将再次检查“/!! END !!/”的匹配行,但是它直到找到匹配行后才会返回真值。这也就是说在"!! START !!" 和"!! END !!" 标记之间的所有行都将被处理。当找到/!! END !!/的匹配行后,连续操作符返回假并再次开始匹配第一个规则表达式。

这些与“$.”有什么关系呢?如果连续操作符的操作数有一个是常量的话,他们将被转化为整型数并于“$.”匹配。 因此输出一个文件的前10行内容我们可以这样写:
while (<FILE>){
print if 1 .. 10;
}

关于“$.”最后要说明的一点是,一个程序中只有一个“$.”变量。如果你在从多个文件句柄中读数据,那么“$.”变量保存了最近读过的文件句柄中的当前记录号。如果你想要更复杂的解决此问题的方法那么你可以使用类似IO::FILE对象。这些对象都有一个input_line_number方法。

记录分隔符
“$/” 和“$\”分别是输入输出记录分隔符。当你在读或者写数据时,他们主要控制用什么来定义一个“记录”。让我更详细地给大家解释一下吧。当你第一次学习 perl,第一次知道文件输入操作符的时候,也许你会被告知“<FILE>”就是从一个文件读入一行数据,而读入的每一行都包括一个新行字符 (“”)。其实你所知道的这些并不完全是真的,那只是一个很特殊的情况。实际上文件输入操作符(“<>”)读数据后会包含一个在“$/” 中指定的文件输入分隔符。让我们来看一个例子:假设你有一个文本文件,内容是些有趣的引文或者一些歌词或者一些别的什么东西。比如类似下面的内容:
This is the definition of my life
%%
We are far too young and clever
%%
Stab a sorry heart
With your favorite finger

在这里有三段被一行“%%”分隔的引文。那么我们该如何从这个文件中一次读取一段引文呢。(译者注:这一段引文可是一行也可以是几行,比如例子中的第一段和第二段引文都是一行,而第三段引文是2行)。其中一个解决方法就是,一次从文件中读取一行,然后检查读入的行是否是“%%”。因此我们需要声明一个变量用来保存每次读入的数据,当遇到“%%”后重新组合先前读入的数据为一段完整的引文。你还需要记得处理最后一段引文因为它最后没有“%%”。

这样的方法太过于复杂,一个简单的方法就是更改“$/”变量的内容。该变量的默认值是一个新行字符(“”),这也就是为什么“<>”操作符在读取文件内容时是一次读一行,但我们可以修改这一变量内容为我们喜欢的任意值。比如:
$/ = "%%";
while (<QUOTE>){
chomp;
print;
}

现在我们每次调用“<>”,perl会从文件句柄中一次读取数据直到发现“%%”为止(不是一次读一行了)。因此当你用chomp函数来去掉读取数据的行分隔符时,就会删除“$/”变量中指定的分隔符了,在上例中经过chomp函数处理后的数据都会将 %%”删除。

更改perl的特殊变量
在继续之前,需要提醒你的是,当修改了这些特殊变量的值后,会得到一个警告。问题就是这些变量中的多数是被强制在主包中的,也就是说当更改这些变量的值时,程序中用到这个值的地方(包括你包含的那些模块)都会给出警告。比如在写一个模块,且在模块中更改了“$/”变量的值,那么当别人把你的模块应用到自己的程序中时就必须相应的修改其他模块以适应程序的执行。所以修改特殊变量的值潜在地增加了查找bugs的难度。因此应该尽可能的避免它。第一个避免的方法是在用完了修改后的特殊变量的值后应该将该特殊变量重值回原始值。比如:
$/ = "%%";
while (<QUOTE>){
chomp;
print;
}
$/ = "";

而这个方法引发的另一个问题就是不能确定在你重置特殊变量的值之前它的值就是系统默认值。(译者注:比如如果你在“$/ = "%%";”之前就修改过“$/”变量的值(不是默认值“”),那么最后重置回默认值肯定会引发错误的)因此应该像如下才对,如下:
$old_input_rec_sep = $/;
$/ = "%%";
while (<QUOTE>){
chomp;
print;
}
$/ = $old_input_rec_sep;

上面的就避免了上述所说的bug,但有另一个看起来更简练的方法,这个方法就是使用local来定义“$/”变量。如下:
{
local $/ = "%%";
while (<QUOTE>){
chomp;
print;
}
}

将以一对大括号括起来。一般地块往往与循环,条件或者是子程序有关联,但在perl中是可以单独用大括号来说明一个块的,而在这个块内用local定义的变量只在当前块中起作用。综上所述,不更改perl的内置变量是一个很好的习惯,除非它被本地化在一个块中。

“$/”的其他值
下面给出一些你可以赋予“$/”变量的特殊值,这些值可以开启一些有趣的行为。第一个就是设置该变量为未定义。这将开启slurp模式,开启该模式后我们可以一次性从一个文件中读取全部的文件内容。如下:
my $file = do { local $/; <FILE> };

一个do语句块的返回值是语句块中最后一个表达式的值,如上面的do语句块的返回值就是“<>”操作符的返回值。而且由于“$/”变量被设置为 undef(未定义),所以返回的就是整个文件的内容。需要注意的是,我们不需要明确地指定“$/”变量为undef,因为所有的perl变量在定义的时候就被自动初始化为undef。

设置“$/”变量为undef和空值是有很大区别的:设置成空值意味着开启paragraph模式 (即段落模式),在这种模式下,每个记录就是一段以一个或更多空行为结束的文本段落。也许你会认为这种效果和把“$/”变量被设置为“”的效果是一样的,但是他们还是有微妙的区别的。如果一定进行比较,那么应该把“$/”变量设置成为“+”才能和paragraph模式相同。(注意,这里只是比方说。实际上是不能将“$/”变量设置为规则表达式的)“$/”变量的最后一个特殊值就是可以将其设置为一个整数标量变量的引用或者是一个整数常量的引用。

在这种情况下,从文件句柄中每次读出的数据最多是“$/”变量指定的大小。(在这里我说“最多”是因为在文件的最后有可能剩余的数据大小小于“$/”变量指定的大小)。因此,如果你想每次读出2kb的数据那么你可以这样做:
{
local $/ = 2048;
while (<FILE>) {
# $_ contains the next 2048 bytes from FILE
}
}

“$/”和“$.”
注意到当改变“$/”变量的值时候也相应的改变了perl对于记录的定义因此也改变了“$.”变量的行为。“$.”变量实际上保存的不再是当前“行”号了,而是当前的记录号。因此在前述的那个引文的例子中,“$.”变量将按照你所要读出数据的文件中的每一段引文递增。

关于“$\”
在前面的开始我提到了“$/”和“$\”变量作为输入和输出的记录分隔符。但是我们一直没有介绍“$\”变量。

说实话,“$\”并不像“$/”那么有用。它包含了每次调用print输出时在最后要增加的字符串。它的默认值是空字符串,因此当你用print进行输出时,并没有任何东西跟在输出的数据后面。当然如果你非常希望能有个类似pascal的输出函数println,那么我们可以这样写:
sub println{
local $\ = "\n";
print @_;
}

这样,在每次用print输出数据时都会在数据后面增加一个"\n"(即换行符)。

数组格式化输出

接下来的两个需要讨论的变量是非常容易混淆,尽管它们做的是完全不同的两件事。为了举例说明,看下面:
my @arr = (1, 2, 3);
print @arr;
print "@arr";

现在,如果不仔细地看你是否知道上面两个print调用的区别吗?
答案是,第一个print调用会紧挨着输出数组的三个元素,其间没有任何分割符(输出为:123)。然而第二个print语句输出的元素确实以空格为分隔的(输出为:1 2 3)。为什么会有此区别呢?

理解这个问题的关键就是,在每种情况下实际传给print调用的是什么。在第一种情况下,传递给print的是一个数组。perl将展开传递过来的数组为一个列表,列表中的三个元素被视为单独的参数。而第二种情况下,在传递给print之前,数组被双引号所包含。确切地说第二种情况也可以理解成如下的过程:
my $string = "@arr";
print $string;

因此,在第二种情况看来,传递给print函数的只是一个参数。事实上的结果就是对一个数组进行了双引号的包含,并不影响print函数是如何对待该字符串的。

因此摆在我们面前的就是两种情况。当print接收一组参数的时候,它将紧凑地将这些参数输出而在输出的参数之间没有空格。当一个数组被双引号包含起来传递给print之前,数组的每个元素将以空格为分隔符展开为一个字符串。这两种情况是完全不相干的。不过从我们上面举的例子我们很容易看出人们是如何混淆这两种情况的。当然,如果我们愿意,perl允许我们改变这种行为。“ $,”变量保存了分隔传递给print函数的参数所用到的字符串。正如上面介绍的,默认分割print参数的字符是空字符,当然这都是可以更改的:
my @arr = (1, 2, 3);
{
local $, = ',';
print @arr;
}

这段将输出1,2,3

相应地,当一个数组被双引号包含传递给print函数时,展开这个数组后用来分割元素的字符则保存在“$"”变量中。如下:
my @arr = (1, 2, 3);
{
local $" = '+';
print "@arr";
}

这段将输出 1+2+3
当然,在一个print语句的使用中“$"”变量并不是必须的。你可以用在任何被双引号包含的数组的地方。而且它也不仅仅是对数组才有效。

也可以用在哈希表上。
my %hash = (one => 1, two => 2, three => 3);
{
local $" = ' < ';
print "@hash{qw(one two three)}";
}

这将输出: 1 < 2 < 3


正则匹配变量(注意:这种方法操作简单,但效率不高)
模式匹配特殊变量 $&, $`,$'
$& 用来存放匹配中的值
$` 用来存放匹配中之前所有字符
$' 用来存放匹配中之后所有字符

如:
if("Hello good  there,neigbor hello" =~ /S(w+),/){
print "That actually matched '$&'. ";
print $`." ";
print $'." ";
}

执行的结果为:
That actually matched 'there,'.
Hello good  
neigbor hell

$^I:文件原位编缉(perl -i.bak -pe ...)

freeoa.net.txt
Company: FreeOA
Department: R&D
Phone: +1 503555-0095
Date: Tues March 9,2014
Version: 2.1

use v5.20;
$^I = ".bak";
my $date=localtime;
while(<>){
    s/^Phone:.*//s;    #移除Phone字段
    s/^Date:.*/Date: $date/;    #更新日期为当前
    print;
}

>freeoa.net.txt(原始文件已被复制到同级目录下的freeoa.net.txt.bak)
Company: FreeOA
Department: R&D
Date: Thu Jan 25 17:20:03 2024
Version: 2.1


特殊变量全汇总

全局标量特殊变量

以下列出了所有的标量特殊变量,包含了特殊字符与英文形式的变量:
$_默认输入和模式匹配内容。
$ARG
$.前一次读的文件句柄的当前行号
$NR
$/输入记录分隔符,默认是新行字符。如用undef这个变量,将读到文件结尾。
$RS
$,输出域分隔符
$OFS
$\输出记录分隔符
$ORS
$"该变量同 $,类似,但应用于向双引号引起的字符串(或类似的内插字符串)中内插数组和切片值的场合。默认为一个空格。
$LIST_SEPARATOR
$;在仿真多维数组时使用的分隔符。默认为 "\034"。
$SUBSCRIPT_SEPARATOR
$^L发送到输出通道的走纸换页符。默认为 "\f".
$FORMAT_FORMFEED
$:The current set of characters after which a string may be broken to fill continuation fields (starting with ^) in a format. Default is "\n"".
$FORMAT_LINE_BREAK_CHARACTERS
$^A打印前用于保存格式化数据的变量
$ACCUMULATOR
$#打印数字时默认的数字输出格式(已废弃)。
$OFMT
$?返回上一个外部命令的状态
$CHILD_ERROR
$!这个变量的数字值是errno的值,字符串值是对应的系统错误字符串
$OS_ERROR or $ERRNO
$@命令eval的错误消息.如果为空,则表示上一次eval命令执行成功
$EVAL_ERROR
$$运行当前Perl脚本程序的进程号
$PROCESS_ID or $PID
$<当前进程的实际用户号
$REAL_USER_ID or $UID
$>当前进程的有效用户号
$EFFECTIVE_USER_ID or $EUID
$(当前进程的实际组用户号
$REAL_GROUP_ID or $GID
$)当前进程的有效组用户号
$EFFECTIVE_GROUP_ID or $EGID
$0包含正在执行的脚本的文件名
$PROGRAM_NAME
$[数组的数组第一个元素的下标,默认是 0。
 
$]Perl的版本号
$PERL_VERSION
$^D调试标志的值
$DEBUGGING
$^E在非UNIX环境中的操作系统扩展错误信息
$EXTENDED_OS_ERROR
$^F最大的文件捆述符数值
$SYSTEM_FD_MAX
$^H由编译器激活的语法检查状态
 
$^I内置控制编辑器的值
$INPLACE_EDIT
$^M备用内存池的大小
 
$^O操作系统名
$OSNAME
$^P指定当前调试值的内部变量
$PERLDB
$^T从新世纪开始算起,脚步本以秒计算的开始运行的时间
$BASETIME
$^W警告开关的当前值
$WARNING
$^XPerl二进制可执行代码的名字
$EXECUTABLE_NAME
$ARGV从默认的文件句柄中读取时的当前文件名
 

全局数组特殊变量
@ARGV传给脚本的命令行参数列表
@INC在导入模块时需要搜索的目录列表
@F命令行的数组输入


全局哈希特殊变量

%INC

散列表%INC包含所有用do或require语句包含的文件.关键字是文件名,值是这个文件的路径

%ENV

包含当前环境变量

%SIG

信号列表及其处理方式


全局特殊文件句柄

ARGV

遍历数组变量@ARGV中的所有文件名的特殊文件句柄

STDERR

标准错误输出句柄

STDIN

标准输入句柄

STDOUT

标准输出句柄

DATA

特殊文件句柄引用了在文件中 __END__ 标志后的任何内容包含脚本内容。或者引用一个包含文件中__DATA__ 标志后的所有内容,只要你在同一个包有读取数据,__DATA__ 就存在。

_ (下划线)

特殊的文件句柄用于缓存文件信息(fstat、stat和lstat)。


全局特殊常量

__END__

脚本的逻辑结束,忽略后面的文本。

__FILE__

当前文件名

__LINE__

当前行号

__PACKAGE__

当前包名,默认的包名是main。


正则表达式特殊变量

$n

包含上次模式匹配的第n个子串

$&

前一次成功模式匹配的字符串

$MATCH

$`

前次匹配成功的子串之前的内容

$PREMATCH

$'

前次匹配成功的子串之后的内容

$POSTMATCH

$+

与上个正则表达式搜索格式匹配的最后一个括号。例如:

/Version: (.*)|Revision: (.*)/ && ($rev = $+);

$LAST_PAREN_MATCH



文件句柄特殊变量

$|

如果设置为零,在每次调用函数write或print后,自动调用函数fflush,将所写内容写回文件

$OUTPUT_AUTOFLUSH

$%

当前输出页号

$FORMAT_PAGE_NUMBER

$=

当前每页长度。默认为 60。

$FORMAT_LINES_PER_PAGE

$-

当前页剩余的行数

$FORMAT_LINES_LEFT

$~

当前报表输出格式的名称。默认值是文件句柄名。

$FORMAT_NAME

$^

当前报表输出表头格式的名称。默认值是带后缀"_TOP"的文件句柄名。

$FORMAT_TOP_NAME



参考链接:
Perl Special Variables Quick Reference
Special Variables
Perl特殊变量
该文章最后由 阿炯 于 2024-02-13 13:15:32 更新,目前是第 5 版。