Perl目录操作

chdir,在目录树中移动

程序运行时会以自己的工作目录作为相对路径的起点。如果想改变当前的工作目录,应该使用chdir命令,类似于unix的cd命令。

chdir '/etc' or die "can not chdir to /etc : $!";

如果不加参数的调用chdir,则会回到用户主目录。

文件名通配

一般来说,shell会将命令行里的文件名模式展开成所有匹配的文件名,这就是文件名通配,比如,把*.pm这个文件名模式交给echo命令,shell会将它展开名称相匹配的所有命令行参数。运行程序时,如果只有一个带文件名通配的参数,shell会先展开该通配模式,再把结果传递给程序,这样,对程序来说,就好比是看到多个参数:

perl show-args *.txt

注意,show-args程序并不需要知道怎么展开*.txt,因为放在@ARGV了的已经是展开好的名称了。

有时候我们也需要在程序里展开,使用glob操作符:

@ARGV = glob '*';
my @all_files = glob '*.txt';
my @all_files_including_dot = glob '.* *';

如果想一次匹配多种模式,可以在参数中用空格隔开各个模式,*不能匹配到以点号开头的文件。而.*能匹配所有文件,无论是否以点号开头。

因为文件名通配非常耗时,而且还可能在目录太大时崩溃,有责任心的perl黑客会避免文件名通配,而是用目录句柄。

文件名通配的另一种语法

以前的perl语法中<>是glob的替代品,两者效果一样。

my @all_files = <*>;
my $dir = '/etc';
my @dir_files = <$dir/* $dir/.*>;

注意,钻石操作符还可以用文件句柄来读取。到底是读取文件句柄,还是进行文件名通配,需要根据尖括号内的内容来决定:

my @files = <FRED/*>;   文件名通配
my @lines = <FRED>;     从文件句柄读取
my @lines = <$fred>;    从文件句柄读取
my $name = 'FRED';
my @files = <$name/*>;  文件名通配

目录句柄

跟文件操作open,close类似,有opendir,closedir命令,用来读取目录里的文件名,而不是文件内容。

opendir my $somedir,$dirname or die "can not open $dirname : $!\n";
while (my $name = readdir $somedir){
    next if $name =~ /^\./;
    $name = "$dirname/$name";
    next unless -f $name and -r $name;
    }

注意readdir操作符返回的文件名并不含路径名,它们只是目录里的文件名而已,因此,如果我们想获得全路径名,需要自己加上。

删除文件

类似于unix shell的rm操作,perl提供了unlink操作符,来删除指定的文件,并返回成功删除的文件个数,如果删除失败,可以在$!变量里查看。

unlink qw(slate.txt bedrock.txt lava.txt);
unlink glob '*.txt';

unlink不能用来删除目录,只能用来删除文件,如果要删除目录,需要用rmdir函数。实际上,unlink函数只是减少该文件名的链接条目,必要时释放inode。

重命名文件 rename

rename 'old_name','new_name';

跟unix的mv命令一样,这会将名为old_name的文件改为同一个目录下的名为new_name的文件。你甚至可以将文件移动到其他目录中:

rename 'old_name','/some_path/new_name';

来看一个例子,批量的将.old结尾的文件替换成.new结尾。

foreach my $file (glob '*.old'){
    my $new_file = $file;
    $new_file =~ s/\.old$/.new/;
    if(-e $new_file){
    warn "can not rename $file to $new_file : $new_file exists\n";
    }elsif(rename $file,$new_file){
    do nothing;
    }else{
    warn "rename $file to $new_file failed : $!\n";
    }   
}

链接与文件

下面介绍一点unix文件及目录模型的知识。

挂载卷指的是硬盘或者相似设备,例如设备分区,软盘等。其中可能含有任意数量的文件和目录,每个文件都存储在编号为inode对应的位置中,我们可以把它想象成磁盘上的门牌号码。

目录是一种由系统管理的特殊文件,基本上目录是一份文件名和相应inode编号的对照表。目录列出来的内容当中一定会有两个特殊条目:一个是.(称作点),代表目录本身,另一个是..,代表上一层目录。 要在指定目录中创建一个新文件时,系统会新增一个条目来记录文件名与新的inode编号,系统怎么知道哪个inode可用呢,答案是每个inode都有自己的链接数,如果inode未在任何目录里出现,它的链接数一定是0.因此,所有链接数为0的inode都可以用来存放新的文件。

每个目录都有.这个条目,它会指回目录本身的inode,所以,任何目录的链接数都至少是2:一个位于它的上层目录的列表里,另一个位于它本身的列表里,除此之外,如果里面有子目录,则每个子目录还会通过..在增加一个链接。链接数代表的是该inode的真实名称的数量。

link 'chicken.txt','egg.txt';
    or warn "can not link chicken to egg : $!\n";

运行程序后,egg.txt这个名称就会指向文件chichen.txt.此时chicken.txt的inode对应的链接数就是2了。

软链接 && 硬链接

当你在egg.txt后面增加一行字,改变会反应到chicken.txt里。如果你删除了其中的任何一个文件,另外一个文件依然可以继续访问,并且,文件内容只会有一份备份,无论创建多少个链接,文件的总大小并不发生变化,还是等于单一文件的大小。

关于目录列表链接的一个规定:在目录列表中所有的inode指向的文件都必须在同一个挂载卷中,这样一来,即使物理介质移动到另外一台机器上,其中的目录和文件间的链接仍然有效,正因为如此,rename虽然可以将文件移到别的目录里,但是来源和目的地必须位于同一个文件系统(挂载卷)上。如果要跨盘移动,就必须重新部署inode的数据,这是一个复杂的过程。

链接的另外一个限制就是不能为目录建立额外的名称,这是因为目录必须按照层次排序。

前面介绍的文件链接link函数,构建的是硬链接,使用symlink函数可以构建软链接,也叫符号链接(symbolic link)。

软链接可以指向任何文件名,软连接跟硬链接不同,它不会增加链接数,因此,它不能像硬链接那样防止数据丢失。并且,软链接可以为目录创建链接。例如:

symlink 'chicken.txt','egg.txt';
    or warn "can not link chicken to egg : $!\n";

它只是告诉系统:“你如果是来这里找egg.txt,请到chicken.txt那里去”。如果把chicken.txt删除了,系统就不能跟随egg.txt这个软连接了。

软链接的用处:当你经常要访问一个非常长的某个名称例如/usr/local/opt/system/httpd/roo-dev/users/staging/barney/cgi-bin,为了免去键入的麻烦,你可以为它创建软链接/home/barney/my_stuff,如果你在自己的主目录创建了my_stuff/bowling文件,该文件的真实名称会是/usr/local/opt/system/httpd/roo-dev/users/staging/barney/cgi-bin/bowling。

在你的系统里,/usr/bin/perl常会是符号链接,都指向真正的Perl二进制文件。因此,如果你刚刚编译了一份新的perl,只需要改变一个符号链接就可以让程序运行新的perl了。简直不能更方便。

从上面可以看出,软硬链接都非常管用,这可能是一个令人惊讶的现实。

readlink

要取得符号链接指向的位置,可以使用readlink函数。

my $perl = readlink '/usr/local/bin/perl';

上面这个例子会告诉你实际的perl程序究竟躲在何处。

创建和删除目录

两个参数:第一个是要创建的目录名称,第二个是权限值,一般采用八进制数。

mkdir 'fred',0755 or warn "can not make fred directory : $!\n";

删除目录时,目录必须为空,否则会导致失败,可以先用unlink删除目录中的内容,再移除空目录。

unlink glob "$some_dir/* $some_dir/.*";
rmdir $some_dir;

修改权限

类似于unix的chmod命令,perl也是用chmod函数来进行同样的操作:

chmod 0775,'fred','barney';

第一个参数是要修改成的权限值,后面是带修改文件或目录的列表,函数会返回成功修改的条目数量。

修改隶属关系

你可以用chown函数修改文件的拥有者及其所属组。拥有者和所属组会被同时更改,并且在指定时必须给出数字形式的用户标示符和组标示符。如果你只知道类似于merlyn这样的字符串,可以用getpwnam和getgrnam来取得相对应的数字编号:

defined (my $user = getpwnam 'merlyn') or die 'bad user';
defined (my $group= getgrnam 'users') or die 'bad group';
chown $user,$group,glob '/home/merlyn/*';

chown会返回受影响的文件数量,在发生错误时将错误信息存在$!变量中。

修改时间戳

在某些罕见的情况下,我们可能需要修改某个文件最近的更改或者访问时间以欺瞒其他程序,这时我们可以用utime函数来造假。它的前两个参数分别是新的访问时间和修改时间,其余参数是要修改时间戳的文件名列表。

my $now = time;
my $ago = $now - 24*60*60;
utime $now ,$ago ,glob '*';

Comments !