Perl 语言入门 Learning Perl

Learning Perl 学习笔记 Ch13 文件目录操作

2019-06-14  本文已影响0人  sakam0to
  1. Perl程序运行时以自己的工作目录(working directory)作为起点,Perl提供了一个和shell中cd命令类似的操作符chdir来改变当前的工作目录,但这是一个系统调用,并不等同于shell中的cd,所以shell中类似波浪线~访问用户目录的功能并没有体现在Perl版本的chdir中。同样的,如果chdir执行失败,会将错误信息写入$!
    demo13-1:
#!/usr/bin/perl
print "Where you want to go? ";
chomp(my $path = <STDIN>);
chdir $path or die "Cannot change dir to $path: $!";
./demo13-1
Where you want to go? ../ch12

./demo13-1
Where you want to go? ./test
Cannot change dir to ./test: No such file or directory at ./demo13-1 line 4, <STDIN> line 1.

  1. Unix中,shell会把形如**.sh的文件名通配符展开成完整的文件名列表,这被称为文件名通配,通常这个工作是由Unix shell完成的,需要命令行参数的程序拿到的参数列表就已经是完整的文件名列表了。在Perl中移植了这个功能,对应的操作符是glob
    glob的参数是字符串,里面是文件通配符的模式,返回的是一个元素由文件名字符串组成的列表,且按字母顺序排序。
my @all_files = glob "*";
my @pm_files = glob "*.pm";
my @all_files_including_dot = glob "* .*";

glob出现以前,Perl使用“尖括号语法”——在尖括号内放上文件名通配模式——来实现同样的功能,例如:my @all_files = <*>;这和my @all_files = glob "*"的效果相同。“尖括号语法”支持变量内插,在一对尖括号的变量名,会被自动替换成变量实际的内容。

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

由于尖括号也用来表示从文件句柄中读取输入,这有时会导致歧义,Perl解析器采用的方法是判断尖括号内的是否是严格意义的Perl标识符,若是则表示“从文件句柄读取”;若否,则表示文件名通配。

my @files = <FRED/*>;  ## glob
my @lines = <FRED>;  ## 从文件句柄读取
my $name = "FRED";
my @files = <$name/*>;  ## glob
my @lines = <$name>;  ## 间接句柄读取(indirect filehandler read)

间接文件句柄就是指字符串变量中存储着实际文件句柄的名字,除了可以用上面的尖括号操作符的变量内插以外,还可以用readline操作符读取间接文件句柄

my $name = "FRED";
my @lines = readline $name; ## 等价于 @lines = readline FRED;

  1. 目录句柄和文件句柄类似,只不过它读取到的是目录里的内容(比如文件名,...,次级目录名),Perl提供了一组和文件句柄类似的操作弗来操作目录句柄
操作 目录句柄操作符 文件句柄操作符
打开句柄 opendir open
关闭句柄 closedir close
读取内容 readdir readline

demo13-2

print "Please type in directory: ";
chomp (my $directory = <STDIN>);
opendir DIR, $directory or die "Failed to open dir '$directory': $!\n";
foreach $filename (readdir DIR){
  print "$filename\n";
}
close DIR;
./demo13-2
Please type in directory: ./
.
..
demo13-1
demo13-2
while $file (readdir DIR){
  next if $file eq "." or $file eq ".."; ## 在结果中排除 .(当前目录)和 ..(上级目录)
  next if $file =~ /^\./; ## 在结果中排除文件名以.开头的文件
  next unless $file =~ /\.pm$/; ## 在结果中过滤后缀为pm的文件
}

这里用正则表达式的模式取代了文件名通配的模式


  1. Perl提供一组和Unix系统兼容的文件操作。
    删除文件操作符unlink和unix shell的rm命令类似,可以直接删除文件。unlink的参数是列表,会将列表中的所有文件都删除,所以可以和glob连用,实现rm加文件通配符的效果
unlink glob "*.out" ## 效果和rm *.out相同
foreach my $file (@file_name_list){
  unlink $file or warn "delete file '$file' failed:$!\n"; #每次删除文件失败都会打印失败信息,且不会中断程序
}

  1. Perl的rename函数可以和Unix shell的mv命令一样重命名文件,而且和mv一样,可以用来移动文件 (当然,执行程序的用户必须具有对应目录的权限)
rename "/home/user/oldfile", "/etc/user/arch" or die "move file to new directory failed: $!\n";

rename返回1或0表示操作结果成功或失败,如果失败,Perl把系统调用返回的错误信息放入变量$!
因为rename每次只能操作一个文件,所以如果要实现批量重命名或者移动文件的需求,就需要借助循环来实现

foreach my $old_file (glob "*.old"){
  (my $new_file = $old_file) =~ s/.old$/.new/; ## 借助正则表达式将原来的文件名后缀改为.new
  if(-e $new_file) {
    warn "Cannot rename file '$old_file' because file '$new_file' already exists.\n";
  } else{
    rename $old_file, $new_file or warn "Rename file '$old_file' to '$new_file' failed: $!\n";  
  }
}

  1. Perl支持Unix系统的硬链接和软链接文件,首先需要了解Unix文件系统是如何工作的。Unix系统中,通过文件索引号(inode)标识文件在磁盘上的位置,目录就相当于是一张由文件名和inode组成的对照关系表。不同的文件名可能指向同一个inode,每一个inode都有一个链接计数,代表目录中,直接指向inode的记录,这就是硬链接,Unix中的普通文件,以及对文件的删除,改名等操作,实际上操作的都是硬链接。如果用系统命令ln file1, link_file1创建一个硬链接link_file1,那么它和file1是完全等价的,即使删除了file1,但是link_file1仍然记录了inode信息,仍然可以读取和修改文件内容。但如果所有硬链接都被删除,即使文件内容还存在硬盘上,但是操作系统再也无法访问和修改这部分内容了,所以会把这个inode节点标记为空闲,可以被新的文件内容覆盖。
ln ../ch12 ./ch1111
ln: ../ch12: hard link not allowed for directory

软链接(符号链接)则和inode解耦,它只局限在目录系统中,指向目录系统中的一个位置,甚至可以指向一个实际不存在路径,如果软链接指向的位置确实有文件存在,那么对软链接的操作都会被跳转到实际的文件进行(删除除外,删除只会删除软链接文件自己)。Unix使用命令ln -s file1, soft_link_file1来创建软链接,Perl则提供symlink "file1", "soft_link_file1"来创建软链接,同样有布尔值返回和$!保存的错误信息。同时,Perl的readlink $soft_link函数可以读取符号链接实际指向的位置并返回,如果参数不是符号链接,则返回undef


所以Perl中删除文件的函数名字是unlink ,它可以删除硬链接和软链接,如果删除的是硬链接(也就是普通文件)还会修改inode链接计数,如果inode计数减至0,则释放inode。


  1. 建立目录可以用Perl提供的和Unix shell同名的函数mkdir
mkdir "new_dir", 0755 or warn "create new directory failed: $!\n";

权限位采用unix的三个八进制的形式,即使在非Unix系统上也是如此,如果第二个参数不是八进制数(第一个数位0表示八进制数)则会转换成八进制,特别的是,如果是字符串转换成数字只会转换成十进制数,即使第一位是0也不会转换为八进制数。必须使用函数oct显式进行转换

#!/usr/bin/perl
print "Please type in new directory: ";
chomp(my $new_dir = <STDIN>);
print "Please type in permission(as octal):";
chomp(my $perm = <STDIN>);

mkdir $new_dir, oct($perm) or die "Create new directory '$new_dir' with permission ACL '$perm' failed: $!\n";
print "Success.\n";
./demo13-3
Please type in new directory: test
Please type in permission(as octal):755
Success.

删除目录可以使用Perl提供的rmdir函数,它接受一个目录名做参数,这意味着它一次只能删除一个目录,而不像unlink可以把参数列表中的文件都删除。rmdir只能删除空目录,如果目录非空,包含了文件或者子目录,就需要先逐级删除子目录和文件,或者使用File::Path::rmtree来递归删除


  1. 修改权限和属组使用Perl的chmodchown函数,在Unix shell中也有同名的命令,他们的功能完全一致。
chmod 0755, "demo_file", "demo_file2", "demo_dir";

执行结果返回成功修改的条目数量,如果失败,会将错误信息放在$!变量
Unix shell中的chmod可以接受诸如u+x这样符号表示的权限,但是Perl版本的chmod函数不支持

defined(my $user = getpwnam "test_user") or die "bad users";
defined(my $group = getgrnam "test_user_group") or die "bad group";
chown $user, $group, glob "* .*";

执行结果返回成功修改的条目数量,如果失败,会将错误信息放在$!变量


  1. Perl可以用utime函数修改文件的atime最近访问时间和mtime最近修改时间,至于ctime则没有函数可以修改。utime接收一个参数列表,第一个参数是新的访问时间,第二个参数是新的修改时间,其余参数是文件列表,新的时间必须以时间戳(单位为秒)的形式传入
    demo13-4
#!/usr/bin/perl
my $access_now = time; ## time函数返回当前时间戳
my $modified_yesterday = $access_now - 24 * 60 * 60;
$result = utime $access_now, $modified_yesterday, glob "demo13-*";
print "success modified $result file(s)\n";
上一篇 下一篇

猜你喜欢

热点阅读