- Perl提供了一组文件测试操作符,可以测试文件的各种属性。文件测试操作符看起来像是由一个连字符
-
和一个字母组成的,操作符的作用对象可以是表示文件名的字符串变量,也可以是文件句柄。 文件测试操作符一般返回的都是布尔值,但也有的会返回有实际意义的值(-s
)。
demo12-1
:
#!/usr/bin/perl
#测试文件是否存在,并且是否在今天修改过
print "Please type in filename: ";
chomp($filename = );
if(-e $filename){
print "A file called '$filename' already exists.\n";
open DEMOFILE, $filename;
if(-M DEMOFILE >= 1){
print "The file $filename modified before today\n"
}else{
print "The file $filename modified today\n";
}
}else{
die "Oops! A file called '$filename' doesn't exist.\n";
}
./demo12-1
Please type in filename: demo12-1
A file called 'demo12-1' already exists.
The file demo12-1 modified today
文件测试操作符 | 意义 |
---|---|
-r/w/x/o | 文件或目录,对目前用户或组是可读/可写/可执行/拥有的 |
-R/W/X/O | 文件或目录,对实际用户或组是可读/可写/可执行/拥有的 |
-e | 文件或目录,是存在的 |
-z | 文件存在且没有内容(对目录来说永远为假) |
-s | 文件或目录存在且有内容(返回以字节为单位的文件大小) |
-f/d/l/S/p/b/c | 是普通文件/目录/符号链接/socket类型文件/命名管道(先入先出队列)/块设备文件/字符设备文件 |
-u/g//k | 文件或目录设置了setuid/setgid/sticky位 |
-t | 文件句柄是TTY设备(类似系统函数isatty();不能对文件名进行此测试) |
-T | 看起来像文本文件 |
-B | 看起来像二进制文件 |
-M | 返回最后一次修改后至今的天数 |
-A | 返回最后一次访问后至今的天数 |
-C | 返回最后一次文件节点编号(inode)变更后至今的天数 |
-f/d/l/S/p/b/c
对应着Unix文件系统的七种文件类型,在非Unix系统上,有的测试可能就没有意义-
-M/A/C
被称为文件时间测试操作符,返回的是换算成天数的具体时间,而非自然天数,所以返回的是浮点数类型。但是时间有时也可能是负数,有两种可能,一是时间被设置在了未来,二是程序已经运行了很长时间,但文件刚刚才发生变更,因为Perl在比较时间时用的是程序启动时的变量$^T
,所以这种情况也有可能发生
下面的程序用来说明变量$^T
在程序启动时已经设定
demo12-2
:
#!/usr/bin/perl
#证明$^T变量在程序启动后不再改变
print $^T."\n";
print "Press to continue...: ";
$input = ;
print "\n$^T\n";
- 对于
-T/B
,因为并没有一个标识符可以用来检测文件是文本文件还是二进制文件,所以Perl是根据文件的内容进行猜测——这就意味着结果可能不一定准确。另外,对于一个不存在的文件,二者都返回假。而对于一个空文件,二者都返回真。 - TTY设备在Linux是一个有历史包袱的文件类型,一般可以被当作键盘一类的交互式设备,
-t
就用来检查是否是这种设备,对管道和普通文件来说,测试的结果都是false - 和其他大多数Perl的操作符一样,当没有指定操作对象时,文件测试操作符也有自己的默认操作对象,那就是
$_
,但-t
是个例外,它默认测试的是STDIN
句柄。但如果没有指定操作对象时,Perl解析器会尽力尝试把文件操作符之后的一切字符都理解为操作对象,这有时会引发错误
#文件名保存在$_中
my $series_in_K = -s / 1000; #ERROR HERE!
如果像上面那样想测试文件名保存在$_
中的文件大小,并除以1000,就会报错,因为Perl会试着把/
理解为操作对象,解决办法是给操作符加上括号(-s)
,或者更稳妥一点,保证操作符后面跟着正确的操作对象
- 对同一个文件进行多次测试可以用逻辑表达式的形式
-e $file and -s $file > 100
,但这会导致额外的系统开销,因为在执行第二条测试的时候,Perl必须重复一些已经在第一次测试中执行过的I/O操作,众所周知,I/O是导致系统变慢的主要原因,特别是外部I/O。所以Perl提供了虚拟文件句柄_
来保存上一次测试过的文件信息,同样的操作就可以写成-e $file and -s _ > 100
。虚拟文件句柄保存上一次查询过的文件信息,所以它不受代码块的约束,子程序和循环代码块都可能导致虚拟文件句柄的改变。
Perl 5.10 引入了栈式(stack)文件测试操作,实现对同一个文件进行多次测试,形如-r -w -x -o -d $filename
测试文件存在,可读,可写,可执行。靠近文件名的测试会先执行,然后从右往左依次执行。
栈式测试操作在只有布尔值结果的时候比较好用,但如果是返回有实际意义的值可能就会引起误会。比如-s -d $file < 512
看起来好像是测试文件存在且大小小于512B,但实际上等价于(-d $file and -s _) < 512
:当-d
返回假时,后者会用布尔值对应的0来和512比较,得到布尔值“真”
- 文件测试操作符并不能测试文件的全部信息,所以Perl移植了Unix系统的
stat
命令,提供了同名函数stat来测试文件属性信息。stat
函数返回一个包含13个元素的列表(如果失败则返回空列表),其中的元素看起来全部由数字填充,stat
函数的参数可以是表示文件名的变量也可以是文件句柄。
demo12-3
:
#!/usr/bin/perl
print "please type in filename: ";
chomp($filename = );
my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat $filename;
print "dev:$dev\nino:$ino\nmode:$mode\nnlink:$nlink\nuid:$uid\ngid:$gid\nsize:$size\natime:$atime\nmtime:$mtime\nctime:$ctime\n";
./demo12-3
please type in filename: demo12-3
dev:2
ino:4503599627830863
mode:33279
nlink:1
uid:1000
gid:1000
size:328
atime:1560309963
mtime:1560309963
ctime:1560309963
对应的内容分别如下
元素 | 含义 |
---|---|
dev和ino | 文件所在设备的编号和文件的inode编号,决定了文件的唯一性,硬链接创建的文件也有相同的编号 |
mode | 权限位集合 |
nlink | 文件或目录的硬链接数 |
uid和gid | 文件拥有者的用户编号及组id |
size | 以字节为单位的文件大小(和-s的测试结果相同) |
atime、mtime和ctime | 三个时间戳,单位为秒 |
- 对符号链接调用
stat
会返回符号链接所指向文件的信息,如果需要查看符号链接本身的信息,需要使用lstat
- 和文件测试操作符一样,
stat
和lstat
的默认操作对象是$_
- Unix系统中的时间大多以时间戳的形式记录,看起来就是一长串毫无实际意义的数字,实际上代表了Epoch开始之后的秒数或毫秒数,在Unix系统上,Epoch的时间是1970年1月1日00:00:00 UTC。如果要获得可读性更好的结果,可以使用
localtime
函数对时间戳进行转换。
localtime
函数的参数是代表时间戳的数字,返回则根据上下文的不同有所区别。在标量上下文中,返回的是形如Wed Jun 12 11:36:23 2019
这样的字符串,在列表上下文中则返回一组数字元素组成的列表
demo12-4
:
#!/usr/bin/perl
print "Current Time:".localtime $^T."\n";
my ($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdat) = localtime $^T;
print "sec:$sec\nmin:$min\nhour:$hour\nday:$day\nmon:$mon\nyear:$year\nwday:$wday\nyday:$yday\nisdat:$isdat\n";
./demo12-4
Current Time:Wed Jun 12 11:39:58 2019sec:58
min:39
hour:11
day:12
mon:5
year:119
wday:3
yday:162
isdat:0
列表中的元素意义如下:
元素 | 含义 |
---|---|
mon | 返回0~11的数字,+1代表月份 |
year | 是一个自1900年开始的年数,+1990是实际的年份 |
wday | 返回0(周日)~6(周六),表示在一周内的第几天 |
yday | 表示在今年的第几天,范围从0~364/365(闰年) |
另外还有一个函数gmtime
它的功能和localtime
相似,区别在于它使用的是格林威治时间而不是本地时区。要取得系统当前的时间戳可以使用time
函数(例子中的$^T
变量在程序启动时设定,不一定代表当前时间),如果没有指定参数,localtime
和gmtime
使用的也是time
函数返回的时间戳。
- 对于
stat
函数返回的权限位信息,需要转换成二级制才能理解,Perl提供了一组和C语言一样对二进制按位操作的操作符:
表达式 | 意义 |
---|---|
10 & 12 |
“按位与”——全1为1,否则为0:1010 & 1100 = 1000(8) |
10 | 12 |
“按位或”——全0为0,否则为1:1010 | 1100 = 1110(14) |
10 ^ 12 |
“按位异或”——相同为0,相反为1:1010 ^ 1100 = 0110(6) |
6 << 2 |
“按位左移”——把符号左边的操作数向左移动,右边补零,移动的位数由右边操作数指定:011000(24) |
25 >> 2 |
“按位右移”——把符号左边的操作数向右移动,越过最低位的舍弃,移动的位数由右边操作数指定:000110(6) |
- 10 |
“按位取反,取决于实际的操作系统的位数,32位系统得0xFFFFFFF5,64位系统得0xFFFFFFFFFFFFFFF5” |
按位操作符可以操作位字符串(bitstring)和整数,如果操作数都是整数,则结果也是整数,如果有一个是“位字符串”则结果是位字符串。对于位字符串,"\xAA"(1010 1010) | "\x55"(0101 0101)
的结果是"\xFF"(1111 1111)