转载于 http://mylinux.5d6d.com/study/27.htm
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;
命令行中提及程序的名字
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($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用户以及网络相关信息
或者通过指定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”格式化输出
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>; |