在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若是測試的目標不是軟連接,則返回空列表。