Perl目录操作

版权声明:转载请说明出处! https://blog.csdn.net/qq_39556143/article/details/85270106

Perl目录操作

1. 工作目录

1.1 当前工作目录

程序运行的时候总有一个相应的工作目录,后续要做的事情都是从这个目录开始的。借助标准模块的Cwd模块,我么可以查看当前目录。

#!/usr/bin/perl
#code1
use v5.10;
use Cwd;
say "The cuurent working directory is:",getcwd();
#打印的路径应该就是程序的保存位置,和在Shell中执行pwd的结果相同。
#code2
#如果使用相对路径(没有提供文件系统树顶端的路径)打开文件,perl会按照当前目录定位这个路径。
#当前目录/home/fred,运行以下代码读取文件:
open my $fh,'<','relative/path.txt';
#perl会打开/home/fred/relative/path.txt文件

1.2 修改工作目录

如果不希望当前工作目录在程序所在目录,可以使用 chdir 操作符修改,它的用法和 shell 中的 cd 一致

  • chdir ‘/etc’ or die “Can’t change to /etc: $!”
  • 由Perl程序启动的所有进程都会继承Perl程序的工作目录。我们可以改变perl的工作目录,但是不能改变其父进程,即shell的工作目录。
  • chdir 不加参数,Perl 会认为我们需要回到用户主目录,和在 shell 中使用不带参数的 cd 一致。

1.3 文件名通配

一般而言,shell 会将命令行中的文件名模式展开为所有匹配的文件名,这就叫做文件名匹配 (glob),比如吧*.pm这个参数交给 echo, shell 将会将它展开为所有相匹配的文件列表。

#!/usr/bin/perl
$ echo *.pm                       # $表示在命令行输入
#打印 
banery.pm fred.pm dino.pm
#这里的echo命令并不知道此刻使用了文件名通配,因为shell会先把*.pm展开为一系列的文件名,然后再交给echo处理。
#以下程序只是简单地输出所有命令行参数
foreach $arg (@ARGV) {           #程序不进行文件名统配,shell已经展开并传入@ARGV,perl只时是用数组而已
    print "One arg is $arg\n";
}
#我们在命令行执行以下命令
$ perl show_arg *.pm       
#打印
One arg is banery.pm 
One arg is fred.pm
One arg is dino.pm
  • 不过有时候需要在程序内部使用类似 ‘*.pm’ 的模式我们可以使用 glob 操作符;
  • glob操作符的效果和 shell 统配的结果完全相同。
#!/usr/bin/perl
my @all_file = glob '*';          #取得当前目录中的所有文件并按照字母进行排序,不包括以点号开头的文件   
my @pm_file  = glob '*.pm';       #得到的列表和之前使用的*.pm时的相同;
#如果要匹配不同的模式,可以在参数中用 空格 隔开各个模式
my @all_file = glob '.* *';       #取得当前目录中所有的文件并按照字符进行排序,包括以点号开头的文件

1.4 文件名通配的隐式语法

  • 在 glob 操作符出现之前,程序可以使用尖括号语法调用相同的功能,看起来和读取文件文件句柄类似;
#!/usr/bin/perl
my @all_perl = <*>;                   #效果和 my @all_file = glob '*'; 一致
#perl会把尖括号内出现的变量替换成它的值,类似于变量内插,这表示使用文件名通配之前,perl会把变量展开
my $dir = '/etc';
my @all_file = <$dir/* $dir/.*>;      #先展开 $dir 的值,然后尽心文件名通配,最后得到 /etc 目录下的所有文件
  • 那么,Perl如何区分文件名通配操作和读取文件呢?
    • 如果<>内时满足Perl标识符条件的,就可以按照文件句柄进行处理 (字母+下划线+数字)
    • 除此之外,使用文件名通配操作
  • Perl 在编译极端就决定了使用文件名通配还是从文件句柄读取,因此与变量内容无关。
#!/usr/bin/perl
my @files = <FRED/*>;   #文件名通配
my @files = <FRED>;     #从文件句柄读取
my @files = <$FRED>;    #从文件句柄读取,如果<>内时一个简单地标量变量,那么就是间接文件句柄读取
my $name  = 'fred';
my @files = <$name/*>;  #文件名通配
#可以使用 readline 读取间接文件句柄(用的机会较少)
my $name  = 'FRED';
my @lines = readline FRED;   # 从 FRED 读取
my @lines = readline $name;  # 从 FRED 读取

1.5 目录句柄

  • 如果想从目录获得文件列表,可以使用目录句柄 (directory handle);
  • 目录句柄看起来像文件句柄,使用起来区别不大;
  • 目录句柄和文件句柄类似,会在程序结束或者重启打开其他目录前自动关闭。
  • 操作符变化:
操作符类型 文件 目录
打开 open opendir
读取 readline readdir(得到文件名,不是文件内容)
关闭 close closedir
  • 目录句柄也可以使用裸字作为目录句柄名称,但这样存在和文件句柄类似的问题,不能作为参数传递。如果把句柄(文件或目录都可以)指定到标量变量中,就可以传递参数,使用更方便。
  • 在标量环境中,readdir函数返回下一个目录记录,如果没有下一个目录记录,返回undef。在列表环境中,它返回在该目录中所有剩下的记录,如果剩下没有记录了,那么这个返回可能是一个空列表。
#!/usr/bin/perl
my $dir_to_process = '/etc';
opendir DIR, $dir_to_process;               #用裸字存储目录句柄
opendir my $dh,$dir_to_process;             #用标量变量存储目录句柄
foreach ($some = readdir $dh) {             #此处不能用DIR替换$dh
	next unless $name =~ /\.pm\z/;          #这里是正则表达式,不是文件名通配
	next if $name eq '.' or $name eq '..';  #'.'表示当前目录,'..'表示上一级目录
	...
}
#需要注意的是readdir读取到的文件名不包含路径,只是目录下的文件名而已
#如果/etc目录下有文件test.pl,我们只能看到test.pl,但是看不到/etc/test.pl这样的绝对路径
#此时需要我们手动添加:
my $dir_to_process = '/etc';
opendir my $dh,$dir_to_process or die "Can't open \$dir_to_process";
while (my $name = readdir $dh) {
	next if $name =~ /\A\./;                 #不处理以点号开头的文件
	$name = catfile($dir_to_process,$name);  #拼接为完整路径
	next unless -f $name and -r $name;       #只处理可读文件
	...
}

2. 文件和目录的操作

2.1 删除文件

  • Unix中: $ rm slat fred
    • 在shell中,我们可以使用 rm 命令来删除单个或者多个文件;
  • Perl中: unlink ‘slat’,‘fred’; unlink qw/slat fred/;
    • 在perl中我们可以使用 unlink 操作符指定要删除的文件。
    • 返回值为成功删除文件的数目。
  • Perl中: link ‘chicken’,‘egg’;
    • 为文件新增硬链接,如果文件是一个房间,那么现在文件有两个门,chicken+egg;
    • 删除任何chicken 和 egg 任何一个文件,文件都保留在存储空间。
  • Perl中: symlink ‘chicken’,‘egg’;
    • 为文件新增软链接,如果文件是一个房间,那么现在文件有一个门chicken,egg告诉用户门在chicken那里;
    • 删除chicken,文件释放存储空间,egg指向空文件;
    • 删除egg,文件依然存储在原来的存储空间,chicken仍然指向该文件;

链接是文件名和磁盘上文件具体存放位置之间的映射关系,但是有些操作系统允许出现多个直接指向相同存储区域的文件链接。直到所有指向链接都销毁后,操作系统用才能回收目标存储区域,unlink 的作用是销毁给定文件名到存储区域的链接,如果这是最后一个链接,那么存储文件的存储区域也就被释放了。

  • 由于 unlink 可以对文件列表进行操作,而且 glob 返回的也是文件列表。因此,两者可以搭配使用。
#!/usr/bin/perl
unlink glob '*.o';   #和在shell中执行 rm *.o 效果一致,但是省去了进程开销
#unlink的返回值是成功删除的文件数目:
my $success = unlink qw/slat fred/;
unlink $file or say "\$file unlink failed: $!";    # $!返回失败的错误类型
  • 注: 不能使用unlink删除目录,目录删除应该使用rmdir

2.2 重命名文件

  • Unix:mv old new
    • 在 shell 中可以使用 mv 命令进行文件名的更改;
  • Perl:rename ‘old’,‘new’;
    • 在 perl 中可以使用 rename 实现和mv一样的功能;
    • 成功返回真,否则返回假,也可以使用 $! 查看错误信息;
    • 参数能不能是列表???
#!/usr/bin/perl
rename '/etc/som/some_file','som_file';   #rename也可以实现文件移动功能
rename '/etc/som/some_file'=>'som_file';  #rename也可以使用胖箭头替换逗号
#使用rename实现文件批量修改(把文件名为.old的文件全部改为文件名为.new)
foreach my $file (glob "*.old") {
	(my $newfile = $file) =~ s/\.old$/.new/;      #先将$file的值给$newfile,然后对$newfile进行正则表达式匹配
	                                              #s/a/b/,a中是正则表达式,b是字符串。所以a中需要的点号需要转义,但是b中的点号不需要转义
	# my $newfile = $file =~ s/\.old$/.new/r;    #可以使用修饰符r,省略括号
	if (-e $newfile) {
		warn "can't rename $file to $newfile, $newfile exist\n";
	} else if (rename $file,$newfile){
	    say "rename $file to $newfile success."
	} else {
		warn "rename $file to $newfile failed: $!"
	}
}

2.3 创建和删除目录

  • 创建目录的命令:mkdir ‘directory_name’, 0755;
    • ‘’ 内为目录名称,0755位目录的初始权限:-rwxr-xr-x
    • mkdir 不要求第二个参数是八进制的,他只是需要某个数字,除非你能迅速算出 0755 的493,否则不如直接让Perl进行计算,并且开头的0不能胜率,否则755会被认为是十进制,对应八进制的1363.
#!/usr/bin/perl
mkdir 'directory_name',0755 or warn "Can't create directory: $!";
#mkdir的第二个参数必须是数字,不能是字符串如下:
my $name = 'fred';
my $permissions = '0755';
mkdir $name,$permissions;  #糟糕,0755会被当做十进制进行处理,用1363的权限来建立文件目录
#我们可以使用oct()函数,强行把字符串当做八进制
mkdir $name,oct($permissions);  
#使用oct()函数可以方便我们从命令行取得参数:
my  ($name,$perm) = @ARGV;    #取命令行最先传入的两个参数,作为目录名称和权限
mkdir $name,oct($perm) or die "can't create directory: $!";
  • 删除目录的命令:rmdir ‘directory_name’;
    • rmdir 不能删除目录列表,每次只能删除一个目录( unlink 的参数可以是列表,但是 rmdir 的参数不能是列表)
    • rmdir 删除空目录时会导致失败,这时候需要先删除文件目录中的内容,然后删除已经清空的目录。
#!/usr/bin/perl
#每次只能删除一个空目录
foreach $dir (qw(fred dino betty)) {                        #rmdir的参数不能是列表
	rmdir $dir or die "can't delete $dir directory: $!\n";  #删除非空目录时失败
}
#如果我们需要一个目录存储程序产生的临时文件
my $temp_dir = "/tmp/scratch_$$";        #使用当前进程ID命名变量
mkdir $temp_dir,0755 or warn "Can't create directory: $!";
...
#将$temp_dir当做临时文件的存储目录
...
unlink glob "$temp_dir/* $temp_dir/.*";  #删除临时文件目录中所有文件
rmdir $temp_dir;                         #现在是空目录,可以删除了 (如果文件目录中有子文件目录,失败)
  • 注意:
    • 初始的临时目录名将会包含当前的进程标识符,每个当前运行的进程都有一个独一无二的进程数字代号,在perl中,这个数字代号存储在变量$$中。

2.4 修改权限

  • Perl中也可以使用chmod来修改文件或目录的权限:
    • chmod 0755, ‘fred’, ‘barney’;
    • 返回值为操作成功的条目数量;
    • 第一个参数表示权限0755,和前面 mkdir 的用法一致,也可以使用oct()函数;
    • Unix中的chmod接受用符号表示的权限,如+x,go=u-w,但是 perl 中的 chmod 函数不允许使用符号。

2.4.1 修改文件属主

  • 只要操作系统允许,可以使用 chown 改变文件的属主和其用户组,属主和用户组会被同时修改,并且在指定时必须给出数字形式的用户标识符和组标识符。
  • 返回操作文件成功数目。
#!/usr/bin/perl
my $user = 1004;
my $group = 100;
chown $user,$group,glob "*.o";    #第一个参数为用户标识符的数字形式,第二个参数为用户所属组的数字形式
                                  #chown的第三个参数是要操作的文件,可以接受列表输入
#如果需要处理的不是数字,需要用 getpwnam 函数将用户名转换为用户编号,用 getgrnam 得到用户组的组编号:
defined(my $user = getpwnam 'fred') or die "bad user\n";
defined(my $group = getgrnam 'grp1') or die "bad group\n";
chown $user,$group,glob "*.o";

2.4.2 修改时间戳

  • 某些罕见的情况下,可能需要修改某个文件最近的更改或访问时间来欺骗其他程序,此时我们需要用utime函数来造假。
  • utime $now,$ago,glob “*.old”;
    • 第一个参数atime:新的访问时间;(使用内部时间戳的格式,和 stat 返回的时间格式一致)
    • 第二个参数mtime:新的更改时间;(使用内部时间戳的格式,和 stat 返回的时间格式一致)
    • 其余参数:要修改时间戳的文件列表;
#!/usr/bin/perl
my $now = time;                    #time时可以返回当前时间的内部时间戳的函数
my $ago = $now - 24*60*60;         #目前时间减去一天的秒数
utime \$now,\$ago,glob "*.old";    #将最后的访问时间改为现在,最后的更改时间改为一天前的现在
  • 当然,我们也可以把时间设置为过去和未来的任何时间,因此可以修改为未来才会产生的文件。
  • 当文件有任何变动时,第三个时间戳(ctime)一定会被设为当前值,没有函数可以篡改,就算用utime修改成功,也会被立刻设为当前的值,这是因为它主要用给增量备份的程序使用。(待增补)

猜你喜欢

转载自blog.csdn.net/qq_39556143/article/details/85270106