Perl入门
前言¶
问题¶
-
如何卸载包?
% cpan install App::cpanminus
% cpanminus --uninstall Module::Name
-
EBCDIC字符集是什么
建议和总结¶
关于学习编程语言,再次我将提几个建议:
- 无论你此时此刻学的是何种语言,但都要记住一点,学会**看文档** ,无论官网的document,还是系统下的manpage,还是语言或工具独有的help,比如Linux/Unix的man,python中的help和dir,命令行工具中的--help和-h,你都必须学会看,因为那里有**最权威**,**最准确**和**最详细**的解释。
- 先选一种**好的开始**,再选一本**好的书籍**,最后再**茁壮成长**。
- 好的开始:如果你要学的一门语言较难,或者你没有编程经验,那么最好的方法是通过阅读一本较为简单的书籍,或是看一个较为入门的视频,或是参加一场短周期的培训。(内容基础,样例充足,但不能影响对一门语言的整体认识,涉及到应有的语法知识,基本变量和常用函数)。
- 好的书籍:其实认真学完一本经典书籍就足以让你得心应手了,所以挑一本经典书籍,学后数日乃至数周都将其思想反复琢磨参透便够。
- 茁壮成长:无论是你的教师还是书籍,亦或是视频都只能作为你的引路者,路最终如何走,走得如何还是得看你自己。当然,如果你到了这一步后,你会发现你已经看透计算机的真面目,你会像欣赏落日余晖般沉醉:你说不出哪里好,但就是喜欢。
- 学时记录疑问(标记和简短的描述,不要让其打断学习节奏),学后总结汇总。
- 脑子不是用来记忆的,计算机不是用来好奇的。如果要我把人脑和计算机做个比喻,现在我喜欢如此描述: > 脑子是CPU的一级缓存(内存小,速度快),计算机是CPU的二级/三级缓存以及内存和磁盘。 > 所以脑子只适合用来思考,用来缓存常用数据和存储记录了低级缓存索引/摘要的目录页。 > 请解放你的大脑,让它回到该有的地方。不要再去强调记忆,让计算机服务于我们才是程序员应有个的态度。 > 专注,专注,再专注。让高级神经里的有限精力都专注于一件事上,打开生物所谓的涌流状态,进入真正意义上的高速运转吧。
书籍¶
- 《Programming Perl》:Perl语言编程
- 《Intermediate Perl》
- 《Mastering Perl》
- 《Effective Perl Programming》 # Perl高效编程
- 《Learning Perl Student Workbook》
- 《Perl 5 Pocket Reference》
- 《Mastering Regular Expressions》 - Jeffrey Friedl(O'Reilly):精通正则表达式
- 《Programming the Perl DBI》 - Alligator Descartes & Time Bunce(O'Reilly):DBI,数据库接口
站点¶
- 源码下载
- CPAN
- https://www.pm.org
- Perl核心文档
- Perl FAQ
- https://learn.perl.org
- Perl package manager
- DBI,数据库接口
特殊变量¶
具体查看**perlvar**文档
变量 | 描述 |
---|---|
$! | 可读的系统错误信息 |
$#array | 数组长度 |
$_ | 默认变量 |
$^R | 内嵌代码的运行结果 |
$" | 字符串中的数组内插的分隔符,默认为空格 |
$ARGV | 当前蒸菜处理的文件 |
@ARGV | 可执行程序的参数列表 |
$$ | 进程标识 |
$? | 进程结束时的返回值 |
%SIG | 信号处理函数,key是信号名(非前缀,如INT),value是信号处理函数 |
$@ | eval的返回值 |
$0 | 子程序名 |
$| | 缓冲设置 |
%ENV | 环境变量 |
$^I | 备份<>操作符操作的文件,该文件的后缀为$^I的值(默认不备份) |
$` | 正则匹配区段前的内容 |
$& | 正则匹配区段的内容 |
$' | 正则匹配区段后的内容 |
$^N, $+ | 编号最小和最大的捕获内容 |
%+ | 命名捕获组 |
@_ | 子程序的参数列表 |
第一章 简介¶
反引号:调用外部命令,可获取返回值
第二章 标量数据¶
- 数字总是按“双精度浮点数”来保存并运算的。
- 允许在整数直接量中插入下划线,便于阅读:
61_284_042_283_586
- 乘幂:
- 2的3次方:
2**3
- 2的3次方:
- “无内置限制”原则:可以填满内存
- 在源码中使用Unicode编码:
use utf8;
- 单引号:直接量,除了单引号和反斜杠外的字符都是本身
-
双引号:直接量,支持**变量内插**,支持**转义字符** o
转义字符 描述 \n 换行 \r 回车 \t 水平制表符 \f 换页符 \b 退格 \a 系统响铃 \e ESC(ASCII编码的转义字符) \007 八进制标识的ASCII值 \x7f 十六机制标识的ASCII \x{2744} 十六进制标识的Unicode代码点 \cC 控制符,也就是Control键的代码(此例表示Ctrl键和C键的返回码) \ 反斜杠 \" 双引号 \l 将下个字母转为小写 \L 将它后面的所有字母都转为小写的,直到\E为止 \u 将下个字母转为大写 \U 将它后面的所有字母都转为大写的,直到\E为止 \Q 相当于把它到\E之间的非单词(non word)字符加上反斜线转义 \E 结束\L、\U、\Q开始的作用范围 -
连接字符串:
- "helloword":
"hello" . "world"
- "helloword":
- 重复字符串
- "haha":
"ha" x 2
- "haha":
- 数字与字符串之间自动转换
- 内置警告信息
% perl -w my_program
use warnings;
/usr/bin/perl -w
- 详细的信息:
use diagnostics;
- 详细的信息:
% perl -W my_program
- 变量:存储一个或多个的得容器的名称。
- 标量变量:单单存储一个值得变量。$符号意为“取标量”,
$variable
- 内插时可用花括号来限定名称范围:
print "Hi ${name}jny\n";
- 标量变量:单单存储一个值得变量。$符号意为“取标量”,
- 双目运算符:
+=
,.=
,/=
,*=
,**=
等等 - 代码点
chr()
:代码转字符,$alef = chr( 0x05D0 )
ord()
:字符转代码点,$code_point = ord('?')
-
优先级
结合性 操作符 左 () 左 -> ++ -- 右 ** 右 ! ~ + -(单目操作符) 左 =~ !~ 左 * / % x 左 + - .(双目操作符) 左 << >> 具名的单目操作符(-X 文件测试; rand) < <= > >= lt le gt ge == != <=> eq ne cmp 左 & 左 | ^ 左 && 左 || .. ... 右 ?:(三目操作符) 右 = += -= .= (以及类似的赋值操作符) 左 , => 右 not 左 and 左 or xor -
数值与字符串的比较操作符
比较 数字 字符串 相等 == eq 不等 != ne 小于 < lt 大于 > gt 小于或等于 <= le 大于或等于 >= ge -
布尔值
- 假:
0
,''
,'0'
,undef
- 假:
-
用户输入:
<STDIN>
,读取的数据会带有换行符 - chomp:去除换行符,
chomp($text = <STDIN>)
- undef:数字使用时如同
0
,字符串使用时如同''
。 -
defined:判断是否定义
第三章 列表与数组¶
概念¶
- 列表:指的是标量的有序集合
-
数组:存储列表的变量。数组的名字空间和标量的名字空间是完全分开的:
-
数组索引:
-
字符串中的数组内插:自动添加分隔符,此分隔符是由特殊变量
$"
的值指定的,默认为空格 -
变量上下文和列表上下文
@people = qw( fred barney betty ); @sorted = sort @people; # 列表上下文,@people -> (barney, betty, fred) $number = 42 + @people; # 标量上下文,@people -> 元素个数 $backwards = reverse qw/ yabba dabba doo /; # 会得到 oodabbadabbay ($dino) = something; # 列表上下文,括号即是列表 @barney = 'hello' . ' ' . 'world'; # 得到的就是一个字符串列表:("hello world") print "I have ", scalar @rocks, " rocks!\n"; # scalar -> 强制引入标量上下文,@rocks会被替换成元素个数
pop、push¶
@rocks = qw( 1 2 3 );
$elem = pop @rocks; # @rocks: 1 2, $elem: 3
push @rocks $elem; # @rocks: 1 2 3
shift、unshift¶
@rocks = qw( 1 2 3 );
$elem = shift @rocks; # $elem: 1, @rocks: 2 3
unshift @rocks $elem; # @rocks: 1 2 3
splice¶
# format: @delete splice @dst, 位置, 删除个数, 插入元素
# 在原来的数组中删掉fred及其后元素
# @removed 编程 qw( fred barney betty )
# 而原先的@array则编程qw( pebbles dino )
@array = qw( pebbles dino fred barney betty);
@removed = splice @array, 2;
# 删除dino和fred这两个元素
# @removed 编程 qw( dino fred )
# 而 @array 则编程 qw( prebbles barney betty )
@array = qw( pebbles dino fred barney betty );
@removed = splice @array, 1, 2;
# 删除 dino 和 fred 并插入 wilma
# @removed 变成 qw( dino fred )
# @array 变成 qw( prebblees wilma barney betty )
@array = qw( pebbles dino fred barney betty );
@removed = splice @array, 1, 2 qw( wilma );
foreach¶
- 控制变量并不是列表元素的复制品,而是列表元素的本身,修改其值也就是修改元素的值。
- 当循环结束后控制变量仍是循环执行之前的值。
- 控制变量的默认值是$_,即:如果没有指定控制变量则默认使用$_
reverse¶
- 读取**列表**的值并返回次序相反的列表
sort¶
- 读取**列表**的值,并按照排序规则进行排序(默认是按照**代码点**排序)
- 数字会被当成字符串来排序
each¶
- 提取hash的**key-value**
第四章 子程序¶
格式¶
调用子程序¶
- 使用
&
符号来调用:&func_name
-
当调用者在被调用之后,可以直接使用函数名,可不使用
&
符号来调用: -
使用子程序调用的语法格式来调用,可不适用
&
符号来调用:func_name( @args )
注意:建议只有在调用内置的时候省略&
符号,其余的都加上&
符号来区分调用的是内置还是自定义子程序。
返回值¶
- 最后一次运算结果
- 单写一个return不给任何参数时:
- 标量上下文:
return undef;
- 列表上下文:
return ();
- 标量上下文:
私有变量/词法变量¶
-
临时:
-
持久(等效C中的静态变量)
- 限制:数组和hash使用state时,不能初始化。
-
参数:
@_
- 注意:Perl允许省略语句块中的最后一个分号,因为分号的作用仅仅是分隔语句,而不是必须的语句结束标记。
- 建议比屏幕长的程序都加上
use strict;
第五章 输入与输出¶
行输入操作符¶
-
唯独
while
/foreach
循环的条件表达式中**只有**行输入操作符的前提下,这个简写才起作用: -
在列表上下文中,会返回一个列表:
-
while
与foreach
的区别:在while
循环里,Perl会读取一行输入,把他存入某个变量并执行循环的主体,接下来它会回头去寻找其他的输入行。但是在foreach
循环里,行输入操作符会在列表上下文中执行(因为foreach需要逐项处理列表内容,因此会一次性读取所有输入)
钻石操作符¶
-
在不指定文件句柄的时候,默认检查@ARGV:
- @ARGV:参数列表
- $ARGV:当前正在处理的文件
- 连字符
-
:表示要从标准输入读取数据,在没有指定参数的时候,默认为连字符。
文件句柄¶
-
存在形式:虽然文件句柄可以存入标量中,但是很多时候我们写的都是应急的短小脚本,用裸字更快捷。
-
裸字:
-
标量:
-
-
建议:使用全大写来命名文件句柄
- 保留:STDIN, STDOUT, STDERR, DATA, ARGV, ARGVOUT
- 编码:
open CONFIG, '<:encoding(UTF-8)', 'dino';
- 查看perl能理解和处理的字符编码清单:
% perl -Mencode -le "print for Encode->encodings(':all')";
- 风格转换:
- DOS与Unix的转换:
open BEDROCK, '>:crlf', $file_name;
- DOS与Unix的转换:
-
以二进制方式读写文件句柄
-
问题句柄:文件结尾在标量上下文中是:
undef
- 关闭句柄:close
-
die处理致命错误:
$!
:可读的系统错误信息-
如果不像显示行号和文件名,请在错误信息结尾处加上换行符:
-
系统调用时自动检测:
use autodie;
- warn:与die用法一致,但只发出警告信息,不会终止程序的运行。
- select:改变默认的文件输出句柄
- 示例:
- 用LOG替代
STDOUT
:select LOG;
- 用LOG替代
- 无缓冲:
$| = 1;
-
print:
- 直接使用裸字:
print LOG "limestone\n";
- 无法得知$log是否是文件句柄,用{}来告知其是文件句柄:
print { $log } "limestone\n";
- 直接使用裸字:
-
注意:重新打开
STDIN
,STDOUT
,STDERR
时,Perl会恢复其默认的文件句柄。但其那题是,$^F
没有被修改的情况下。
其他综述¶
- 命令选项多余一两个:参阅“Getopt::Long”和"Getopt::Std"
- 除非会改变表达式的意义,否则Perl里的括号可以省略
-
加入print的调用看起来像函数调用,它就是一个函数调用:适用于所有函数
-
printf:格式化输出
- say:自动加"\n",通过
use feature say;
打开此函数。
第六章 hash¶
- 概念:用唯一的字符串来索引。结构为**key-value**
- 访问hash元素:$hash{key}
- 访问整个hash:%hash
- 列表上下文:
@any_array = %some_hash;
- 赋值:
my %new_hash = %old_hash
:- 上述代码会把
%old_hash
展开为**key-value**列表,然后通过列表赋值重新构造每个**key-value**,最终形成新的hash%new_hash
- 上述代码会把
-
胖箭头/胖逗号(
=>
):胖箭头与逗号等效,所有右称为胖逗号my %last_name = ( # hash也可以是词法变量 'fred' => 'flintstone', 'dino' => undef, 'barney'=> 'rubble', 'betty' => 'rubble', ); # 使用胖箭头的时候可以省略引号,左边的部分会自动被引起 my %last_name = ( fred => 'flintstone', dino => undef, barney => 'rubble', betty => 'rubble', );
- 省略引号条件:如果key只是由字母,数字和下划线组成的,并且不是以数字开头那就可以省略。这类不许引号的字符序列,我们称之为裸字,因为它们是孤立存在的。
-
keys:
- 列表上下文:返回key的列表
- 标量上下文:返回key的个数
- values
- 列表上下文:返回value的列表
- 标量上下文:返回value的个数
- each:返回
(key, value)
- 每个hash都有自己的迭代器,因此处理不同hash的each调用可以嵌套。
- 使用keys或values函数可以重置hash的迭代器。另外使用新列表重置整个hash时也可以重置迭代器,或者each调用遍历了整个hash的时候也能重置迭代器。然而在迭代hash过程中增加新的key-value就不太好,因为这不会重置迭代器,反而会迷惑开发人员,程序维护人员,另外还会愚弄each。
-
exists
-
delete
- 注意:
delete
与存入undef
是不同的,delete
后key-value便不再存在,因此exists
的结果是假,而存入undef
德华,exists
的结果会是真。
- 注意:
-
内插:不支持内插整个hash
第七章 漫游正则表达式王国¶
Unicode属性¶
- 每个字符除了字节组合外,还附带着属性信息。(可以理解为字符特征吗?)
- 具体查看文档:perluniprops
- 匹配某项属性:
\p{PROPERTY}
:匹配**PROPERTY**属性\P{PROPERTY}
:匹配非**PROPERTY**属性
量词¶
{M,N}
:匹配至少M次,最多N次。{M,}
:匹配至少M次{M}
:匹配M次*
:等同于{0,N},此处N意味着无上限+
:等同于{1,N},此处N意味着无上限?
:等同于{0,1}
分组¶
()
:捕获组,默认由序号表明。对应的序号可通过依次点算左括号(包括嵌套括号)的序号。\N
:反向引用,N为正整数\g{N}
:反向引用N
可以是负数,意味着相对于自己的位置,即往前第几个括号。N
可以是名字
$N
:使用捕获组匹配的内容,N为正整数。
多选结构¶
|
字符集¶
- 匹配但字符
[abc]
:匹配字符,且该字符是'a'或'b'或'c'[a-z]
:匹配字符,且该字符落在'a'到'z'之间[^ab]
:匹配字符,且该字符即不是'a'也不是'b'[-ab]
:匹配字符,且该字符是'-'或'a'或'b'
- 可以使用简写
[\000-\177]
:匹配任意一个7位的ASCII字符(0 ~ 127)[\w]
:ASCII的话即等效为:[-a-zA-Z0-9]
- 简写
\w
\d
\s
:匹配任意空白。perl15.6之前,\s只能匹配[\f\t\n\r]
,可以通过/\s/a
来按老的ASCII字符语义解释\h
:水平空白\v
:垂直空白\p{Space}
:匹配任意空白,等效于[\h\v]
,等效于\s
\R
:匹配断行
- 反义简写(大写)
\W
\D
\S
P{...}
- 注意:引入Unicode之后,所有的简写已经不再局限于ASCII了。可参阅:《know your character classes under different semantics》
其他综述¶
- pattern: 模式,用来表示匹配(或不匹配)某个字符串的特征模板
- 想认真学习正则表达式请参考:《Mastering Regular Expressions》-Jeffrey Fried(O'Reilly)
.
:- 匹配任意字符的通配符,除了
\n
外。 - 如果使用了Single模式,即
\s
则可以匹配\n
- 匹配任意字符的通配符,除了
第八章 用正则表达式进行匹配¶
匹配操作符:m¶
-
m
即Match,后面跟着即为分隔符 -
如果使用
/
作为分隔线,那么m
可以省略。 - 列表上下文:返回所有捕获变量的列表,如果匹配失败则返回空列表
-
通过
g
全局匹配来返回多个捕获内容 -
通过
g
全局匹配来讲捕获的字符串变成hash(列表转hash)
模式匹配修饰符¶
i
:忽略大小写s
:单行模式,可以匹配换行符m
:多行模式,^
和$
变为匹配行开头和行结尾x
:忽略空白符和注释,即要匹配空白符和#需要转义a
:ascii模式l
:类似于a,只是此时取决于本地化设定u
:Unicode模式p
:自动捕获变量的名字从$
,$&
,$'
变更为${^PREMATCH}
,${^MATCH}
,${^POSTMATCH}
锚位¶
\A
:匹配字符串的绝对开头\z
:匹配字符串你的绝对末尾\Z
:匹配行尾,即\n前^
:匹配字符串的绝对开头,等效于\A,当使用m
时,匹配行开头$
:匹配字符串的绝对末尾,等效于\z。当使用m
时,匹配行末尾\b
:单词边界,即一组连续的\w
字符开头或结尾\G
:上次匹配结束的位置
绑定操作符:=~¶
-
默认情况下模式匹配的操作对象是
$_
,绑定操作符告诉perl,拿右边的模式来匹配左边的字符串,而不是$_
捕获¶
- 捕获组存储位置:
%+
- 非捕获:
(?:...)
- 命名捕获:
(?<LABEL>...)
- 反向引用:如果有多个同名补货组时,则总是指最左边的那组
\g{LABEL}
\k<LABEL>
- 匹配区段之前的内容:
$`
- 匹配区段的内容:
$&
- 匹配区段之后的内容:
$'
优先级¶
- 圆括号(分组或捕获):
(...)
,(?:...)
,(?<LABEL>...)
- 量词:
*
,+
,?
,{n,m}
- 锚位:
abc
,^
,$
,\A
,\b
,\z
,\Z
- 多选结构:
|
- 原子:
a
,[...]
,\d
,\1
,\g{LABEL}
其他综述¶
- pattern中允许内插变量
- 保护或组的存续期:捕获变量通常能存活到下次成功匹配为止
第九章 用正则表达式处理文本¶
s/pattern/replace/modifier¶
-
作用目标必须得是个左值
-
返回值:成功真,失败假
- 分隔符可以不同
s{fred}{barney};
s[fred](barney);
s<fred>#barney#;
- modifier
g
:全局替换r
:使返回值编程替换后的结果,而保留变量中的原字符串
-
大小写转换
非贪婪模式(?)¶
- 默认情况下,所有量词都是贪婪模式(尽可能匹配多的字符)可通过在量词后面增加
?
来启用非贪婪模式(匹配尽可能少的字符)
分离字符串:split¶
- 查阅文档
将列表拼接为字符串:join¶
- 查阅文档
其他综述¶
- 读取UTF-8文档:
use open OUT => ':encoding(UTF-8)';
-
命令行直接运行:查看
perlrun
文档-
-P
:可以让Perl自动生成一端小程序,类似: -
-n
:抹掉-P
中自动执行的print
-i.bak
:把$^I
设置为".bak",如果你不想做备份的话,请直接写出-i-e
:后跟可供执行的程序
-
第十章 其他控制结构¶
- 任何能激活或停用某段程序代码的东西都算是控制结构
if¶
unless¶
- if的反义
while¶
until¶
- 直到满足条件才推出循环体
裸块控制结构¶
- 受循环控制符控制
- 可以限定临时词法标量的作用域
自增域自减:++ --¶
for¶
foreach域for的关系¶
- 如果连有两个分号,它就是for循环,如果没有分号,说明它是一个foreach循环
循环控制¶
last LABEL
:就像C语言中的break,作用于for, foreach
,while
,until
及裸块next LABEL
:就像C语言中的continue,作用于for, foreach
,while
,until
及裸块redo LABEL
:将控制返回到当前循环体的顶端,而不经过任何测试条件,也不会进入下一次循环-
continue
:在when中使用,继续检查向下执行 -
带标签的块:
- 当你需要从内层对外层循环块进行控制时,请使用标签(LABEL),标签由
\w
组成,但不能以数字开头。 - Larry建议用全大写字母命名标签。
- 当你需要从内层对外层循环块进行控制时,请使用标签(LABEL),标签由
条件操作符 "?:"¶
逻辑运算发 "and && or ||"¶
短路操作符¶
-
逻辑运算符同C一样有短路效应,但求值结果不是简单的布尔值,而是最后运算那部分表达式的值
- 注意:有个特殊情况,已定义的假值可能会导致意外的结果
-
定义或操作符:
//
- 只要发现左侧已定义时,就进行短路操作,无论该值是属于逻辑真还是逻辑假都会。
十一章 Perl模块¶
模块来源¶
- 随Perl发行版一同打包的,所以安装了Perl就可以用这些模块
- 从CPAN下载,需要自己安装
- 使用
cpan
工具 - 查看模块文档
% perldoc CGI
:查看CGI模块文档% cpan -a
:查看已安装的模块
安装模块¶
- 参考README和INSTALL文件
- 使用Perl自带模块
ExtUtils::MakeMaker
% perl Makefile.PL
% make install
- 如果没有权限安装到系统目录,在Makefile.PL后面加上INSTALL_BASE参数:
% perl Makefile.PL INSTALL_BASE=/Users/fred/lib
- 如果没有权限安装到系统目录,在Makefile.PL后面加上INSTALL_BASE参数:
- 使用另一个编译安装模块
Module::Build
% perl Build.PL
% ./Build install
- 如果没有权限安装到系统目录:
% perl Build.PL --install_base=/Users/fred/lib
- 如果没有权限安装到系统目录:
- 运行CPAN.pm的shell:
% perl -MCPAN -e shell
- 直接使用
cpan
安装(推荐,简单方便)% cpan Module::CoreList
- 使用PPM(Perl Package Manager)来安装:参见http://aspn.activestate.com/ASPN/docs/ActivePerl/faq/ActivePerl-faq2.html
- 使用轻量级安装工具:
cpanm
- 使用Perl自带模块
-
安装到工作目录
-
设置环境变量:
% perl -Mlocal::lib
-
加载环境变量:
% cpan -I Set::Crossproduct
-
设置cpan配置,让后续下载安装模块时自动放置到工作目录下:
-
加载模块
- 如果使用
local::lib
的话,则在Perl程序内部加入代码use local::lib;
- 如果安装在其他地方:
use lib qw( /Users/fred/perl5 );
- 如果使用
-
使用模块¶
- 加载模块File::Basename中的所有符号:
use File::Basename;
- 加载模块File::Basename中的符号'basename':
use File::Basename qw/ basename /;
-
不直接加载符号,但可以通过制定namespace访问
处理文件名 File::Basename¶
use File::Basename;
print 'please enter a filename: ';
chomp(my $old_name = <STDIN>);
my $dirname = dirname $old_name;
my $basename = basename $old_name;
$basename =~ s/^/not/; # 增加前缀
my $new_name = "$dirname/$basename";
rename $old_name, $new_name
or warn "Can't rename '$old_name' to '$new_name': $!";
print "$old_name, $new_name\n";
路径名拼接 File:Spec¶
use File::Basename;
use File::Spec;
print "Please enter a filename: ";
chomp(my $old_name = <STDIN>);
my $dirname = dirname $old_name;
my $basename = basename $old_name;
print "$old_name, $base_name\n";
my $new_name = File::Spec->cattle($dirname, $basename); # 将依据系统去拼接路径,比如WIN7用'\',Linux用'/'
rename $old_name, $new_name
or warn " Can't rename '$old_name' to '$new_name': $!";
print "$old_name, $new_name\n";
更友好的处理路径 PATH::Class¶
my $dir = dir( qw(Users fred lib) ); # Users/fred/lib/perl5
my $subdir = $dir->subdir( 'perl5' );
my $parent = $dir->parent; # Users/fred
my $windir = $dir->as_foreign( 'Win32' ); # Users\fred\lib
CGI¶
use CGI qw/ :all /; # 加载分组all,模块可以有不同的分组,比如::cgi,:html5
print header(),
start_html('This is the page title');
h1('Input parameters');
my $list_items;
foreach my $param ( param() )
{
$list_items .= li( "$param: " . param($param) );
}
print ul( $list_items );
print end_html();
数据库接口 DBI¶
- 参考书籍:《Programing the Perl DBI》-Alligator Descartes&Time Bunce(O'Reilly)
- 参考站点:http://dbi.perl.org/
处理日期和时间 DateTime¶
Time::Piece¶
第十二章 文件测试¶
测试操作符¶
- 参考文档:
% perldoc -f X
- 建议去了解对应系统的具体内容,比如Linux/Unix下的文件,可阅读《高级环境编程》等。
-
如果没有写操作目标(文件名/文件句柄)的话,默认为
$_
。-t
:对文件名无效,所以它的默认值是STDIN
-
虚拟文件句柄
_
:用上次查询过的文件信息来做当前测试 -
栈式写法:
if (-w -r $file) {...}
,先测试-r
,后测试-w
-T
和-B
:测试某个文件是文本文件还是二进制文件。 > 但是对文件系统有一点经验的人都知道,(至少在Unix类似的操作系统下)没有任何地方会告诉你它是二进制文件还是文本文件,那么Perl是如何办到的呢?答案是Perl会作弊: > > 1. 先打开文件,检查开头的几千个字节,然后作出一个合理的猜测。 > 2. 如果它看到了很多空字节、不寻常的控制字符,而且还设定了高位(即第8位是1)的字节,那么它看起来就是**二进制文件** > 3. 如果文件里没有许多奇怪的东西,而且它看起来像文本文件,那就猜测为**文本文件** > > 因此,它可能会有猜错的时候,所以这种猜测并不完美,不过如果你只想把编译过的文件和源文件分开,或是将HTML文件和PNG文件分开,那么这两种测试操作符还算够用。 > 你可能会认为-T
和-B
出现的结果必定相反,因为文件若不是文本文件,就该是二进制文件。但是,有两种特殊情况会让测试结果相同: > > 1. 如果文件不存在,两则都会返回**假**。因为它即不是文本文件也不是二进制文件。 > 2. 在空文件的情况下,两者都会返回**真**,因为它即是空的文本文件也是空的二进制文件。
stat和lstat函数¶
localtime和gmtime¶
位运算符¶
- 按位与:
10 & 12
- 按位或:
10 | 12
- 按位异或:
10 ^ 12
- 按位左移:
6 << 12
- 按位右移:
25 >> 2
-
按位取反:
~10
-
注意:加入按位运算操作符的任何一个操作数是字符串,则perl会把它当成位字符串来处理:
"\xAA" | "\x55"
:结果为"\xFF"
十三章 目录操作符¶
chdir¶
- 功能:改变当前的工作目录
- 如果调用chdir时不加参数,会回到自己的用户主目录,并试着将工作目录设成主目录。
- 可以使用第三方模块:
FILE::HomeDir
文件名通配符¶
- 程序内部使用通配符
glob
:my @all_files = glob '*.pm';
glob
操作符的效果之所以和shell完全相同,是因为在Perl5.6以前它只不过是在后台调用/bin/csh来展开文件名。因此文件统配非常耗时,而且可能在目录太大时崩溃。当然,如果是新版本则无需担心此问题。
-
使用
<>
调用my @all_files = <*>; # 效果等效于:my @all_files = glob "*"; my $dir = '/etc'; my @dir_files = <$dir/* $dir/.*>; # 这里的点号不具有正则效果
- 当使用
<>
时,perl如何区分文件句柄和文件名通配的? > 因为合理的文件句柄必须是严格意义上的Perl标识符,所以如果<>
内满足Perl标识符条件的,就作为文件句柄来读,否则以文件名通配操作。 > > - 文件名通配操作 > > > > - 文件句柄 > > > > 注意: > > 1. Perl会在编译阶段决定它是文件名通配操作还是从文件句柄读取,因此和变量的内容无关。 > 2. 如果间接文件句柄是一个文本字符串,那么在use strict的情况下降它交给“符号引用”来测试是不允许的。
- 当使用
目录句柄 opendir readdir closedir¶
my $dir_to_process = '/etc';
opendir my $dh, $dir_to_process
or die "Can't open $dir_to_process: $!";
foreach $file (readdir $dh)
{
print "one file in $dir_to_proces is $file\n";
}
close $dh;
递归访问目录 File::Find¶
-
其他模块:
File::Find:Rule
、File::Finder
-
% find2perl . -name '*.pm'
#!/usr/bin/perl -w eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' if 0; #$running_under_some_shell use strict; use File::Find qw(); # Set the variable $File::Find:: don't use nlink if you're using AFS # since AFS cheats # for the convenience of &wanted calls, including -eval statements; use vars qw/*name *dir *prune/; *name = *File::Find::name; *dir = *File::Find::dir; *prune = *File::Find::prune; sub wanted; # Traverse desired filesystems File::Find::find({wanted => \&wanted}, '.') exit; sub wanted { /^.*\.pm\z/s && print ("$name\n"); }
删除文件 unlink¶
重命名/移动文件 rename¶
- 重命名:
rename 'old.txt' => 'new.txt';
- 移动:
rename 'over_there/some/place/some_file' => 'some_file';
-
批量改名
链接 link symlink readlink¶
- 硬链接:
link
- 软链接:
symlink
- 读取软链接的真实位置:
readlink
创建/删除目录 mkdir rmdir File::Temp File::Path¶
修改权限 chmod File::chmod¶
修改隶属关系 chown¶
修改时间戳 utime¶
第十四章 查找子字符串¶
查找字符串 index¶
反向查找字符串 rindex¶
操作字符串 substr¶
格式化字符串 sprintf¶
- hex
- oct
排序 sort¶
my @result = sort &bynumber, @array;
sub by_number {
if ($a < $b) { -1 } elsif ($a > $b) { 1 } else { 0 };
}
-
比较:
- 数字
<=>
:$a <=> $b;
- 字符串
cmp
:$a cmp $b;
- 数字
-
注意:出于性能考虑,\(a和\)b并非数据项拷贝,实际上他们是原始列表元素的别名,所以切勿中途改变他们。
第十五章 智能匹配与given-when结构¶
- 智能匹配符:
~~
,可查看文档,"perlop"和"perlsyn"- 用于判断事物是否相同(或则差不多相同),所以在需要比较大小时,就不能用智能匹配了,此时还是用传统的比较符好。
-
given结构:对应C语言的switch
use 5.010001; # 至少是5.10.1版本 use experimental qw( smartmatch switch ); # 使用实验性功能,智能匹配和given-when结构 # 将参数放入$_,同时每个when条件都尝试用表达式匹配对$_做测试 given( $ARGV[0] ) { say "\nProcessing $_" # 可以加入调试信息 when ( 'Fred' ) { say 'Name is Fred'; } # when中等效于:$_ ~~ 'Fred' when ( /\AFred/ ) { say 'Name starts with Fred'; continue } # 类似于C语言中未加入break,继续执行下面的when when ( /fred/i ) { say 'Name has fred in it'; } say "into default" # 加入调试信息 default { say "I don't see a Freds"; } }
-
在foreach中使用when
-
笨拙匹配:正常的判断真假,而不是用智能匹配符去测试$_
- 在when中调用子程序/内置函数
- 否定表达式,包括否定的正则表达式
第十六章 进程管理¶
- 建议:在此不做详细解读,请阅读《UNIX高级环境编程》
system¶
system 'date';
# 通过使用参数来避免使用shell
my $tarfile = 'something*wicked.tar';
my @dirs = qw( fred|flintstone <barney&rubble> betty );
system 'tar', 'cvf', $tarfile, $tarfile, @dirs; # 也就是说参数最后回被引号括起来,并不会被shell解析,具体请查看perlfunc
Unix中,退出值0表示正常,非0表示有问题¶
环境变量:%ENV
¶
exec¶
反引号¶
- 注意:如果不需要返回值,请用
system
替代反引号,提高效率 - 上下文
- 标量上下文:返回一个很长的字符串,包括换行符
- 列表上下文:返回一个按行拆分的列表
qx¶
qx( perldoc -t -f $_ )
:等效于`perl perldoc -t -f $_`
- 好处
- 避免对反引号转义
- 如果选用
'
(单引号)作为分隔符的话,可以禁止变量内插:qx'perldoc -t -f $_'
,此时$_
不会被perl解释
IPC::System::Simple¶
- 执行外部命令时,不会通过shell调用,所以不会碰到shell导致意外的状况
- 通过capture或capturex就可以捕获外部命令的输出了
use IPC::System::Simple qw( system systems captures )
my $tarfile = 'something*wicked.tar';
my @dirs = qw( fred|flintstone <barney&rubble> betty );
system 'tar', 'cvf', $tarfile, @dirs;
systemx 'tar', 'cvf', $tarfile, @dirs;
my @output = captures 'tar', 'cvf', $tarfile, @dirs;
通过文件句柄执行外部进程¶
- 输出重定向:
open DATE, 'date|' or die "can't pipe from date: $!"
; - 输入重定向:
open MAIL, '|mail merlyn' or die "can't pipe to mail: $!"
-|
:读取的文件句柄,-
相当于外部命令的占位符|-
:写入的文件句柄,-
相当于外部命令的占位符
open my $date_fh, '-|', 'date' or die "can't pipe from date: $!"
open my $mail_fh, '|-', 'mail merlyn' or die "can't pipe to mail: $!"
my $now = <$date_fh>; # 开始执行并读取响应结果
print $mail_fh "the time is now $now"; # 写入邮件,假设$now以换行符结尾
- 注意:如果外部进程在连接到某个以读取模式打开的文件句柄后自行退出运行,那么这个文件句柄就会返回文件结尾标识符,好像已经读完了正常文件一样。当你关闭用来写入数据到某个进程的文件句柄时,该进程会读到文件结尾标识符。所以,要结束邮件的发送,只需要关闭这个文件句柄即可。
-
结束状态会存入
$?
,0表示成功,非0表示失败。每个进程结束的时候都会覆盖掉前一个返回值,所以,需要这个值的话请尽快保存。 -
$?
也会存储system
和反引号圈引的命令的返回值
fork exec waitpid¶
defined( my $pid = fork ) or die "Cannot fork: $!";
unless ( $pid )
{
# subprocess
exec 'date';
die "can't exec date: $!";
}
# parent process
waitpid( $pid, 0 );
信号、kill¶
-
发送信号:
kill
-
signal 0:测试能否向指定进程发信号,但并不是真的发送
-
信号处理函数:
%SIG
。key是非前缀信号名,如INT。value是信号处理函数名。sub clean_up; sub my_int_handler; my $temp_directory = "/tmp/myprog.$$"; mkdir $temp_directory, 0700 or die "can't create $temp_directory: $!"; $SIG{INT} = 'my_int_handler'; sub clean_up { unlink glob "$temp_directory/*"; rmdir $temp_directory; } sub my_int_handler { $clean_up(); die "interrupted, exiting...\n"; }
-
注意:Perl会立即处理SIGKILL、SIGBUS以及SIGSEGV这些信号,所以对进程运行而言,这些信号可能会造成不安全。
第十七章 高级Perl技巧¶
切片¶
- 切片后的结果是:列表
- 切片和列表的区别:切片可以内插到字符串中,但列表不行
-
列表切片
-
数组切片
-
理解变量的前置符号及后置括号
- 前置符号:
$
:一个元素@
:一组元素%
:一整个hash
- 后置括号:
[]
:访问列表(切片){}
:访问hash元素
- 前置符号:
-
hash切片
eval¶
- 在
eval
块中出现致命错误时会立即停止运行整个块,然后退出继续运行其余的代码。 eval
是一个表达式,所以末尾需要一个分号。-
eval
的返回值是最后一条表达式的执行结果,如果捕获到错误则返回undef或则(),同时将错误信息存于$@
变量中。 -
eval
无法捕获到的错误- 源码中的语法错误
- perl解释器本身崩溃的错误
- 警告
- 调用exit退出
-
由于
$@
是一个特殊变量,而你所写的eval
也许会被另一个高层的eval包裹,所以就要确保出现的错误不干扰高层出现的错误
Try::Tiny¶
-
为了避免干扰
$@
,Try::Tiny
把错误消息放到了默认变量$_
里。
grep¶
- 格式:
@dst_list grep { selector } @src_list;
grep
会将@src_list
中的每个元素放到$_
中并执行代码块selector,最后将返回真的元素以列表的形式返回,即@dst_list
- 由于
$_
是@src_list
中元素的别名,所以不要修改$_
- 标量上下文的
grep
返回符合过滤条件的元素个数
map¶
-
于grep区别
map
使用代码块中的最后一个表达式的实际计算结果,所以最终会返回一个这样的结果组成的列表。map
使用的表达式(selector)是在一个列表上下文环境中,意味着可以一次返回多个元素的结果
List::Util¶
- 高效处理列表的工具