Perl基础

脚本

Perl 是一种脚本语言,它在每次运行前都需要编译。在unix/linux下,通常perl脚本都是以 #!/usr/bin/perl 为开头的。这其实是perl(可执行文件)的全局路径,这个路径当然可以写成其他,只要能找到perl, 但有一点需要注意,perl的全局路径(/usr/bin/perl)不能超过32个字符。

Perl的内容和命令行

Perl路径给出后: #!/usr/bin/perl 接下来就该写perl脚本实际 内容了,你可以什么都不写,也可以写点注释语句,当然最应该写的就是命令行了。通常,注释行都是 以" #" 开头的,内容任意,反正在perl脚本中不会被运行。命 令行是以非空格字符开始并且以" ;"结束的。所以,你可以在把一条命令 写成多行,而直到你给出分号时结束。

直接命令行和子程序(函数)

普通命令行在perl脚本中是由上及下一行一行执行的。然而,子程序可以放到任何地方,只有被调用时才会被执行 。子程序是什么样子的呢?如果你看到一段代码以"sub"开头,并且格式是这样的:sub name {command;} , 那 么这就是子程序了。

其他特殊行

Perl 可以包含其他程序的代码,但需要这样的格式: require something 或者 use something.

引用

单引号: '' 或者: q//
双引号: "" 或者: qq//
引用执行结果: `` 或者: qx//
引用几个词组: ('term1','term2','term3') 或者: qw/term1 term2 term3/
引用一个被引用的字符串: qq/"$name" is $name/;
引用包含 "/": qq!/usr/bin/$file is readdy!;

标量上下文和列表上下文

在Perl中,标量上下文和列表上下文是有区别的,这是Perl脚本语言的一个亮点,这个特性在众多脚本语言当中是 独一无二的,也是非常实用的。

在Perl的子程序中,不仅可以返回一个列表类似于C中的标量,而且还可以返回一个数组,这个数组不仅可 以给出标量上下文中元素的个数还可以给出列表上下文中元素本身。

Perl该特性的巨大价值应该是显而易见的。


变量和运算符

概况

Perl的变量包含很多内容:标量变量、一维数组、二维数组以及关联数组。你可以通过特殊字符来声明Perl的变量 是标量还是数组,如 $variable 表示普通的标量变量; @variable 表示数组; %variable 表示关联数组。 在Perl中,你不需要去区分变量中的字符串和数字,因为Perl可以自动识别它们。

标量

赋值变量: $price = 300; $name = "JOHN"; 变量的计算: $price *= 2; $price = $oldprice * 4; $count++; $worth--; 输出变量的值: print $price,"\n";

数组

赋值数组: $arr[0] = "Fred"; $arr[1] = "John"; 输出数组: print join(' ',@arr),"\n";
二维数组: $arr[0][0] = 5; $arr[0][1] = 7;

哈希(关联数组)

赋值单一元素的哈希: $hash{'fred'} = "USA"; $hash{'john'} = "CANADA";

赋值完整哈希:
%a = (
    'r1',  'this is val of r1',
    'r2',  'this is val of r2',
    'r3',  'this is val of r3',
);
或者:
%a = (
    r1 => 'this is val of r1',
    r2 => 'this is val of r2',
    r3 => 'this is val of r3',
);

赋值

" ="可以把一个值赋给一个变量,或者其他和 "="组合的操作符在赋值的同时也可以做其他操作。

$var = "string"; 把string赋予$var
$var = 5; 把数字赋值给$var

$var .= "string"; $var 后面加string
$var += 5;$var加5
$var *= 5; $var乘以5
$var ||= 5; 如果 $var 为 0 则$var的值为 5
$var x= 3; 使$var重复3次,如 $var 原来为a,则现在 为 aaa

替换和赋值:

($new = $old) =~ s/pattern/replacement/;

比较

字符串的比较: eq ne 如 in: $name eq "mary".
数字的比较: == != >= <= <=> 如: $price == 400.


And/Or/Not

评估成功或者失败的表达式: $yes or die; 意思是,如果$yes 没有定义那么脚本会 exit.
AND 我们可以写成: && 或者 " and" 而OR 我们可以写成: || 或者" or". Not写成 " !"或者 " not".

And, Or 和 Not 一般用于 if() 语句中:
if($first && $second){....;}
if($first || $second){....;}
if($first && ! $second{....;}
这个表示,如果$first 为真,并且$second为假,才会执行{}内的语句。
但大多时候,我们会用unless()语句去替代Not,这样看起来更容易懂:
print if ! $noway; one uses: print unless $noway;

.


分支

if

if(condition){
    command;
}elsif(condition){
    command;
}else{
    command;
}

如果满足 condition, 那么会执行 command;

unless (和if相反)

unless(condition){
    command1;
}else{
    command2;
}

只有满足了condition才会执行command2, 否则执行command1;

循环

while

while(condition){
    command;
}

# 这里的next if表示,当condition2为假时,才会执行

command2 
while(condition1){
    command1;
    next if condition2;
    command2;
}

# 当满足last condition时会退出循环
while(condition){
    command;
    last if condition;
}

# 如果满足condition1 则会执行command1, 同时满足

condition2时会进一步执行command2,每执行一次while循环都会执行command3
while(condition1){
    command1;
    continue if condition2;
    command2;
}continue{
    command3;
}

# 当满足condition1时,执行command,当同时满足condition1

以及condition2时,再次执行command
while(condtion1){
    command;
    redo if condition2;
}

command while condition;

until (和while正好想反)

until(condition){
    command;
}

until(condition){
    command;
    next if condition;
    command;
}

until(condition){
    command;
    last if condition;
}

until(condition){
    command;
    continue if condition;
    command;
}continue{
    command;
}

command until condtion;

for (=foreach)

# 迭代@data,输出每个 $_的值
for(@data){
    print $_,"\n";
}

# 把@data的值赋予$info
for $info (@data){
    print $info,"\n";
}

#输出1到100中的奇数
for $num (1..100){
    next if $num % 2;
    print $num,"\n";
}

#  (;;),表示为真。当$num>100时就停止循环
for (;;){
    $num++;
    last if $num > 100;
}

map

# 语法
map (command,list);
map {comm1;comm2;comm3;} list;
# 例
map (rename($_,lc($_),<*>);
.

文件检测操作

用来检测文档的状态,如下:
-f $file 是否是普通文件
-d $file 是否是目录
-r $file 是否是可读
-x $file 是否可执行
-w $file 是否可写
-o $file 当前用户是否为该文件的所属主
-l $file 文件是否为链接
-e $file 文件是否存在
-z $file 文件存在,检测大小是否为0
-s $file 文件大小大于0
-t FILEHANDLE 该文件句柄是连接到某一个tty的
-T $file 文件是否是文本的文档
-B $file 文件是否是二进制文档
-M $file 距最后一次更改多长时间

正则表达式

什么是正则表达式

正则表达式是用某种模式去匹配字符串的一个公式。通常情况下,你可以通过搜索模式来找到匹配的字符串,也可 以把匹配的字符串给替换为你想要的。

模式

Perl的模式很多,可以是一个或多个字符,也可以是某个或者多个特殊字符,当然还可以是任意字符或者无任何字 符。总之,Perl的模式匹配是非常精准、抽象以及灵活的。
模式 匹配
. 任意一个字符
.* 任意个(包含0)任意字符
a* the maximum of consecutive a's
a*? the minimum of consecutive a's
.? 一个或者无任意字符
.+ 一个或者多个任意字符
.{3,7} 3到7个任意字符,尽量多
.{3,7}? 3到7个任意字符,尽量少
.{3,} 至少3个任意字符
.{3} 3次任意字符
[ab] a或者b
[^ab] 不是a并且也不是b
[a-z] 任意一个小写字母
^a
\Aa
a开头
a$
a\Z
a结尾
A|bb|CCC a或者bb或者CCC
tele(f|ph)one telefone 或者 telephone
\w 大小写字母或下划线_
\W 除大小写字母和下划线的任意字符
\d 0-9 任意一个数字
\D 除数字之外的所有字符
\s 空格或者tab、换行以及其他空白字符
\S 除上面的以外
\t tab
\n 换行
\r carridge return (翻译不通)
\b 以英文字母,数字为边界的字符串
\bkey matches key but not housekey
(?#.......) 注释
(?i) 区分大小写,可以是内部模式变量
(?:a|b|c) a 或者 b 或者 c, 但在$n 中不存在字符
(?=.....) 包含..... 但不会存储在$&
(?!.....) 不包含..... 但不会存储在$&

替换

你可以使用这样的语句 s/pattern/replacement/把匹配到的 东西给替换。注释:
这里的"s"表示命令,然后紧跟着三个分隔符(斜杠),前两个斜杠中的内容就是要匹配的内容,后两个斜 杠中的内容则为要替换为的内容。

可以这样更换一个变量的内容: $var =~ s/pattern/replacement/;
修改一个变量的内容,并把修改后的变量赋值给另外一个变量:
($name = $line) =~ s/^(\w+).*$/$1/;
命令 做什么
s/A/B/; 替换第一个出现的A为B
s/A/B/g; 替换所有A为B
s/A+/A/g; 替换1个或多个A为单一的A
s/^#//; 删除所有以#为开头的字符串
s/^/#/; 在字符串最前增加一个#
s/A(\d+)/B$1/g; A后边是一连串数字,把A替换为B
s/(\d+)/$1*3/e; 把一连串数字后边再重复两遍该连串的数字
使用 -e 可以直接在linux命令行中运行perl语句:  
perl -e '$aa = 4; $bb = '$aa'; $bb =~ s/(\$\w+)/$1/ee; print $bb,"\n";'  
s/here goes date/$date/g; 把 "here goes date" 替换成 $date
s/(Masumi) (Nakatomi)/$2 $1/g; 把两个字符串的位置互换
s/\000//g; 把空字符串移除
s/$/\033/; 在每行后加一个^M,使其在dos下可读

输入输出

输出一个变量的值

print $var,"\n";

输出格式化的字符串

printf("%-20s%10d",$user,$wage);

把输入的值赋值给变量,并删除新行

chomp()(perl5)会删除\n以及后面的所有内容。chop()(perl4)会删除最后一个字符,而不管这个字符是什么.

chomp($var = <STDIN>);

流线性读入一个文件

open(IN,"<filename") || die "Cannot open filename for input

\n";
while(<IN>){
    command;
}
close IN;

把读入的文件赋值给数组

open(AAA,"<infile") || die "Cannot open infile\n";
@bigarray = <AAA>;
close AAA;

把输出的结果重定向到文件中

open(OUT,">file") || die "Cannot oben file for output\n";
while(condition){
    print OUT $mystuff;
}
close OUT;

检测打开文件是否会输出信息(eof)

open(IN,"<file") || die "Cannot open file\n";
if(eof(IN)){
    print "File is empty\n";
}else{
    while(<IN>){
        print;
    }
}
close IN;

命令行中提及程序的名字

空文件句柄“<>”能够迭代性的读取文件,当前程序的文件名可通过$ARGV得到。如下,可以在诸多行的行头加 上文件名:
while(<>){
    $file = $ARGV;
    print $file,"\t",$_;
    open(IN,"<$file") or warn "Cannot open $file\n";
    ....commands for this file....
    close(IN);
}

得到文件名

得知当前目录

@dir = <*>;

迭代性的使用当前目录

while(<*>){
    ...commands...
}

通过< >选择文件

@files = </longpath/*.c>;

通过glob()选择文件

This is the official way of globbing:
@files = glob("$mypatch/*$suffix");


Readdir()

Perl即使不使用globbing shell也是可以得到目录的,并且既快速又可控,但是我们必须要使用opendir()以及 closedir().
opendir(DIR,".") or die "Cannot open dir.\n";
while(readdir DIR){
    rename $_,lc($_); 
}
closedir(DIR);

Unix命令的输入、输出的重定向

通过Unix管道得到程序要使用的数据

open(IN,"unixcommand|") || die "Could not execute 

unixcommand\n";
while(<IN>){
    command;
}
close IN;

把输出的数据给Unix管道

open(OUT,"|more") || die "Could not open the pipe to more\n";
for $name (@names){
    $length = length($name);
    print OUT "The name $name consists of $lenght characters\n";
}
close OUT;

执行Unix命令

如果输出信息只有一行字符串

system("someprog -auexe -fv $filename");

执行Unix命令,并且把输出值赋给变量

如果输出信息只有一行字符串:

chomp($date = qx!/usr/bin/date!); The chomp($date = qx!/usr/bin/date!); chomp()(perl5)把字符串后边的换行符“\n”去掉,$date得到Unix命令 "date"的值.

如果输出信息是多行,那么Perl会把所有输出信息存到数组中:

chomp(@alllines = qx!/usr/bin/who!);

替换整个Perl程序为一个Unix程序

exec anotherprog; 但是这样会删除掉Perl程序

Perl内置函数

字符串函数

所有小写变大小:  $name = uc($name);
只把第一个字母变大写:  $name = ucfirst($name);
所有大写变小写:  $name = lc($name);
只把第一个字母变小写:  $name = lcfirst($name);
得到字符串的长度: $size = length($string);
截取字符串的第4到第5: $part = substr($whole,4,5);
删除行尾(一般是删除换行符以及下面内容): chomp($var);
删除最后一个字符: chop($var);
拷贝(把$word的值拷贝给$salt): $code = crypt($word,$salt);
把字符串当作perl代码来执行: eval $var;
打印字符(substring)在字符串(string)的什么位置: $pos = index($string,$substring);
最后面的substring在string的什么位置: $pos = rindex($string,$substring);
引用字符串: $quote = quotemeta($string);

数组函数

和Unix中的grep类似,过滤出符合要求的元素: @found = grep(/[Jj]ohn/,@users);
把数组各元素作为操作对象,本例中把每个元素所有字母都变为大写: @new = map(lc($_),@start);
把数组中的所有元素组合成一个字符串,元素间用函数给定的分隔符分割,本例中分隔符为空格: $string = join(' ',@arr);
把一个字符串分割成一个数组: @data = split(/&/,$ENV{'QUERY_STRING'};
按字母顺序排序数组: sort(@salery);
将数组的次序颠倒: reverse(@salery);
获取哈希的键: keys(%hash);
获取哈希的值: values(%hash);
获取哈希的键和值: each(%hash);
清空数组: @arr = ();
删除哈希中的某个元素: delete $hash{$key};
检查哈希的某个键是否存在: if(exists $hash{$key}){;}
查看哈希是否有元素: scalar %hash;
把数组最后一个元素取出来并返回其值: $last = pop(@IQ_list);
把数组第一个元素取出来并返回其值: $first = shift(@topguy);
在数组最后增加一个元素: push(@waiting,$name);
在数组最前面增加一个元素: unshift(@nowait,$name);
把数组从第0个元素起,共2个元素替换为$var,这里的0和2不是固定的: splice(@arr,0,2,$var);
得到数组总共有多少个元素: scalar @arr;
得到数组最后一个索引: $lastindex = $#arr;

操作文件的函数

打开文件作为输入内容: open(IN,"</path/file") || die "Cannot open file\n";
打开文件作为输出内容: open(OUT,">/path/file") || die "Cannot open file\n";
打开文件作为追加内容: open(OUT,">>$file") || &myerr("Couldn't open $file");
关闭文件: close OUT;
给文件设置权限: chmod 0755, $file;
删除文件: unlink $file;
给文件重命名: rename $file, $newname;
给文件做硬链接: link $existing_file, $link_name;
给文件做软链接: symlink $existing_file, $link_name;
创建目录: mkdir $dirname, 0755;
删除目录: rmdir $dirname;
把$file的大小减小到$size: truncate $file, $size;
修改文件的所属主以及所属组: chown $uid, $gid;
找到软链接文件的源文件: $file = readlink $linkfile;
获取文件的全部属性信息: @stat = stat $file;
   

转换函数

数字变字符: chr $num;
字符变数字: ord($char);
十六进制变浮点: hex(0x4F);
八进制变浮点: oct(0700);
把time变成本地时间格式: localtime(time);
把time变成格林威治格式: gmtime(time);
把一个列表或数组以在实际机器存贮格式或C等编程语言使用的格式转化到一个简单变量中: $string = pack("C4",split(/\./,$IP));
与pack功能相反,将以机器格式存贮的值转化成Perl中值的列表: @arr = unpack("C4",$string);

子程序(等同于C语言中的函数)

定义一个子程序

sub mysub {
    command;
}
例子:
sub myerr {
    print "The following error occured:\n";
    print $_[0],"\n";
    &cleanup;
    exit(1);
}

调用一个子程序

&mysub;

给子程序参数

&mysub(@data);

在子程式中接收参数

作为全局变量:
sub mysub {
    @myarr = @_;
}
sub mysub {
    ($dat1,$dat2,$dat3) = @_;
}
作为局部变量:
sub mysub {
    local($dat1,$dat2,$dat3) = @_;
}

一些特殊的变量

语法

含义

$_ 在(@arr){ $field = $_ . " ok"; } 这个例子中,$_表示循环中的字符串
$. 表示当前程序的行号: while(){
$0 程序名
$$ 运行该perl脚本的进程号
$< 当前程序的真实uid
$> 当前程序有效的uid
$| 该变量默认为0,当设定为非0时,会强制刷新输出: select XXX; $| = 1;
$& 最后一次匹配模式匹配到的字符串
$1.... The ()-embraced matches of the last patternsearch
$` 含有上次成功的模式匹配内容之前的字符串
$' 含有上次成功的模式匹配内容之后的字符串

派生

派生很简单,通常我们会以三种方式区分派生 if(){} ,分别是父、子 和 错误。
if($pid = fork){
    # 父
    command;
}elsif($pid == 0){
    # 子
    command;
    # 子必须以exit为结尾!!
    exit;
}else{
    # 错误
    die "Fork did not work\n";
}
 

为派生的子创建管道

创建一个管道

pipe(READHANDLE,WRITEHANDLE);

刷新管道

select(WRITEHANDLE); $| = 1; select

(STDOUT);

在父与子之间设置两个管道

pipe(FROMCHILD,TOCHILD);  select(TOCHILD); $| = 1; 

select(STDOUT);
pipe(FROMPARENT,TOPARENT);select(TOPARENT);$| = 1; select(STDOUT);

if($pid = fork){
    # 父
    close FROMPARENT;
    close TOPARENT;
    command;
}elsif($pid == 0){
    # 子
    close FROMCHILD;
    close TOCHILD;
    command;
    exit;
}else{
    # 错误
    command;
    exit;
}

创建一个套接字来连接另外的计算机

# 需要放置在脚本开始的一些地方
require 5.002;
use Socket;
use sigtrap;

# 需要提前设置好的一些信息
$port   = 80;
$remote = 'remotehost.domain';
$iaddr  = inet_aton($remote);
$paddr  = sockaddr_in($port,$iaddr);

#  套接字
socket(S,AF_INET,SOCK_STREAM,$proto) or die $!;

# 刷新套接字
select(S); $| = 1; select(STDOUT);

# 连接
connect(S,$paddr) or die $!;

# 输入到套接字
print S "something\n";

#  从套接字中读取
$gotit = <S>;

#只读出一个字符
read(S,$char,1);

# 关闭套接字
close(S);

获取Unix用户以及网络相关信息

获取指定用户的密码: @entry = getpwnam("$user");
或者通过指定uid的方式获取用户的密码: @entry = getpwuid ("$UID");

你也可以得到group, host, network, services, protocols这些信息通过以下命令: getgrnam, getgrid, gethostbyname, gethostbyaddr, getnetbyname, getnetbyaddr, getservbyname, getservbyport, getprotobyname, getprotobynumber.

你还可以通过如下方式,获取一系列你想得到的信息,只不过你需要一个循环而已:

setpwent;
while(@he = getpwent){
commands...
}
entpwent;


如,你想要得到一些用户的家目录:
setpwent;
while(@he = getpwent){
    printf("%-20s%-30s\n",$he[0],$he[7]);
}
endpwent;
类似的,该方法同样适用于上面刚刚描述的那些东西,只不过需要在设置命令后面加上一个" stayopen" .

数学计算

加: +
减: -
乘: *
除: /
平方: **
返回e的n次方,()内为n exp()
模: %
平方根: sqrt()
绝对值: abs()
Tangens: atan2()
Sinus: sin()
Cosine: cos()
返回一个随机数: rand()


使用“format”格式化输出

这个要比printf要简单,首先需要在程序显眼的地方定义一下格式,然后就可以使用这个定义好的格式了:

format filehandle =
@<<<<<<<<<<@###.#####@>>>>>>>>>>@||||||||||
$var1, $var3, $var4
.


现在可以使用上面定义的输出格式了:

write FILEHANDLE;

The @<<< does left adjustment, the @>>> right adjustment, @##.## is for numericals and @||| centers.


命令行下使用Perl

输出Perl的版本号: perl -v;
检查perl脚本语法是否正确而不运行: perl -wc <file>;
命令行下直接执行perl语句: perl -e 'command';
循环读取文件,并输出到标准输出: perl -pe 'command' <file>;
循环读取文件,并输出到标准输出: perl -lpe 'command' <file>;
在文件中直接就地修改: perl -i -pe 'command' <file>;
该选项将会以空格分割输入,并把分割结果存放到@F中: perl -a -e 'print if $F[3] =~ /ETH/;' <file>;
循环读入文件,但不是输入: perl -ne 'command' <file>;

猜你喜欢

转载自blog.csdn.net/luguifang2011/article/details/53405163
今日推荐