Perl学习笔记(1)-基础语法

前言

在生物信息学方面,Python,R,Perl,Linux我觉得是必须要掌握的4个工具,以前学习一些Perl,只是不系统,笔记记得有点散,没有把它们总结起来。现在总结一下Perl的学习笔记。参考资料主要为:一、Perl教程|菜鸟教程(这个是主要的,思路非常简洁,并且把原代码列了出来,非常适应初学者);二、O’Reilly:Perl语言入门(第6版)(中文版)(刚开始是想看小骆驼这本书的,但后来发现在案例操作上写得不太好);三、百度搜索辅助。Linux的Ubuntu发行版中自带了Perl语句,输入perl -v就能够查看到Perl的版本,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
biotest@ubuntu:~/perl$ perl -v
This is perl 5, version 22, subversion 0 (v5.22.0) built for x86_64-linux-thread-multi
Copyright 1987-2015, Larry Wall
Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.
Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl". If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

交互式编程

在Linux的shell中可以直接使用Perl进行交互式编程 ,格式为perl -e 代码块,如下所示:

1
2
biotest@ubuntu:~/perl/02datatype$ perl -e 'print "Hello,world\n"'
Hello,world

一个简单的Perl程序

先看一个简单的Perl程序,在Ubuntu中输入vim helloworld.pl建立一个perl程序,如下所示:

1
2
3
#!/usr/bin/perl -w
print "Hello, world!\n";
# this is comment

从这一段代码中我们可以了解到这些内容:

  1. 第一行的#!/usr/bin/perl是告诉系统,Perl的解释器位于哪里,这一段代码不要省略,-w不是必须的,加上-w参数表示提示警告;
  2. 每次输入一行代码后,要以分号(;)结尾;
  3. Perl程序用井号(#)作为注释。

Esc键,输入wq,保存退出。在命令行模式输入perl first.pl后,按回车就可以运行,显示结果。再次按回车,运行结束,运行结果如下所示:

1
2
3
4
5
biotest@ubuntu:~/perl$ perl first.pl
This is my first perl program
biotest@ubuntu:~/perl$

其实Perl语句的扩展名也可以不用,如下所示:

1
2
3
4
5
6
biotest@ubuntu:~/perl$ mv helloworld.pl helloworld2
biotest@ubuntu:~/perl$ perl helloworld2
This is my first perl program
biotest@ubuntu:~/perl$

也可以更改权限,直接执行,如下所示:

1
2
3
4
5
6
biotest@ubuntu:~/perl$ chmod u+x helloworld2
biotest@ubuntu:~/perl$ ./helloworld2
This is my first perl program
biotest@ubuntu:~/perl$

注释

Perl程序除了使用井号作为单行注释外,还可以使用POD(Plain Old Documentations)来进行多行注释,格式如下所示:

1
2
3
4
5
6
=pdo 注释
这是一个多行注释
这是一个多行注释
这是一个多行注释
这是一个多行注释
=cut

看一个Perl程序(命令为multiple_comment.pl),如下所示:

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/perl
print "This is my perl program \n";
=pdo
This is a multiple annotation;
THis is a multiline commnet;
=cut
$a=<>;
print $a;

运行结果如下所示:

1
2
3
4
5
biotest@ubuntu:~/perl$ perl multiple_comment.pl
This is my perl program
biotest@ubuntu:~/perl$

使用POD注释需要注意的是,①=pod、 =cut只能在行首;②以=开头,以=cut结尾;③=后面要紧接一个字符,=cut后面可以不用。

Here文档

Here可以向命令中传递大量的字符串,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/perl
$a=10;
$var = <<"EOF";
This is a Here Document instance using double quotation marks.
You can input soma character and variables,for example:
a = $a
EOF
print "$var\n";
$var=<<'EOF';
This is another Here Document instance using single quotation marks.
for example:a=$a
EOF
print "$var\n";

运行结果如下所示:

1
2
3
4
5
6
7
biotest@ubuntu:~/perl/01basic$ perl here.pl
This is a Here Document instance using double quotation marks.
You can input soma character and variables,for example:
a = 10
This is another Here Document instance using single quotation marks.
for example:a=$a

从结果可以看出,here文档可以向一条命令传递大量的字符串,并且这些字符串中如果有变量,它也会显示出来。它的用法为:

  1. 必须后接分号,否则编译通不过。
  2. END可以用任意其它字符代替,只需保证结束标识与开始标识一致。
  3. 结束标识必须顶格独自占一行(即必须从行首开始,前后不能衔接任何空白和字符)。
  4. 开始标识可以不带引号号或带单双引号,不带引号与带双引号效果一致,解释内嵌的变量和转义符号,带单引号则不解释内嵌的变量和转义符号。
  5. 当内容需要内嵌引号(单引号或双引号)时,不需要加转义符,本身对单双引号转义。

Perl关于空格的处理

Perl 解释器不会关心有多少个空白,以下程序也能正常运行,如下所示:

1
2
3
4
5
6
biotest@ubuntu:~/perl$ cat blank.pl
#!/usr/bin/perl
print "Hello, world\n";
biotest@ubuntu:~/perl$ perl blank.pl
Hello, world
biotest@ubunt

但是如果出现了两行,就会原样输出,如下所示:

1
2
3
4
5
6
#!/usr/bin/perl
print "Hello
world\n";
biotest@ubuntu:~/perl$ perl blank2.pl
Hello
world

其实这个也好理解,因为换行的时候有可能引入了一个换行符(虽然看不到),与原来只在一行时,中间有几个空格已经不同了。

单引号和双引号

双引号可以正常解析一些转义字符与变量,而单引号无法解析会原样输出,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
biotest@ubuntu:~/perl$ cat quotation.pl
#!/usr/bin/perl
print "Hello, world\n";
print 'Hello, world\n';
$a=10;
print "a=$a\n";
print 'a=$a\n';
print "this is the end"
biotest@ubuntu:~/perl$ perl quotation.pl
Hello, world
Hello, world\na=10
a=$a\nthis is the endbiotest@ubuntu:~/perl$

从结果可以看出,使用双引号,可以使用转义字符,如果是单引号,则无法使用转义字符,另外,还能发现,如果结尾没有转义字符,输出的内容就会连成一片,形成一行,不太美观,因此在前面的案例中,多数使用双引号,并在结尾部分引入\n转义字符。

案例分析

现在看一个实际运用中的案例,代码(脚本文件名为example.pl),如下所示:

1
2
3
4
5
6
#!/usr/bin/perl
@lines=`perldoc -u -f atan2`;
foreach(@lines){
s/\w<([^>]+)>/\U$1/g;
print;
}

在运行之前,先看一下直接在bash中运行perldoc -u -f atan2的结果,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
biotest@ubuntu:~/perl/01basic$ perldoc -u -f atan2|cat
=over 8
=item atan2 Y,X
X<atan2> X<arctangent> X<tan> X<tangent>
=for Pod::Functions arctangent of Y/X in the range -PI to PI
Returns the arctangent of Y/X in the range -PI to PI.
For the tangent operation, you may use the C<Math::Trig::tan>
function, or use the familiar relation:
sub tan { sin($_[0]) / cos($_[0]) }
The return value for C<atan2(0,0)> is implementation-defined; consult
your atan2(3) manpage for more information.
Portability issues: L<perlport/atan2>.
=back

再次运行刚才那段代码(代码名称为example.pl),如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
biotest@ubuntu:~/perl/01basic$ perl example.pl |cat
=over 8
=item atan2 Y,X
ATAN2 ARCTANGENT TAN TANGENT
=for Pod::Functions arctangent of Y/X in the range -PI to PI
Returns the arctangent of Y/X in the range -PI to PI.
For the tangent operation, you may use the MATH::TRIG::TAN
function, or use the familiar relation:
sub tan { sin($_[0]) / cos($_[0]) }
The return value for ATAN2(0,0) is implementation-defined; consult
your atan2(3) manpage for more information.
Portability issues: PERLPORT/ATAN2.
=back

现在解释一下原来的代码(example.pl):

第一行:#!/usr/bin/perl:这是告诉Ubuntu,perl解释器位于/usr/bin/perl(每台电脑可能有所不同,但是必须是perl解释器所在的地址);

第二行:@lines=`perldoc -u -f atan2` :这里有使用了一对反引号(` )来调用外部的命令,即perldoc -u -f atan2,在bash中直接运行这条命令,出来的结果是一文字,这个命令是用于阅读Perl及相关扩展和工具程序的说明文档,这里显示的是在Perl中如何使用三角函数atan2的一些信息。反引号输出的结果会一行行依次被储存在@lines这个数组变量中,接着代码启动了一个循环(这里的foreach就相当于bash中的for),依次对每行数据进行处理,如下所示:

1
2
3
4
foreach(@lines){
s/\w<([^>]+)>/\U$1/g;
print;
}

第三到六行:其中最主要的是这么一段奇怪的符号,即s/\w<([^>]+)>/\U$1/g,它的大概意思是:对每个包含一对尖括号(<>)的行进行相应的数据替换操作(跟bash shell中的sed有点类似)。

Perl帮助文档的查阅

Perl的帮助文档的函数是perldoc -f 指令perldoc -m 模块,或者是perldoc perldoc(这个是教你怎么使用Perldoc),在使用perl -f之前,需要安装perldoc,否则会报错,如下所示:

1
You need to install the perl-doc package to use this program

安装Perldoc的代码为:

1
sudo apt-get install perl-doc

现在使用一下帮助,查询一下opendir这个函数,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
biotest@ubuntu:~/perl/03condition$ perldoc -f opendir|cat
opendir DIRHANDLE,EXPR
Opens a directory named EXPR for processing by "readdir",
"telldir", "seekdir", "rewinddir", and "closedir". Returns true if
successful. DIRHANDLE may be an expression whose value can be used
as an indirect dirhandle, usually the real dirhandle name. If
DIRHANDLE is an undefined scalar variable (or array or hash
element), the variable is assigned a reference to a new anonymous
dirhandle; that is, it's autovivified. DIRHANDLEs have their own
namespace separate from FILEHANDLEs.
See the example at "readdir".

再查询一下MIME::Base64这个包,如下所示:

1
2
3
4
5
6
7
8
9
10
biotest@ubuntu:~$ perldoc -m MIME::Base64|head
package MIME::Base64;
use strict;
use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(encode_base64 decode_base64);
@EXPORT_OK = qw(encode_base64url decode_base64url encoded_base64_length decoded_base64_length);

Perl的包

Perl 中每个包有一个单独的符号表,定义语法如下所示:

1
package mypack;

这段代码定义了一个名为mypack的包,在此后定义的所有变量和函数的名称都储存在这个包关联的符号表中,直到遇到另外一个package为止。Perl中的这个包我觉得有点类型Python中的模块。

每个符号表有其自己的一组变量、函数,各组名字是不相关的,因此可以在不同的包中使用相同的变量名,而代表的是不同的变量。从一个包中访问另外一个包的变量,可通过包名+双冒号(::)+变量名的方式。存贮变量和函数的名字的默认符号表是与名为main的包相关联的。如果在程序里定义了其它的包,当你想切换回去使用默认的符号表,可以重新指定main包,如下所示

1
package main;

这样,接下来的程序就好象从没定义过包一样,变量和子程序的名字象通常那样存贮。以下实例中文件有mainFoo包。特殊变量__PACKAGE__用于输出包名,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
biotest@ubuntu:~/perl/01basic$ cat package.pl
#!/usr/bin/perl
# This is "main" package
print "The name of package is: ",__PACKAGE__," $i\n";
package Foo;
# This is "Foo" package
$i=10;
print "The name of package is: ",__PACKAGE__," $i\n";
package main;
# Redirect "main" package
$i =100;
print "The name of package is: ",__PACKAGE__, " $i\n";
print "The name of package is: ",__PACKAGE__," $Foo::i\n";
1;
biotest@ubuntu:~/perl/01basic$ perl package.pl
The name of package is: main
The name of package is: Foo 10
The name of package is: main 100
The name of package is: main 10

代码及结果解释:perl中默认的包是main包,因此在开头并没有定义这个main包,接着定义了一个Foo包,最后为了切换回main包,又需要对mian包重新指定。末尾要添加1;执行返回 TRUE,这是必须的,否则返回错误。

BEGIN和END模块

Perl语言提供了两个关键字:BEGIN,END。它们可以分别包含一组脚本,用于程序体运行前或者运行后的执行,使用格式如下所示:

1
2
3
4
BEGIN { ... }
END { ... }
BEGIN { ... }
END { ... }
  1. 每个BEGIN模块在Perl脚本载入和编译后但在其他语句执行前执行。
  2. 每个END语句块在解释器退出前执行。
  3. BEGINEND语句块在创建Perl模块时特别有用。

看下面的案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
biotest@ubuntu:~/perl/01basic$ cat begin_end.pl
#!/usr/bin/perl
package Foo;
print "Begin and Block instance\n";
BEGIN{
print "This is BEGIN statements blocks\n";
}
END {
print "This is END statements blocks\n";
}
1;
biotest@ubuntu:~/perl/01basic$ perl begin_end.pl
This is BEGIN statements blocks
Begin and Block instance
This is END statements blocks

这段代码定义了一个叫Foo的包,在运行这个包的时候,先执行了BEGIN的代码,在运行这个包之后,又执行了END这一部分的代码。

Perl模块的创建

Perl模块是一个可重复使用的包,模块的名字与包名相同,定义的文件后缀为.pm。以下我们定义了一个模块Foo.pm,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
biotest@ubuntu:~/perl/01basic$ cat module.pl
#!/usr/bin/perl
package Foo;
sub bar {
print "Hello $_[0]\n";
}
sub blat {
print "World $_[0]\n";
}
1;

关于这段代码以及Perl模块的解释:

  1. 末尾1;执行返回TRUE,这是必须的,否则返回错误。
  2. 使用函数require和use载入一个模块。
  3. requireuse函数调用eval函数来执行代码。
  4. @INC是Perl内置的一个特殊数组,它包含指向库例程所在位置的目录路径。

包的调用函数require和use

Perl使用require函数来调用模块,如下所示:

1
2
3
4
5
6
#!/usr/bin/perl
require Foo;
Foo::bar("a");
Foo::blat("b");

Perl也可以使用use函数来调用要共模块,如下所示:

1
2
3
4
5
6
#!/usr/bin/perl
use Foo;
bar("a");
blat("b");

从这两个案例可以看出这些内容:

  1. require用于载入module或perl程序(.pm后缀可以省略,但.pl必须有);
  2. Perl use语句是编译时引入的,require是运行时引入的;
  3. Perl use引入模块的同时,也引入了模块的子模块。而require则不能引入,要在重新声明;
  4. USE是在当前默认的@INC里面去寻找,一旦模块不在@INC中的话,用USE是不可以引入的,但是require可以指定路径;
  5. USE引用模块时,如果模块名称中包含::双冒号,该双冒号将作为路径分隔符,相当于Unix下的/或者Windows下的\。 例如use MyDirectory::MyModule

通过添加以下语句 use 模块就可以从模块中导出列表符号:

1
2
require Exporter;
$ISA=qw(Exporter);

@EXPORT数组包含默认导出的变量和函数的名字,如下所示:

1
2
3
4
5
6
7
8
9
10
11
package Module;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(bar blat); # 默认导出的符号
sub bar { print "Hello $_[0]\n" }
sub blat { print "World $_[0]\n" }
sub splat { print "Not $_[0]\n" } # Not exported!
1;

Perl模块的安装

由于Perl本身只带了核心的Perl模块数,如果要用到其他的Perl模块,还需要到CPAN上下载。CPAN是Perl的一个公共模块仓库,英文全称为Comprehensive Perl Archive Network。搜索相应模块的地址为:http://search.cpan.org/。Linux下安装Perl模块有两种方法:手工安装和自动安装。

手工安装模块

手工安装需要从CPAN上下载所需要的模块,手工编译、安装,如下所示:

1
2
3
4
5
tar xvfz 某个模块.tar.gz
cd 解压后的目录
perl Makefile.PL
make
make install

整个过程与Ubuntu编译安装软件类似,过程就是这个样子:

  1. 首先运行 perl Makefile.PL在当前目录生成 Makefile
  2. 然后运行 “make” 编译并创建所需的库文件;
  3. 之后用 “make test” 测试编译结果是否正确;最后运行 “make install” 将库文件安装到系统目录,至此整个编译过程结束。

自动安装模块

第二种方法是联网安装,需要使用CPAN的模块自动完成下载、编译、安装,过程如下所示:

1
2
3
perl -MCPAN -e shell #进入cpan
install 包名
q 退出

Perl模块的查看

第一种方法:使用perldoc + 模块名

有时候需要查看Perl的某个模块是否已经安装了,此时使用perldoc直接查看这个模块的文档即可,如下所示:

1
perldoc Carp # Check Carp module installed

第二种方法:perldoc + perllocal

1
perldoc Carp

第三种方法:使用instmodsh函数

直接在Shell中输入instmodsh,此时会出现命令提示行,如下所示:

1
2
3
4
5
Available commands are:
l - List all installed modules
m <module> - Select a module
q - Quit the program
cmd?

系统会提示你输入相应的代码,输入l是查看已经安装的模块,输入m 模块名,查看具体的模块,输入q是退出。