【Perl】基础

综述

官网

Perl语言官网:<The Perl Programming Language - www.perl.org>。

简述

Perl语言的应用范围很广,除CGI以外,Perl被用于图形编程、系统管理、网络编程、金融、生物以及其他领域。由于其灵活性,Perl被称为脚本语言中的瑞士军刀。

Perl语言的优点:

  • 相比C、Pascal这样的“高级”语言而言,Perl语言直接提供泛型变量、动态数组、Hash表等更加便捷的编程元素;
  • Perl具有动态语言的强大灵活的特性,并且还从C/C++、Basic、Pascal等语言中分别借鉴了语法规则,从而提供了许多冗余语法;
  • 在统一变量类型和掩盖运算细节方面,Perl做得比其他高级语言(如:Python)更为出色;
  • 由于从其他语言大量借鉴了语法,使得从其它编程语言转到Perl语言的程序员可以迅速上手写程序并完成任务,这使得Perl语言是一门容易用的语言;
  • Perl语言是可扩展的,我们可以通过CPAN(Comprehensive Perl Archive Network)中心仓库找到很多我们需要的模块;
  • Perl语言的mod_perl模块允许Apache Web服务器使用Perl解释器。

Perl语言的缺点:

  • Perl语言的灵活性和“过度”的冗余语法,使得许多Perl程序的代码令人难以阅读和维护(变成了“Write-Only”);
  • Perl语言随意的特点,可能会导致一些Perl程序员遗忘语法,以至于不得不经常查看Perl手册;

建议的解决方法是在程序里使用use strict;以及use warnings;,并统一代码风格,使用库,而不是自己使用“硬编码”。

Perl擅长处理整体来说“约有90%与文字处理有关,10%与其它事务有关”的问题。

Perl是编写小型CGI的最佳语言。

Perl的注释用#,没有块注释。

Perl程序并不需要变量声明的部分。

源代码使用Unicode书写的话,需要使用use utf8;,除非有明确的原因不加,否则就应该用上。

下载和安装

下载Perl编译环境,需要注意的是Windows版本下有两种编译环境:

在这里插入图片描述

就目前的使用来看没法确定差别,暂时下载Strawberry Perl,因为它不需要账户就可以下载。下载之后安装,安装之后可以通过如下的方式查看:

在这里插入图片描述

表示已经安装成功。

HelloWorld

代码位于Basic\HelloWorld.pl:

#!/usr/bin/perl 

use utf8;

print "Hello, World!\n";

执行的结果:

E:\Gitee\perl\Code\Basic>HelloWorld.pl
Hello, World!

E:\Gitee\perl\Code\Basic>

参考书

《Learning Perl》(《Perl语言入门》)

《Internediate Perl》(没中文)

《Mastering Perl》(《精通Perl》)

《Programming Perl》(《Perl语言编程》)

其它

一般P大写的Perl表示程序语言,P小写的perl表示实际编译并运行的解释器。

基本语法

标量数据

标量是Perl里面最简单的一种数据类型,分为数字字符串(其实还有undef)。

  • 数字分为整型浮点型,但是在Perl内部都是浮点型,整型也是由浮点型表示的。
  • 字符串可以用单引号双引号,双引号中的反斜杠更为强大,可以转义许多控制字符:
    • 单引号中的\n是两个字符;
    • 双引号中的\n是换行符;
    • 诸如此类的差异。

数字和字符串会自动转换,如何转换主要依赖于操作符。

字符串操作

连接操作用.

#!/usr/bin/perl 

use utf8;
use warnings;

print "hello" . " " . "world";  # 点号用来连接

字符串重复使用x,其实就是小写的x字符:

#!/usr/bin/perl 

use utf8;
use warnings;

print "hello" x 3;

注意下面的例子:

#!/usr/bin/perl 

use utf8;
use warnings;

print 5 x 4.8   # 打印结果5555

因为使用了x所以是字符串重复操作,而该操作左侧必须是字符串,所以5被转换成了"5",右侧的数字又会被取整,所以变成了4,最终就输出了"5555"。当重复次数小于1时会返回空字符串:

#!/usr/bin/perl 

use utf8;
use warnings;

print "hello" x 0.1;    # 打印为空

标量变量

标量变量是单单存储一个值的变量。

标量变量名以美元符号开头

#!/usr/bin/perl 

use utf8;
use warnings;

$hello = "Hello";   # 定义变量
$space = " ";       # 定义变量
$world = "world";   # 定义变量
print $hello, $space, $world;   # print后面的参数可以用逗号隔开,实际上是个列表

变量可以内插,即把字符串内出现的所有标量变量替换成变量当前的值:

#!/usr/bin/perl 

use utf8;
use warnings;

$hello = "hello";
print "$hello world";   # 变量内插

如果变量没有被赋值,就会用空字符串来替换。

如果只要打印变量值,则不需要使用变量内插:

#!/usr/bin/perl 

use utf8;
use warnings;

$hello = "hello";
print $hello;   # 变量内插,这种情况可以不需要要双引号,不过也可以加

变量内插只适用于双引号:

#!/usr/bin/perl 

use utf8;
use warnings;

$hello = "hello";
print "$hello"; # 打印hello
print '$hello'; # 打印$hello,多一个$

内插的变量会以最长且合法的方式获取:

#!/usr/bin/perl 

use utf8;
use warnings;

$hello = "hello";
$helloworld = "hello world";
print "$helloworld";

如果想使用$hello,可以用大括号{}括起来:

#!/usr/bin/perl 

use utf8;
use warnings;

$hello = "hello";
$helloworld = "hello world";
print "${hello}world";	# 使用{}

代码点创建字符

chr()函数可以将数字转换成字符:

#!/usr/bin/perl 

use utf8;
use warnings;

print chr(0x30);    # 0的ASCII是0x30,所以这里打印0
print chr(0x31);    # 1的ASCII是0x30,所以这里打印1

ord()执行相反的操作:

#!/usr/bin/perl 

use utf8;
use warnings;

print ord('0'); # 打印48

布尔值

  • 如果是数字,0为假,其它为真;
  • 如果是字符串,空字符串(""'')为假,其它为真(有例外,见下面);
  • 如果既不是数字也不是字符串,那就先转换成数字或者字符串再行判断;
  • 字符串’0’和数字0是同一个标量值,所以都为假:
#!/usr/bin/perl 

use utf8;
use warnings;

if (0) {
    print "0 is true\n";
} else {
    print "0 is false\n";   # 执行到这里
}

if ('0') {
    print "'0' is true\n";
} else {
    print "'0' is false\n"; # 执行到这里
}

if ('') {
    print "'' is true\n";
} else {
    print "'' is false\n";  # 执行到这里
}
  • undef也是假
#!/usr/bin/perl 

use utf8;
use warnings;

if (undef) {
    print "undef is true\n";
} else {
    print "undef is false\n";   # 执行到这里
}

如果有else if,则使用elsif,注意少一个e

前面的布尔值可用于if,还可以用于while

#!/usr/bin/perl 

use utf8;
use warnings;

$count = 2;
while ($count) {
    $count -= 1;
    print "count: $count\n";
}

获取用户输入

“行输入”操作符是<STDIN>

#!/usr/bin/perl 

use utf8;
use warnings;

$input = <STDIN>;
print "input: ", $input;

<STDIN>默认(通过$/存放换行符)在遇到换行符之后退出。

<STDIN>返回的字符中包含换行符。

chomp()函数作用于字符串,用于去掉字符串末尾的换行符(注意只去掉行尾)并返回:

#!/usr/bin/perl 

use utf8;
use warnings;

$input = <STDIN>;
chomp $input;
print "input: ", $input;

注意chomp()是有返回值的,所以下面的代码跟上面的代码不同:

#!/usr/bin/perl 

use utf8;
use warnings;

$input = <STDIN>;
print "input: ", chomp $input;  # 打印input: 1

input: 1表示chomp()去掉的字符数,这里就是1个换行符。

$/记录了分隔符,它默认就是换行符\n , 设置这个操作符会影响chomp()chomp()默认是去掉行尾的\n,当设置了$/chomp()会去掉$/设置的符号。

local $/ = "END";

上述的语句之后chomp()会将END认为换行符:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

local $/ = "END";
while (<>) {
    chomp;
    print "$_\n";
}

执行上述的代码结果:

E:\Gitee\perl\Code\Basic>Input.pl
nihao
END
nihao

只有当输入END才会往下执行。

undef

在首次赋值前,变量的初始值就是特殊的undef

  • 当作为数字时,它表现得像0;
  • 当作为字符串时,它表现得像空字符串。
  • undef即不是数字也不是字符串,它是另一种类型的标量值。

defined()函数用于判断某个变量是undef还是空字符串,如果是undef则返回假:

#!/usr/bin/perl 

use utf8;
use warnings;

$var;
if (defined($var)) {
    print "defined\n";
} else {
    print "undef\n" # 执行到这里
}

$var = '';
if (defined($var)) {
    print "defined\n";  # 执行到这里
} else {
    print "undef\n"
}

运算符

比较操作符

比较数字字符串
相等==eq
不等!=ne
小于<lt
大于>gt
小于或等于<=le
大于或等于>=ge

逻辑运算符

表格示例中我们设置变量$a为true,$b为false。

运算符描述
and逻辑与运算符符。如果两个操作数都为true,则条件为true。
&&c语言风格的逻辑与运算符符。如果两个操作数都为true,则条件为true。
or逻辑或运算符。如果两个操作数中有任意一个非零,则条件为true。
||c语言风格逻辑或运算符。如果两个操作数中有任意一个非零,则条件为true。
not逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将使其为false。

两种风格都可以使用。

列表和数组

在Perl里代表复数的是列表数组

列表是标量的有序集合,而数组则是存储列表的变量。

列表指的是数据,而数组指的似乎变量。每个数组变量都一定包含一个列表。

用于列表和数组的操作有许多是相同的,后面一般使用数组这个词

数组是有序的。

数组可以包含数字、字符串、undef和它们的组合。

数组的每个元素都是单独的标量变量(可以需要使用$):

#!/usr/bin/perl 

use utf8;
use warnings;

@list = ();     # 定义空的数组
$list[0] = 1;   # 数组中的元素赋值
$list[1] = 2;
$list[2] = 3;
print @list;

@list中的@表示引用整个数组,在定义和使用整个数组时需要用到。

数组的名字空间和标量的名字空间是分开的,所以程序中可以取相同的名字,不过最好不要这么做

#!/usr/bin/perl 

use utf8;
use warnings;

$Var = 1;
print $Var; # 打印1
@Var = (1, 2, 3);
print @Var; # 打印123

访问元素

如果对超过数组尾端的元素进行赋值,则数组会自动扩大,中间没有赋值的元素就是undef

数组的最后一个元素索引是$#XXXX是数组变量名,不过这个用起来比较麻烦,用的更多的是负值索引:

#!/usr/bin/perl 

use utf8;
use warnings;

@list = ();     # 空的数组
$list[0] = 1;   # 数组中的元素赋值
$list[1] = 2;
$list[2] = 3;
print $list[$#list];    # 3
print $list[-1];        # 3
print $list[-2];        # 2,也可以使用-2这种负值索引

一般情况下值用-1来访问最后的元素,其它的负值不怎么用。

数组直接量

数组直接量用小括号包含,中间用逗号隔开成员,还可以使用...

#!/usr/bin/perl 

use utf8;
use warnings;

print (1, 2, 3, 4, 5);  # 打印12345
print (1...5);          # 打印12345
print (5...1);          # 打印空,因为...前后必须是向上计数的

字符串直接量:

#!/usr/bin/perl 

use utf8;
use warnings;

print ("hello", " ", "world");

为了避免键入太多的引号,可以使用qw()函数:

#!/usr/bin/perl 

use utf8;
use warnings;

@list = qw(hello world);
foreach $value (@list) {
    print $value;
    print "\n"
}

qw()会将内部元素转换为字符串,其中的空白符是隔断,并且最终会被抛弃。

数组元素的赋值:

#!/usr/bin/perl 

use utf8;
use warnings;

($name, $age) = ("Jake", 19);
print "The name is $name\n";
print "THe age is $age\n";

操作函数

push()函数将新元素添加到数组尾部。

pop()函数取出数组最后一个元素并将其作为返回值返回。如果数组本来就是空的,则返回undef

shift()函数将新元素添加到数组开头。

unshift()函数取出数组第一个元素并将其作为返回值返回。

splice()函数在数组中间添加或移除某些元素。它接收4个参数:

  1. 目标数组;
  2. 要操作的开始元素的索引;
  3. 元素长度,可选,如果不指定,则默认长度到最后一个元素为止;
  4. 替换用的数组,可选,如果不指定,就是删除目标数组的数据。

如果元素长度为0,替换用的数组存在,则相当于添加数组。

上述的函数都会改变数组本身。

reserve()函数返回次序相反的数组,但是不改变原数组。

sort()函数返回排序之后的数组,但是不改变原数组。

上述两个函数不改变数组本身。

字符串中的数组内插

数组内插也可以在字符串中使用,所以需要注意下面的情况:

#!/usr/bin/perl 

use utf8;
use warnings;

@google = qw(hello google);
print "perl@google.com\n";  # 打印perlhello google.com
print "perl\@google.com\n"; # 打印perl@google.com

foreach/each

foreach用于遍历数组,它包含两个参数,第一个是数组变量,第二个是数组本身,它需要用括号括起来:

#!/usr/bin/perl 

use utf8;
use warnings;

@name = qw(Jack Jimmy John);
foreach $n (@name) {
    print "The name is $n\n";
}

如果省略数组变量,则默认用$_替代:

#!/usr/bin/perl 

use utf8;
use warnings;

@name = qw(Jack Jimmy John);
foreach (@name) {
    print "The name is $_\n";
}

更特殊的是,即使print()函数什么也不带,也会打印$_

#!/usr/bin/perl 

use utf8;
use warnings;

@name = qw(Jack Jimmy John);
foreach (@name) {
    print;
}   # 最终打印JackJimmyJohn

each以前只能用于哈希,但是现在也能用于数组,它返回数组中下一个元素对应的键值对(对于数组就是索引和元素值):

#!/usr/bin/perl 

use utf8;
use warnings;

@name = qw(Jack Jimmy John);
while (($index, $value) = each @name) {
    print "$index: $value\n";
}

数组输入

标量输入是一行,以回车键结尾;数组输入是所有行,以EOF结束(Linxu中是Ctrl+D,Windows中是Ctrl+Z):

#!/usr/bin/perl 

use utf8;
use warnings;

chomp(@line = <STDIN>);
print @line;

chomp()可以去掉每一行末尾的换行符。

子程序

子程序其实就是函数。

使用关键字sub定义函数:

#!/usr/bin/perl 

use utf8;
use warnings;

sub add {
    $n += 1;
    print "value: $n\n";
}

add     # 1
add     # 2
add     # 3
&add    # 4,注意这里使用了&

从这里也可以看到$n实际上是全局的。

注意这里的&add前面的&,这里是先定义了函数,然后调用,所以Perl能够识别到函数,可以不用&,但是这可能跟Perl内置函数冲突,所以最好还是加上&

返回值

在Perl中所有函数都有返回值,返回的是最后一次运算的结果(不管是什么):

#!/usr/bin/perl 

use utf8;
use warnings;

sub add {
    $n += 1;
}
print &add; # 1
print &add; # 2

这里$n += 1;的计算结果就是返回值。注意下面的代码:

#!/usr/bin/perl 

use utf8;
use warnings;

sub add {
    $n += 1;
    print $n;
}
print &add; # 1
print &add; # 1

这里的两个print &add;打印的值都是1,原因是函数add()返回的是print()的结果,后者的返回值一般就是1。

返回值也可以是非标量。

返回值也可以使用return操作符,尤其是要在函数中间返回的时候,不过这个时候return后面需要加返回值。如果什么也不写,则在标量上下文返回undef,在列表上下文返回空列表。

参数

函数可以有参数,用小括号括起来。参数其实是一个列表,在函数中用@_访问,但是要使用其中的元素,还是应该用$_[x]

#!/usr/bin/perl 

use utf8;
use warnings;

sub sum (a, b) {
    foreach (@_) {
        print "$_\n";
    }   # 打印1和2
    $sum = $_[0] + $_[1];
}
print &sum(1, 2);   # 打印3

@_在标量上下文中也可以是标量,表示参数的个数:

#!/usr/bin/perl 

use utf8;
use warnings;

sub max {
    if (@_ != 2) {
        print "Invalid parameter\n";
        return;
    }
    if ($_[0] < $_[1]) {
        print "2ed is bigger\n";
    } elsif ($_[0] > $_[1]) {
        print "1st is bigger\n";
    } else {
        print "equal\n"
    }
}
&max(1);        # Invalid parameter
&max(1, 2);     # 2ed is bigger
&max(1, 2, 3);  # Invalid parameter

参数也可以通过shift传递:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

sub print_param {
    $first = shift;
    $sencond = shift;
    say $first;
    say $sencond;
}

&print_param(1, 2);

每调用一次shift就可以用参数赋值一次。

私有变量

函数中的变量一般是全局变量,如果要使用私有变量,需要使用my操作符:

#!/usr/bin/perl 

use utf8;
use warnings;

sub add {
    my $n += 1;
}
print &add; # 1
print &add; # 1

由于$n是私有变量,所以每次调用函数都会重启被初始化为undef,然后加上1,就变成了1,最终两次返回都是1。

如果想要把私有变量保存起来(类似于c语言中的statisc),则需要使用state操作符:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;  # 注意state是在5.10版本引入的,所以要加上

sub add {
    state $n = 0;
    $n += 1;
}
print &add; # 1
print &add; # 2

my不会改变赋值时的上下文:

my ($num) = @_;	# 列表上下文,和 ($num) = @_; 相同
my $num = @_;	# 标量上下文,和 $num = @_; 相同

my操作符不加括号时,只能用来声明单个词法变量:

my $fred, $barney;	# 没有将$barney私有
my($fred, $barney);	# 两个都私有了

my可以用在数组上,或者foreach里面。

全局变量

my对应的有一个全局变量,通过our声明:

our %file_types = qw/
	RAW                   0x01
	FREEFORM              0x02
	SECURITY_CORE         0x03
	PEI_CORE              0x04
	DXE_CORE              0x05
	PEIM                  0x06
	DRIVER                0x07
	COMBINED_PEIM_DRIVER  0x08
	APPLICATION           0x09
	SMM                   0x0A
	FIRMWARE_VOLUME_IMAGE 0x0B
	COMBINED_SMM_DXE      0x0C
	SMM_CORE              0x0D
	DEBUG_MIN             0xe0
	DEBUG_MAX             0xef
	FFS_PAD               0xf0
/;

还有一个local。在一个全局变量上使用local操作符的时候,在每次执行local的时候都给该全局量一个临时值,但是这并不影响该变量的全局可视性。当程序抵达动态范围的末尾时,临时值被抛弃然后恢复原来的值。

输入和输出

读取标准输入

while循环中的行输入简写:

#!/usr/bin/perl 

use utf8;
use warnings;

while (<STDIN>) {
    print "I saw $_";
}

foreach也可以使用:

foreach (<STDIN>) {
    print "I saw $_";
}

需要注意两者有明显的区别,foreach需要将输入全部读取出来然后再显示,而while是一行一行地读。所以对于内容较多的情况(比如读大文件),应该使用while而不是foreach

钻石操作符<>

<>是行输入操作符的特例,但是它并不是从键盘取得输入,而是从用户指定的位置读取:

#!/usr/bin/perl 

use utf8;
use warnings;

while (defined($line=<>)) {
    chomp($line);
    print "$line\n";
}

执行脚本,并加上参数:

E:\Gitee\perl\Code\Basic>Input.pl Input.pl
#!/usr/bin/perl 

use utf8;
use warnings;

while (defined($line=<>)) {
    chomp($line);
    print "$line\n";
}

实际上就是打印Input.pl脚本的内容。如果不加参数,就还是从标准输入获取数据。所以不加参数时,chomp()就是作用在$_上。

钻石操作符是用来读取输入的,而输入的内容可以在$_中找到。

钻石操作符的入参来自@ARGV数组:

#!/usr/bin/perl 

use utf8;
use warnings;

@ARGV = qw# Input.pl #;
while (<>) {
    chomp;
    print "$_\n";
}

这里直接将入参写到了@ARGV中,所以不需要在调用脚本的时候指定,作用还是打印Input.pl这个文件。而且即使加了入参也没有用,因为还是会在代码中被覆盖掉。

say

say()print()类似,但是会自动加上换行符。

printf

perl也支持格式化输出。

#!/usr/bin/perl 

use utf8;
use warnings;

$name = "world";
printf "Hell %s\n", $name;

文件句柄

文件句柄就是程序里代表Perl进程与外界之间的IO联系的名称。

Perl有6个特殊的句柄:

  • STDIN
  • STDOUT
  • STDERR
  • DATA
  • ARGV
  • ARVGOUT

建议使用大写来命名文件句柄。

使用open()来打开句柄:

#!/usr/bin/perl 

use utf8;
use warnings;

open INPUT_PL, "< Input.pl";

while (<INPUT_PL>) {
    chomp;
    print "$_\n";
}

还是打印Input.pl文件内容。文件句柄的使用还是跟之前的STDIN类似,就是<>加打开的句柄名称。

注意open()中有使用到括号:

  • <表示只读;
  • >表示创建新文件;
  • >>表示追加;

注意在括号和后面的文件之间最好加上空格,避免出现意外,比如:

my $selected_output = ">password";
open LOG, ">$selected_output";

这样就导致变成追加了,而这可能不是想要的结果。

还有一种办法是直接采用下面的三个参数的写法:

open INPUT_PL, "<", "Input.pl";

这样就不用考虑因为没有空格导致的意外了。此外使用三种参数的情况还可以修改中间的第二个参数:

open INPUT_PL, "<:encoding(UTF-8)", "Input.pl";

这里增加了编码方式,当然第二个参数还有其它的参数可以加,这里不多介绍。

还可以用bitmode以二进制方式读写文件句柄。

关闭句柄使用close()

close INPUT_PL;

句柄可以判断其真假,真表示打开成功,假表示打开失败:

my $fd = open INPUT_PL, "<:encoding(UTF-8)", "Input.pl";
if (! $fd) {
    die "Open file failed. $!\n"
}

die()函数会输出指定的信息,并且让程序立即终止并返回不为0的退出码。$!是可读的系统错误信息,比如:

E:\Gitee\perl\Code\Basic>Input.pl
Open file failed: No such file or directory

只有系统服务请求失败后的瞬间,$!的值才有用。

还有一个类似的warn(),不过它不会立即退出程序。

上面是输入的例子,还是输出的例子:

#!/usr/bin/perl 

use utf8;
use warnings;


my $fd = open INPUT_PL, "<:encoding(UTF-8)", "Input.pl";
if (! $fd) {
    die "Open file failed: $!\n"
}
my $fd_tmp = open INPUT_TMP_PL, ">:encoding(UTF-8)", "Input_tmp.pl";
if (! $fd_tmp) {
    die "Open file failed: $!\n"
}

while (<INPUT_PL>) {
    print INPUT_TMP_PL $_;
}

close INPUT_PL;
close INPUT_TMP_PL;

注意这里的print INPUT_TMP_PL $_;输出句柄和输出内容之间没有逗号

从本地获取字符串

my $usage = <<"END";
Usage:
    $0 -o output.ffs [options] file.efi [...]
Options:
    -h | -? | --help              This help
    -o | --output output.ffs      Output file (default is stdout)
    -n | --name FileName          Name to include in UI Section 
    -t | --type Type              FREEFORM|DRIVER|SMM|DXE_CORE|SMM_CORE|PEIM
    -v | --version 1.0            Version section
    -g | --guid GUID              This file GUID (default is hash of Name)
    -d | --depex 'guid guid..'    Optional dependencies (all ANDed, or TRUE)
    -z | --compress               Enable LZMA compression
    -a | --auto                   Detect the section types from the filenames
END

say $usage

这里的$usage从本地获取字符串,<<之后是结束符,所以这里就是将后面到END为止的所有数据都当成了字符串赋值给$usage

哈希

数组是以数字来索引,而哈希则以名字来索引。即哈希的键是字符串,要求是该字符串必须是唯一的。

哈希没有顺序。

一个空的哈希:

%some_hash = ();

要指代整个哈希,可以用%作为前缀。

给哈希赋值:

$some_hash{"name"} = "Jack";
$some_hash{"age"} = 18;
$some_hash{2} = "Hello world";

访问哈希使用{},这里实际上是赋值。

注意第三个$some_hash{2},这里的2会被转换成字符串。

哈希也可以自带元素:

%some_hash = ("name", "Jack", "age", 18, 2, "Hello world");

这里的个数需要是偶数的,按照**(键, 值, 键, 值, …)**的方式排布。哈希也可以展开成列表:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

%some_hash = ("name", "Jack", "age", 18, 2, "Hello world");
@array = %some_hash; 
foreach $n (@array) {
    print "The element is $n\n";
}

使用列表方式来初始化哈希不太方便,所以引入了=>

%some_hash = (
    name => "Jack",
    age => 18,
    2 => "Hello world",
);

注意这里可以键可以省略引号。同样{}中检索用的键名也可以不用引号。

哈希赋值

哈希可以直接赋值:

%some_hash = (
    name => "Jack",
    age => 18,
    2 => "Hello world",
);
%new_hash = %some_hash;

更常用的方式是根据现有哈希得到新的哈希:

%some_hash = (
    name => "Jack",
    age => 18,
    2 => "Hello world",
);
%new_hash = reverse %some_hash;

reverse()实际上是(键-值)换成了(值-键)。

哈希函数

keys()函数返回哈希的键列表。

values()函数返回哈希的值列表。

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

%some_hash = (
    name => "Jack",
    age => 18,
    2 => "Hello world",
);

my @k = keys %new_hash;
my @v = values %new_hash;

say @k;
say @v;

each()函数每次调用返回哈希中的包含一对键和值的列表。

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

%some_hash = (
    name => "Jack",
    age => 18,
    2 => "Hello world",
);

while (($key, $value) = each %some_hash) {
    say "$key => $value";
}

exists()函数检查哈希中是否存在某个键。

delete()函数从哈希中删除指定的键及其相对应的值。

%ENV哈希

%ENV是一个哈希,包含环境变量,比如:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

say "PATH is $ENV{PATH}";

注意这里引号里面的%ENV里面用的是$ENV,实际上是一种双引号中的内插。

控制结构

if/unless

if是为真时执行,unless是为假时执行。

elsif,注意只有一个e

when/until

when是为真时执行,until是为假时执行。

for/foreach

foreachfor这两个关键字实际上是等价的,但实际上foreachfor的用法还是分开的。

for的用法:

for ( init; condition; increment ){
   statement(s);
}

如果for里面有两个分号,对应的就是真正的for,就是上面的用法,而下面的用法实际上对应的是foreach

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

for (1...10) {
    say "The number is $_";
}

而对应的真正的for的用法:

for ($i = 1; $i <= 10; $i++) {
    say "The number is $i";
}

注意say()字符串中的参数引用,前者是$_,后者是$i

循环控制

last,类似于c语言中的break

next,类似于c语言中的continue

还有一个redo,会重新执行当次迭代。

定义或

这是一个操作符,对应//,它在发现左边的值属于已定义时进行短路操作:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

my $name;
printf "%s", $name; # 会告警,因为变量不存在
printf "%s", $name // "No name";

简写

控制语句可以简写,比如:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

$value = 1;
say "value is larger than 0" if $value > 0;    # 倒置的if

虽然if语句在后面的,但是也会先执行判断。

部分求值操作符可以用于简写:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

$value = 1;
($value < 0) || say "value is larger than 0";

($value < 0)为假,所以say()函数会执行。

Perl模块

绝大部分Perl代码都是以模块的形式存在。

Perl模块有两个来源,一种是随Perl发行版本一同打包;另一种则需要从CPAN下载并安装。

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;
use File::Basename;

my $name = "/usr/local/bin/perl";
print basename $name;

使用use来包含模块。后面还可以增加导入列表:

use File::Basename qw/ basename /;

这里就只导入了basename。也可用()

use File::Basename ( "basename" );

可以导入空:

use File::Basename ();

不知道有什么用。

Getopt

脚本参数可以通过Getopt获取:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;
use Getopt::Long;

my $input   = "Unknown";
my $output  = "Unknown";

GetOptions(
    "o|output=s"    => \$output,
    "i|input=s"     => \$input,
) or die "Can't get options";

say $input;
say $output;

直接脚本的示例:

E:\Gitee\perl\Code\Basic>Option.pl -i nihao -o hello
nihao
hello

文件测试

Perl提供了一组用于测试文件的操作符,它们大部分返回布尔值:

操作符描述
-A文件上一次被访问的时间(单位:天)
-B是否为二进制文件
-C文件的(inode)索引节点修改时间(单位:天)
-M文件上一次被修改的时间(单位:天)
-O文件被真实的UID所有
-R文件或目录可以被真实的UID/GID读取
-S为socket(套接字)
-T是否为文本文件
-W文件或目录可以被真实的UID/GID写入
-X文件或目录可以被真实的UID/GID执行
-b为block-special(特殊块)文件(如挂载磁盘)
-c为character-special(特殊字符)文件(如I/O 设备)
-d为目录
-e文件或目录名存在
-f为普通文件
-g文件或目录具有setgid属性
-k文件或目录设置了sticky位
-l为符号链接
-o文件被有效UID所有
-p文件是命名管道(FIFO)
-r文件可以被有效的UID/GID读取
-s文件或目录存在且不为0(返回字节数)
-t文件句柄为TTY(系统函数isatty()的返回结果;不能对文件名使用这个测试)
-u文件或目录具有setuid属性
-w文件可以被有效的UID/GID写入
-x文件可以被有效的UID/GID执行
-z文件存在,大小为0(目录恒为false),即是否为空文件

可以同时测试多项属性:

if (-r $file and -w $file)

还可以连用:

if (-r -w $file)

文件测试函数

文件还有许多属性没有对应的操作符,这就需要使用到stat()lstat()函数:

my($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($filename);

这里的几个time都是时间戳,可以通过localtime()函数转换成容易阅读的形式:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

$filename = "File.pl";
my($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($filename);

say $ctime;

my($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst) = localtime $ctime;

printf "%d-%d-%d %d:%d:%d", $year + 1900, $mon + 1, $day, $hour, $min, $sec;

注意这里的时间起点是1900,月份起点是0,所以需要加上相关偏移值。

time()返回时间戳,以秒为单位。

目录操作

文件通配

跟shell下的通配类似,Perl下使用glob()来通配:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

my @all_file = glob "*.pl";
foreach (@all_file) {
    say $_;
}

打印当前目录下的所有Perl文件。

通配符的另一种用法:

my @all_file = < "*.pl" >;

因为glob()出的比较晚,所以就用上了<>

目录操作函数

chdir()改变当前的工作目录。

opendir()打开目录句柄。

readdir()读取目录内容。

unlink()删除文件。

rename()重命名文件,甚至还可以移动文件:

rename "Control.pl", "../Control.pl";

这里将文件移动到了上一层。还有一种写法:

rename "Control.pl" => "../Control.pl";

mkdir()创建目录。

rmdir()删除目录。非空目录需要先删除里面的文件。

chmod()修改文件或目录的权限。

chown()修改文件或目录的拥有者以及其所属组。

字符串和排序

index()找到字符串首次出现的位置:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

$big_string = "Hello world";
$small_string = "wor";
$where = index($big_string, $small_string);
say $where; # 6,计数从0开始

index()找到字符串最后一次出现的位置:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

$big_string = "Hello world";
$small_string = "l";
$where = rindex($big_string, $small_string);
say $where; # 9,计数从0开始

substr()从大字符串中获取到小的字符串,它接收三个参数:

  1. 大字符串;
  2. 其实数值,从0开始;
  3. 小字符串的长度。
#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

$big_string = "Hello world";
say substr($big_string, 1, 2);  # el

如果第三个参数不写,则表示获取到结尾为止:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

$big_string = "Hello world";
say substr($big_string, 1); # ello world

假如加上第三个参数后超过了大字符串的长度,那也只是获取到大字符串的结尾为止。

第二个参数可以是负数,表示从大字符串的结尾开始来计算起始位置,-1就是最后一个字符:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

$big_string = "Hello world";
say substr($big_string, -1);    # d

substr()取出来的值可以直接修改:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

$big_string = "Hello world";
substr($big_string, 0, 5) = "Goodbye";
say $big_string;    # Goodbye world

sprintf()可以返回格式化字符串:

#!/usr/bin/perl 

use utf8;
use warnings;
use 5.010;

$money = sprintf "%.2f", 2.456;
say $money; #2.46
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值