在shell中经过test命令或者中括号[]
能够进行文件测试以及其它类型的测试,例如判断文件是否存在,比较操做是否为真等等。perl做为更强大的文本处理语言,它也有文件测试类表达式,并且和shell的文件测试用的字母符号都相似。node
perl中测试文件的属性来源是perl的内置函数stat,它能够得到文件的13项属性。后文会介绍该函数。shell
测试符号都是短横线开头,加一个字母。例如,测试文件是否存在-e "a.log"
。在可能产生歧义的状况下,这些测试符能够用括号包围,例如:(-e "a.log")
。缓存
注意,perl主要应用于Unix类系统,而Unix中一切皆文件,因此下表中出现的"文件"如非特意指明,不然它既可表示普通文件,也表示目录,还表示其它类型的文件。less
如下是文件大小测试符:socket
符号 意义 ---------------------------------------------- -e 文件是否存在 -z 文件是否存在且为空(对目录而言,永远为假) -s 文件是否存在且不为空,返回值是文件大小,单位为字节
其中-s
会返回文件的字节数大小。对于真假的判断,经过这个返回值也能够直接判断,大于0表示非空,等于0表示空。函数
例如:测试
print "not empty" if "-s /tmp/a.log"; print "empty" unless "! -s /tmp/a.log"; print ((-s "/usr/bin/passwd") + 10); # 返回文件字节数+10
如下是文件类型测试符:ui
符号 意义(同时检测文件的存在性) ---------------------------------------------- -f 文件是否为普通文件 -d 文件是否为目录文件 -l 文件是否为软连接(字符连接) -b 文件是否为块设备 -c 文件是不是字符设备文件 -p 文件是否为命名管道 -S 文件是否为socket文件
如下是权限类测试符:操作系统
首先区分一下Effective uid和real uid:unix
如未对文件进行特殊设置,real uid和effective uid是一致的。可是若是设置了setuid属性,那么文件在执行的时候会提高为某用户的权限(通常是提高为root),这时候effective uid就是root,而real uid则是文件调用者的uid。
好比longshuai用户读取一个文件,则这个文件的real uid就是longshuai。好比/usr/bin/passwd这个文件设置了setuid,调用这个程序的用户是real uid,最后执行的时候提权为root用户,那么这个程序的effective uid就是root。
通常来讲,只会去检测real uid的权限属性。只有极少数文件会设置setuid/setgid/sticky,去检测这类文件的权限的机会就更小了。
符号 意义(同时检测文件的存在性) ---------------------------------------------- -r 文件(对effective uid)是否可读 -w 文件(对effective uid)是否可写 -x 文件(对effective uid)是否可执行 -o 文件(对effective uid)的全部者 -R 文件(对real uid)是否可读 -W 文件(对real uid)是否可写 -X 文件(对real uid)是否可执行 -O 文件(对real uid)的全部者 -u 文件是否设置了setuid (setuid只对可执行普通文件有效) -g 文件是否设置了setgid (setgid只对普通文件或目录有效) -k 文件是否设置了sticky (sticky属性只对目录有效)
例如:
print "readable" if -r "/tmp/a.log";
如下是其它测试符:
符号 意义 ---------------------------------------------- -e 文件是否存在 -z 文件是否存在且为空(对目录而言,永远为假) -s 文件是否存在且不为空,返回值是文件大小,单位为字节 -M 最后一次修改(mtime)距离目前的天数 -A 最后一次访问(atime)距离目前的天数 -C 最后一次inode修改(ctime)距离目前的天数 -T 文件看起来像文本文件 -B 文件看起来像二进制文件 -t 文件句柄是否为TTY设备(该测试只对文件句柄有效)
上面-M/-A/-C
会计算天数,它是(小时数/24)来计算的。例如,6小时前修改的文件,它的天数就是0.25天。
上面的-T/-B
是mime类型猜想,perl会根据文件的前几个字节来猜想这个文件,固然,它不必定能猜对,但大多数时候是没什么问题的。
例如:
# 修改文件mtime时间为6小时前 touch -m -t "6 hours ago" /tmp/b.log
若是perl程序内容为:
print (-M "/tmp/b.log");
它的执行结果将输出0.25:
0.250162037037037
如下是文件mime类型猜想。脚本内容以下:
print "text file\n" if -T "$ARGV[0]"; print "Binary file\n" if -B "$ARGV[0]";
执行结果以下:
$ ./19.pl 1.plx text file $ ./19.pl /etc Binary file $ ./19.pl initramfs-3.10.0-327.el7.x86_64.img Binary file $ ./19.pl /bin/ls Binary file
测试符操做能够省略参数,这时它的操做对象是默认变量$_
。可是对于-t
测试符来讲例外,它的默认操做对象是<STDIN>
,由于它的对象是文件句柄,而非文件名。
例如:
#!/usr/bin/perl foreach (`ls`){ chomp; print "$_ is executable\n" if -x; }
可是省略参数的时候,很容易出错。操做符会把后面任何一个非空格字符(串)看成它的参数。例如,-s
返回的是字节大小,你想让它按kb显示。
print (-s / 1024);
但这时的"-s"会把/
看成它的测试对象参数,而不是$_
。因此,省略参数的时候,建议将测试符用括号包围起来:
print ((-s) / 1024);
若是想要同时测试文件的可读性、可写性:
print "writable and readable\n" if -w "/tmp/a.log" and -r "/tmp/a.log";
可是这不是最佳方式,由于perl每执行一次测试符表达式,都须要对文件执行一次stat函数,但实际上第二次测试执行的stat是多余的,由于一次测试就能够获取到文件的全部属性。
perl中有一个特殊的缓存文件句柄_
(就是一个下划线),它能够最近的缓存文件属性信息。
下面是等价的操做:
print "writable and readable\n" if -w "/tmp/a.log" and -r _;
_
的缓存周期能够延续,直到测试下一个文件,因此将两次测试分开写也能够。但须要注意的是,在使用_
的时候要确保它缓存的对象正是所须要的文件属性。
print "a.log writable\n" if -w "/tmp/a.log"; print "b.log writable\n" if -w "/tmp/b.log"; print "b.log writable\n" if -r _;
上面第三个语句中_
缓存的是/tmp/b.log文件的属性。
能够将多个测试操做符连在一块儿写。
例以下面两个语句,它们在最终测试结果上是等价的。第一个语句先测试可写性,再测试可读性,只有二者均为真时if条件才为真。
print "writable and readable\n" if -r -w "/tmp/a.log"; print "writable and readable\n" if -w -r "/tmp/a.log";
可是,返回非真/假值的测试符,须要当心当心再当心。例如,-s
返回的是文件字节数:
@arr=`ls`; foreach (@arr){ chomp; if (-s -f $_ < 512){ # 这里的结果会出乎意料 print "${_}'s size < 512 bytes\n"; } }
上面的if条件子句等价于(-f $_ and -s _) < 512
,它会输出小于512字节的普通文件,以及全部非普通文件。由于and是短路的,若是测试的目标$_
不是一个普通文件,而是一个目录,-f $_
就会返回假,并结束测试,而后这部分表达式和512作数值比较,假对应的数值是0,它永远会返回真。
全部,对于返回非真/假值的测试符,应该避免测试符连写:
@arr=`ls`; foreach (@arr){ chomp; if (-f $_ and -s _ < 512){ print "${_}'s size < 512 bytes\n"; } }
虽然文件测试符有不少,且测试的属性来源都是stat函数,但stat函数返回的信息(共13项属性)比支持的文件测试符还要多。
注:Unix操做系统里有一个stat命令,它也是返回文件的属性信息,和perl的内置stat函数基本相似。
它返回13项属性前后顺序分别是:
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($filename); 属性 意义 --------------------------------------------------- dev :文件所属文件系统的设备ID inode :文件inode号码 mode :文件类型和文件权限(二者都是数值表示) nlink :文件硬连接数 uid :文件全部者的uid gid :文件所属组的gid rdev :文件的设备ID(只对特殊文件有效,即设备文件) size :文件大小,单位字节 atime :文件atime的时间戳(从1970-01-01开始计算的秒数) mtime :文件mtime的时间戳(从1970-01-01开始计算的秒数) ctime :文件ctime的时间戳(从1970-01-01开始计算的秒数) blksize :文件所属文件系统的block大小 blocks :文件占用block数量(通常是512字节的块大小,可经过unix的stat -c "%B"获取块的字节)
须要注意的是,$mode返回的是文件类型和文件权限的结合体,且文件权限并不是直接的8进制权限值,要计算出Unix系统中直观的权限数值(如075五、0644),须要和0777作位运算。
use 5.010; $filename=$ARGV[0]; my @arr = ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($filename); say '$dev :',$arr[0]; say '$inode :',$arr[1]; say '$mode :',$arr[2]; say '$nlink :',$arr[3]; say '$uid :',$arr[4]; say '$gid :',$arr[5]; say '$rdev :',$arr[6]; say '$size :',$arr[7]; say '$atime :',$arr[8]; say '$mtime :',$arr[9]; say '$ctime :',$arr[10]; say '$blksize :',$arr[11]; say '$blocks :',$arr[12];
经常使用的位移是二、七、八、9,分别用来获取权限、大小、atime和mtime属性。这几项能够记忆下来,或perldoc -f stat
查看。
返回结果:
$dev :2050 $inode :67326520 $mode :33188 $nlink :1 $uid :0 $gid :0 $rdev :0 $size :12 $atime :1533544992 $mtime :1533426824 $ctime :1533426824 $blksize :4096 $blocks :8
若是要计算文件权限,则:
printf "perm :%04o\n",$mode & 0777; # 将返回064四、0755类型的权限值
也能够直接一点取出某项属性的值:
my $mode = (stat($filename))[2];
如下是stat函数的其它一些注意事项:
_
_
,它将不会从新测试缓存文件,而是直接返回缓存的属性信息$_
对于软连接文件,stat会追踪到连接的目标。若是不想追踪,则使用lstat函数替代stat。lstat若是测试的目标不是软连接,则返回空列表。