参考文档:
骏马金龙 https://www.cnblogs.com/f-ck-need-u/p/10420910.html
http://yuncode.net/code/c_507eb777522ae10
https://blog.csdn.net/philosophyatmath/article/details/58604561
perl语言入门骆驼书
http://www.freeoa.net/development/perl/perl-mod-file-find_2114.html
use strict命令是perl的编译器指令,加上会使perl的编译要求更严格一些。如果perl代码中有不好的编码风格,如未加分号结尾或者变量没有加my来控制作用范围,那么会提示编译失败
1)数组索引
(1)$#
加数组名
$#
加数组名表示数组最后一个元素的索引,其实,这种糟糕的语法来源于C Shell,在实际的代码中不常见。
(2)负数索引
判断命令行参数个数,可以把@ARGV用在标量上下文中,直接操作最后一个数组元素,可以利用负数索引值。-1表示数组最后一个元素,-2表示倒数第二个,如$array[-1]
2)常用操作命令
pop操作:取回数组中最右侧的即最后一个元素并作为返回值返回
@array = ,5..9;
$num0 = pop(@array); #$num0变成9,@arrray是(5,6,7,8)
$num1 = pop @array; #$num1变成8,@array是(5,6,7)
pop @array; #@array变成(5,6)。7被丢掉了
push操作:数组的尾端即最右端添加一个元素
push(@array,0); #@array是(5,6,0)
push @array,8; #@array是(5,6,0,8);
push @array, 1..10; #push一个列表,@array得到了10个新元素
@others = qw/9 1 3 5 7/;
push @array,@others; #@array又得到了5个新元素
push操作: 数组的尾端即最右侧添加一个元素
shift 从数组的开头即最左端移出一个元素,并将该元素作为返回值
unshift 给数组的开头即最左端添加一个元素
@array = qw #dino fred barney #;
$m = shift(@array); #$m变成"dino",@array现在是(“fred”,“barney”)
$n = shift @array; #$n变成了“fred”,@array现在是("barney")
shift @array; @array变成空的了
$o = shift @array; #$o变成undef,@array还是空的
unshift(@array,5); #@array现在仅包含只有一个元素的列表(5)
unshift @array,4; #@array现在是(4,5)
@others = 1..3;
unshift @array,@others; #@array又变成了(1,2,3,4,5)
splice 添加或者移除数组中间的某些元素,第一个参数是操作的数组,第二个参数是开始操作的位置,第三个是结束操作的位置。只用前两个的话,perl会把开始位置到结束的元素都取出来并返回
@array = qw(pebbles dino fred barney betty);
@removed = splice @array, 1,2; #删除dino和fred这两个元素
#@removed变成了qw(dino fred)而@array则变成qw(pebbles barney betty)
foreach遍历每个数组元素
scalar @array 可以返回数组的长度
1)打开文件句柄
open FILE_in, 'file_in.txt';open FILE_in, '< file_in.txt'
file_in.txt文件的内容通过文件句柄FILE_IN读到我们的perl程序中,后面的程序可以用文件句柄对该文件的内容进行操作
chomp函数去掉换行符
open FILE_IN, '>file_out.txt'
会创建一个新文件,若文件已存在则清空,将FILE_IN文件句柄的内容输出到这个文件中,对FILE_IN文件句柄操作之前,必须先声明它所输出的文件。
如下图代码,可以根据返回值来判断open文件句柄操作是否成功
使用文件句柄,用下面的代码格式可以,将读取模式打开的文件句柄中的一行行数据,放入变量$_中(未指定,默认)
while( ){
chomp;
针对默认变量$_操作
......
}
读写:open(文件句柄,"+<文件名"),通过“+<”模式,你可以既可以读文件,又可以写文件。你可以通过tell() 函数在文件内部移动,通过seek()函数进行定位。如果文件不存在,就会被创建。如果文件已经存在,原来的数据不会被清除。
读写:open(文件句柄,"+>文件名"),通过“+>”模式,可以同时读写文件,但与上面不同的是,它是破怀性写,会清除原来的内容
2)print方式追加文件句柄
以写入或添加模式打开的文件句柄可以在print或printf函数中使用
if(!open MYTEST,'>test.rpt'){
die "can not open test.rpt:$!";
}
print MYTEST "it is for test\n";
结果:产生一个test.rpt文件,里面的内容为 it is for test\n
3) say和print都是打印信息的,但say会在打印的每行内容后加上换行符
1)单个点号可以作为字符串拼接
$a = "hihi";
$b = "okok".$a;
print "$b\n";
结果:okokhihi
2)小写字母xn(数字),会将左边的操作数重复n次
"fred"x3 #得到"fredfredfred"
1)fileparse
用法:($name,$path,$suffix) = fileparse($fullname,@suffixlist)
方法fileparse是将一个文件的全路径拆成三部分,文件名、目录名和后缀,@suffixlist是给一个后缀的集合。例如 :
($base,$path,$type)= fileparse("\dir0\dir1\test0.txt7",qr{.txt\d+});
输出$base=>draft,$path=>\dir0\dir1,$type=>txt7
File::Basename;
$full_name = "/usr/local/src/perl-5.6.1.tar.gz";
$file = basename($full_name);#$file="perl-5.6.1.tar.gz
$dir = dirname($full_name); #$dir="usr/local/src";
3)File::Find
Perl的File::Find模块对在整个文件系统中搜索文件名十分有用,特别是你可以使用正则来匹配文件名并循环地穿过任何的目录结构(许可情况下)
find 函数的用法如下:find(\&wanted, @directories_to_search);
find的第一个参数是 一个函数的引用,而所有其它的参数都应该是目录。其中第一个参数 wanted 是个子函数,也就是回调函数,由你自己来定义,这个参数必须有,即使它的内容为空。第二个参数是个目录列表,一般情况下,我们可能只是处理一个目录,所以它也可以是个普通的表示目录名的字串标量。需要注意的是,&wanted 子程序的前面需要加一反斜杠转义。
File::Find模块的find函数中,$_
变量被设置成当前正在访问的文件(即目录参数中的文件)的基本名称,变量$File::Find::name
是被访问的文件的全路径名。
当前目录有文件a.txt,b.txt,以及子文件夹sub_dir。执行下面命令后
use File::Find;
find sub {print "dir:$File::Find::name \$_:$_\n" if -d}, ".";
find sub {print "dir:$File::Find::name \$_:$_\n" if -f}, ".";
find sub {print "all:$File::Find::name \$_:$_\n"}, ".";
结果:
dir:. $_:.
dir:./sub_dir $_:sub_dir
file:./a.txt $_:a.txt
file:./b.txt $_:b.txt
all: $_:.
all:./sub_dir $_:sub_dir
all:./a.txt $_:a.txt
all:./b.txt $_:b.txt
4)File::Copy
File::Copy模块导出两个函数,copy和move,它们分别把第一个函数拷贝到或者改名为第二个参数,类似于调用unix的cp(1)和mv(1)命令。copy函数也可以接受文件句柄作为参数。成功返回真,失败返回假。
use File::Copy qw(cp mv);
cp("./test.pl","./test0.pl") or die "can not copy!:$!\n";
将本地目录下的test.pl文件拷贝生成一个本地目录下的test0.pl文件。
后面加上qw(cp my)
,将copy和move函数改名为cp和mv。这时使用copy函数或move函数就会报错,只能使用cp或者mv。
(1)q 相当于 单引号’ ’ my $test = q{\n q test}; #打印出\n q test
(2)qq相当于双引号“”,可变量内插 my $test = qq{\n qq test}; #打印出换行符, 下一行qq test
(3)qw用来构建列表,相当于(’ ‘,’ ’ ,’ ')在每一个单词上添加 ’ ',用空格分割字符串得到列表
@rocks = qw{bed rock sleep};#相当于 @rocks = (“bed”,“rock“,”sleep”)
(4)qr相当于创建正则表达式
$test = "my_test";
$match = qr(my);
if($test =~ $match){
print “success”;
}
5)qx用来执行外部程序,相当于``
(1)perl中任意一个子程序都是有返回值的,返回值默认为最后一次运算的结果,如果最后执行的是print,那么返回值为1
(2)子程序的参数列表, $n = &max(3,4); perl会自动将参数列表保存到@_,该变量在子程序执行期间有效。
(3)关键字sub定义子程序,关键符号&调用子程序
(4)在定义子程序用的sub{…}中,可以用my $a, my用来定义私有变量
(1)直接执行子程序,按顺序由上到下执行代码,一个线程内执行
sub test0{
print “test0 0\n”;
print “test0 1\n”;
print “finish test0\n”;
}
&test0;
print "main 0\n";
print "main 1\n";
结果:test0 0
test0 1
finish test0
main 0
main 1
(2)用create创建线程后的执行过程
use threads;
sub test0{
print "test0 0\n";
print “test0 1\n”;
print “finish test0\n”;
}
my $thr0 = threads->create(\&test0);
print "main 0\n";
print "main 1\n";
$thr0->join;
结果:
main 0
main 1
test0 0
test0 1
finish test0
可见用create创建线程以后,不等test0子程序执行完,就开始执行下面的打印信息,直到调用join函数,才将test0子程序执行过程中的打印信息打印出来,join操作的意思是:“对该线程收尸并收集该线程的返回值”。
use threads;
sub test0{
print "test0 0\n";
print “test0 1\n”;
print “finish test0\n”;
}
my $thr0 = threads->create(\&test0);
sleep 2;
print "main 0\n";
print "main 1\n";
$thr0->join;
结果:
test0 0
test0 1
finish test0
main 0
main 1
sleep 2时子线程thr0执行完。子线程先执行完毕,但是父线程还没对它进行join,这时子线程一直处于joinable的状态,其实这个时候子线程基本已经失去意义了,它的返回值和相关信息都保存在线程栈(或调用栈call stack),当父线程对其进行join()的时候,自然能从线程栈中找到返回值或某些信息的栈地址从而取得相关数据,也能从现在开始对其进行收尸行为。线程的状态有create-running-finished execution后进入joinable-然后可以joined/unjoined/undetached
对比上面的代码,可以理解为加入create子线程后,在主线程中执行时,用create创建的子线程和主线程并行执行。
线程运行在进程内部,每个进程都至少有一个线程,即main线程,它在进程创建之后就存在。一个进程中除了主main进程外还可以create很多个线程,所有线程全都在进程内部并行地被调度、运行,就像多进程一样。每个线程都共享了主进程的很多数据,除了线程自己所需要的数据,它们都直接使用父进程的变量、子程序。
(3)create多线程
按顺序create多线程时,create第二个线程时第一个线程就执行完了
use threads;
&test_thread;
&test0;
&test1;
sub test0{
print "test0 0\n";
print “test0 1\n”;
print “finish test0\n”;
}
sub test1{
print "test1 0\n";
print “test1 1\n”;
print “finish test1\n”;
}
sub test_thread{
my $thr0 = threads->create(\&test0);
print "after create thread0\n";
my $thr1 = threads->create(\&test1);
print "after create thread1 and thread0\n";
foreach(threads->list(threads:all)){
if($_->is_joinable()){
$_->join();
}
}
print "finish test_thread\n";
}
结果:
after create thread0
test0 0
test0 1
finish test0
after create thread1 and thread0
test1 0
test1 1
finish test1
finish test_thread
test0 0
test0 1
finish test0
test1 0
test1 1
finish test1
4)线程id
sub test_thread{
my $thr0 = threads->create(\&test0);
print "after create thread0\n";
my $thr1 = threads->create(\&test1);
print "after create thread1 and thread0\n";
my $cur_id = threads->tid();
my $thr0_id = $thr0->tid();
my $thr1_id = $thr1->tid();
print "\$cur_id:$cur_id \$thr0_id:$thr0_id \$thr1_id:$thr1_id\n";
foreach(threads->list(threads:all)){
if($_->is_joinable()){
$_->join();
}
}
print "finish test_thread\n";
}
新加入的打印id的代码:
$cur_id:0 $thr0_id:1 $thr1_id:2
三个线程主线程、thr0、thr1的id不同,通过id可以区分不同的线程。可以理解为在主线程主干上分出来了两个分支作为分支线程 。
threads->self()可以获取当前线程对象。类方法threads->tid()来获取当前线程对象的ID。对于已知道tid的线程,可以使用类方法threads->object($tid)
去获取这个tid的线程对象。注意,object()只能获取正激活的线程对象,对于joined和detached线程,都返回undef,不仅如此,对于无法收集的线程对象,object()都返回undef,例如收集$tid不存在的线程。
5)线程状态
所以,threads->list()只能统计未detach、未join的线程,running返回的是正在运行子程序主体的线程,joinable返回的是已完成子程序主体的线程,all返回的是它们之和。
$thr->is_running()如果该线程正在运行,则返回true
$thr->is_joinable()如果该线程已经完成了子程序的主体(即running刚结束),且未detach未join,换句话说,这个线程是joinable,于是返回true
$thr->is_detached() threads->is_detached() 测试该线程或线程自身是否已经detach
1)时间相关的函数
time() 函数来获取新纪元时间,该函数返回从1970年1月1日起累计的秒数。
localtime()函数返回系统时间
$now_time = time();
print "now time is $now_time\n";
$local_time = localtime();
print "now local_time is $local_time\n";
结果:
now time is 1574130774
now local time is Tue Nov 19 10:32:54 2019
2)use Time::HiRes
use Time::HiRes后,此模块提供sleep(), alarm(), time() 的增强版以取代perl内置的相应函数。
其中sleep() 和alarm() 的参数可以是小数。比如sleep ( 0.1 ) 表示休眠0.1秒,time() 可以返回浮点数。
如果没加这个use Time::HiRes,sleep(0.9)认为是sleep(0),根本不会进入sleep状态。
alarm也称为闹钟函数,在进程中设置一个定时器,当指定的时间到时,向进程发送SIGALRM信号,可以设置忽略或者不捕获此信号,如果采用默认方式其动作是终止调用该alarm函数的进程。
use Time::HiRes qw(sleep time);
$now_time = time();
print "now_time is $now_time\n";
$local_time = localtime();
print "local_time is $local_time\n";
sleep(0.9);
print "end test\n";
结果:
now_time is 1574132019:49568
local_time is Tue Nov 19 10:53:39 2019
end test
use Time::HiRes qw(sleep time);
$now_time0 = time();
print "now_time0 is $now_time0\n";
$local_time0 = localtime();
print "local_time0 is $local_time0\n";
sleep(3.9);
$now_time1 = time();
print "now_time1 is $now_time1\n";
$local_time1 = localtime();
print "local_time1 is $local_time1\n";
$delta_time = $now_time1 - $now_time0;
print "delta_time is $delta_time\n";
$delta_local_time = $local_time1 - $local_time0;
print "delta_local_time is $delta_local_time\n";
print "end test\n";
结果:
now_time0 is 1574132698:67586
local_time0 is Tue Nov 19 11:04:58 2019
now_time1 is 1574132702:57626
local_time0 is Tue Nov 19 11:05:02 2019
delta_time is 3.9004008769989
delta_local_time is 0
end test
time()函数拿到的时间只差可以精确到小数点后5位,单位是秒
而local_time函数拿到的时间一段时间的差值为0
$data = `data +'%Y%m%d%H%M'`;
print "data is $data\n";
结果:
data is 201911191624
上面代码注意+号后面没有空格,有空格会报错
1)-r 文件或者目录,可读 -w文件或者目录,可写
-e 文件或者目录,存在 -x文件或者目录,可执行
-s 文件或者目录存在且有内容,返回值是以字节为单位的文件大小
-d 是目录
-f 是普通文件
虚拟文件句柄_,高速perl用上次查询过的文件信息做当前测试
perl将perl命令行的参数列表放进@ARGV中
while($ARGV=shift@ARGV){
print "ARGV is $ARGV\n";
}
输入命令:test0.pl a b c d
结果:ARGV is a
ARGV is b
ARGV is c
ARGV is d
while($ARGV=shift){
print "ARGV is $ARGV\n";
}
执行结果一样
省略到@ARGV,两端代码执行结果一样
1)if(/yes/i)
/i
进行大小写无关的匹配
/s可以让.号来匹配任意字符,例如if(/yes.*ok/s)
只要出现yes和ok,两个单词之间有任意长度的任意字符都可以匹配成功,该两个单词跨越多行出现也可以匹配成功。而没有加/s,即/yes.*ok/只能匹配同行出现yes后加ok的情况,可匹配除换行符外的任意字符
将.
号替换成字符集[/d/D]
,会匹配任意字符
\d
表示数字,大写表示否定意义,\D
表示非数字。也可以用下面的格式表示否定[^\d]、[^\w]、[^\s]
,分别表示非数字字符、非单词字符或者非空白字符。
*
匹配前一个条目零次或者无限多次
+
号匹配前一个条目一次以上
?
号前一个条目可出现一次或者不出现
|择一匹配 if(yes|ok)只要匹配到yes或者ok任意一个就算成功
用()进行模式分组,\1 \2
$_ = "yabba dabba cabba";
if(/y(.)(.)\2\1/){ #匹配'yabba'
print "It matched after the y!\n";
}
字符集[a-zA-Z]
$_ = "HAL-9000 aaaaa";
if(/HAL-[0-9]+/){
print "match string\n";
}
\w 匹配到的事[a-zA-Z0-9_]
\s能匹配到5个空白符:换页符(form-feed\f),水平制表符(tab\t),换行符(newline\n),回车符(return\r)以及空格字符本身
2) 绑定操作符,模式匹配的操作对象不是$_
,例如if($some_other =~ /\bru\b/)
3) 锚位:
^
字符串开头的锚位或行首
$
字符串结尾的锚位或行尾,除换行符以外,不包含换行符的行尾
\b单词边界的锚位,/\bfred\b/
匹配整个单词为fred,如 my $string = "fred ok hi";
则能匹配上,如my $string="fredok hi
,则不能匹配上
1)s///替换,/g进行全局替换,默认只能替换一次
s/ok/hi/g
s/^\s+// 将开头的空白替换成空字符串
s/\s+$// 将结尾的空白替换成空字符串
\U转义符会将其后的所有字符替换成大写的
\L转义符会将其后的所有字符替换成小写的
$_ "I saw Barney with Fred.";
s/(fred|barney)/\U$1/gi; #$_现在成为了“I saw BARNEY with FRED.”
\E可以关闭大小写转换功能
s/(\w+) with (\w+)/\U$2\E with $1/i; #$_替换后为“I saw FRED with barney.”
$test = 'abc***a900001';
$test =~ s/a[0-9]+//g;
print "after s, test is $test\n";
结果:after s, test is abc***
2)split以给定的模式拆分字符串,返回一个数组
my @fields = split /:/,"abc::def:g:h"; #得到(“abc”,“def”,“g”,“h”)
join将数组以给定模式拼接成字符串
my $x = join ":",4,6,8,10,12; #$x为“4:6:8:10:12”
3)通过变量拿到匹配到的字符串
while($ARGV=shift){
if($ARGV =~ /-cfg=(\w+)/){
print "\$1 is $1\n";
}
}
输入命令:test.pl -cfg=yaya
结果:$1 is yaya
$1可以拿到第一个括号内匹配到的变量,同理,$2可以拿到第二个括号内匹配的变量
访问哈希,使用下面的语法
$hash($some_keys}
例如:$family_name{'fred'}='hi'; $keys_value=$family_name{'fred'};#$keys_value为hi
对哈希赋值等同于对列表赋值
%some_hash = ('foo',35,'bar',12.4,2.5,'hello','wilma',1.72e30,'betty',"bye\n");
在列表上下文中,哈希的值是简单的健-值对列表@any_array = %some_hash;
还可以采用下面的胖箭头方式对哈希赋值
my %last_name = (
fred => 'flintstone',
dino => undef,
barney => 'rubble';
betty => 'rubble';
);
keys %hash 返回哈希的键列表
values %hash 返回哈希的值列表
each %hash 返回键值对,但顺序是乱的
$hash_test{'a'} = 0;
$hash_test{'b'} = 1;
$hash_test{'c'} = 2;
@hash_a = %hash_test;
print "@hash_a\n";
$a0 = "a0";
$a1 = "a1";
$hash_test0{'a'} = [$a0,$a1];
$hash_value0 = values %hash_test0;
@hash_a0 = %hash_test0;
print "hash_a0:@hash_a0 hash_value0:$hash_value0\n";
结果:
c 2 a 0 b 1
hash_a0:a ARRAY(0xef75e0) hash_value0:1
values返回的列表在标量上下文中为列表的大小,所以为1
1)glob操作符
glob操作符可以取得匹配模式的所有文件,返回一个列表
如下图当前目录所有的.pl文件
@file = glob './*.pl';
$n = 0;
foreach(@file){
print "$n:$_\n";
$n ++;
}
结果:
0:./test.pl
1:./test0.pl
2:./test1.pl
@file =glob '.*'
;可以取得当前目录下所有文件名有.的文件,等价于 @file=<.*>
@file =glob '*'
;可以取得当前目录下所有文件 等价于 @file=<*>
@file = glob '*.pl *.txt'
可以取得当前目录下所有.pl文件和.txt文件
2)取得当前目录用$ENV{‘PWD’}
3)目录句柄,可用目录句柄读取目录里面的文件,readdir可以读取目录句柄中的文件以及文件夹
$dir = "../tc";
opendir TC, $dir or die "can not open $dir!\n"
foreach $file (readdir TC){
print "get from $dir is $file\n";
}
close TC;
结果:
get from ../tc is a_test.sv
get from ../tc is b_test.sv
get from ../tc is .
get from ../tc is ..
get from ../tc sub_dir
其中sub_dir为文件夹
其中的TC裸字文件句柄也可以用变量来代替
while ($name = readdir $dh){
next unless $name =~ /\.pm$/;
...其它对文件名的处理...
}
4)unlink操作符可以指定要删除的文件列表
unlink 'slate','bedrock','lava';
unlink qw(slate bedrock lava);
这会把这三个文件放进碎纸机,从此消失在系统中
既然unlink的参数是一个列表,glob函数返回的也是列表,可以联合使用这两个函数,一次删除多个文件
unlink glob '*.o';
5) rename重命名文件
rename 'old','new';
将old文件改为同一目录下名为new的文件。也可以用这个命令将文件移到其它目录中:
rename 'dir0/sub_dir0/a.rpt','a0.rpt';
1 last类似于c中的break,可终止循环
perl中的循环有for,foreach,while,until,用{}起来的裸块也可以用last跳出来
next立刻结束当次循环,进入当次循环的最底部然后进入下次循环执行。
2 until会一直执行,直到until后面的条件为真。与while相反,while会一直执行,直到while后面的条件为假。
3 unless当后面的条件为假时,可以执行后面的语句。与if表示的逻辑相反,if为条件真时,才执行后面语句。
1)substr函数操作字符串
substr函数只处理较长字符串中的一小部分内容,用法为:my $part = substr($string,$initial_position,$length
。它需要三个参数:一个原始字符串、一个从零起算的起始位置以及要截取的字符串长度。
my $mineral= substr("Fred J. Flintstone",8,5);#返回“Flint”
my $rock= substr "Fred J. Flintstone",13,1000;#返回“stone”
2)sprintf格式化字符串
格式化定义中数字字段的前置零表达必要时会在数字前补零以符合指定的宽度要求。如果没有前置零,那么日期与时间字符串里的数字就不会用零补足宽度,只留下相应长度的空格
my $date_tag = sprintf "%4d%02d%02d %2d:%02d:%02d",$yr,$mo,$da,$h,$m,$s;
##得到”2019/01/19 3:00:08“,注意3前面有两个空格
my $date_tag = sprintf "%4d%2d%2d %2d:%2d:%2d",$yr,$mo,$da,$h,$m,$s;
##得到“2019/ 1/19 3: 0: 8”,注意3前面有两个空格
3)前面加一个int可以做强制类型装换,rand(9999), 在0到9999之间随机得到一个浮点数
my $num;
do{
$num = int(rand(9999));#$num为5620
$num0=rand(9999); #$num0为683.46014028546
$int_num0=int($num0);#为683
数字9999可以用0xffff表示一个十六进制的数。rand函数调用多次,每次的随机结果不同。