Perl文件、目錄經常使用操做

注意,這些操做的對象是文件名(相對路徑/絕對路徑),而非文件/目錄句柄,句柄只是perl和文件系統中文件的關聯通道,而非實體對象。html

建立文件

在unix類操做系統中有一個touch命令能夠很是方便的建立文件,還能批量建立一些名稱規律的文件。但實際上touch的主要介紹中倒是"修改文件時間戳",建立文件只不過是它的輔助能力。若是沒有touch命令,如何在shell環境下建立文件?最佳方式是經過重定向的方式。node

在perl中沒有touch相似的功能,因此原始地只能經過open打開輸出類的文件句柄和輸出操做來建立文件(同時也能夠寫入數據)。shell

open FH,">>/tmp/cc.log";
print FH;       # 建立空文件
close FH;

若是想批量建立命名規範的文件,能夠將建立操做放進循環中進行迭代。例如建立cc{1..10}.log這10個文件:數組

foreach (1..10){
    open FH,">cc${_}.log";
    print FH;
    close FH;
}

固然,perl能夠經過system或反引號或exec來和shell進行交互。例如:函數

`touch dd{1..10}.log`;

可是若是想要批量建立的文件不能用一個touch(或少數幾個)來建立的話,仍是建議採用perl的建立方式,由於每次和shell交互touch都fork一次perl進程並解析執行shell語句,文件數量多時,這樣的效率很通常。不過畢竟不太可能用到這種情形。工具

刪除文件

在unix系統裏,使用rm刪除文件/目錄,但它內部調用的是unlink函數。ui

在perl中刪除文件使用perl自帶的unlink函數,它也會調用操做系統的unlink函數來刪除文件。它能夠接一個文件元素,也能夠接一個列表,或者說它的參數上下文就是列表。操作系統

可是注意,unlink沒法刪除目錄,要刪除目錄,見下文。unix

unlink @lotfiles;            # 刪除數組中的文件
unlink 'cc.log';             # 刪除單個文件
unlink 'cc1.log','cc2.log';  # 刪除文件列表
unlink qw(cc3.log cc4.log);  # 刪除文件列表
unlink glob dd*.log;         # 通配文件名刪除

關於文件名通配詳細內容,見後文。code

須要注意,unlink有返回值,返回的是成功刪除的文件數量。

因此,unlink刪除3個文件時,若是它的返回值爲3,表示全刪除成功了,若是返回值爲0表示一個都沒刪除,但若是返回的是1或者2,咱們就沒法判斷哪些文件刪除成功,哪些文件刪除失敗。這時須要在循環中一個一個文件地迭代刪除操做,並給出錯誤提示。

`touch dd{1..10}.log`;
foreach (1..10){
    unlink "dd${_}.log"
        and ++$count       # 注意,++放在變量的前面
        or  warn "Can't remove file dd${_}.log: $!";
}
print "removed $count files\n";

注意上面的and語句中,自增++$count的自增符號放在變量的前面,若是放在後面,會由於初始化$count爲0,$count++表達式返回的值爲0(但$count加完後返回1)而執行or語句。也就是說,刪除第一個文件dd1.log時也會報告警告信息。

建立目錄

  • 建立目錄能夠使用mkdir,它會發起系統調用並返回布爾值:成功/失敗,失敗時會設置$!
  • mkdir時能夠同時指定文件權限,權限須要指定4位的8進制。若是使用變量傳遞這個權限位,應當使用oct()函數保護它不被看成十進制數
  • mkdir不能遞歸建立目錄(unix系統下mkdir -p的功能),能夠使用File::Path裏的make_path函數或mkpath函數,他們等價

例如,建立一個目錄

mkdir "/tmp/test1";
mkdir;                # 等價於mkdir "$_"
mkdir "/tmp/test2",0755   # 權限不能加引號包圍,它是8進制數值
    or die "Can't create directory: $!";

若是是使用變量傳遞權限位,應當使用oct()函數來保護它做爲8進制數。

$perm="0755";
mkdir "/tmp/test3",oct($perm);

mkdir函數沒法遞歸建立目錄。也就是說,當要建立的目錄的上級目錄不存在時,mkdir函數將失敗。若是想遞歸建立目錄,可以使用File::Path裏的make_path函數或mkpath函數,他們是等價的。

這個函數的語法是:

make_path(dir1,dir2,...,{opts})

opts能夠是如下幾種:
mask  => NUM       # mask和mode是同義詞,NUM指定八進制權限值,
mode  => NUM       # 這種方式指定權限值受umask影響,若目錄已存在,則不修改

chmod => NUM       # 直接賦予必定權限值,不受umask影響,若目錄已存在,則不修改

verbose => $bool   # 是否輸出詳細信息,默認啥也不輸出

error => \$err     

owner => $owner    # 這3條都表示爲建立的目錄設置全部者,若是已存在,則不設置
user  => $user     # 能夠使用username,也能夠使用uid,但若是username沒法
uid   => $uid      # 映射爲uid,或者uid不存在,或者無權限的時候,將報錯

group => $group   # 設置所屬組,處理方式和上面全部者的處理方式同樣
use File::Path qw(make_path);
make_path "/test/foo/bar";    # 一次性建立3級目錄
make_path "/test/foo1/bar1",{
    chmod => 0777,
    verbose => 1
}

固然,和shell交互來建立目錄也很是方便:

`mkdir -p /test/a/b/c`;

刪除目錄

  • 刪除空目錄用rmdir,且只能刪除空目錄
  • 要刪除非空目錄,能夠使用File::Path裏的rmtree函數或remove_tree函數,它們等價
  • 刪除目錄時,可隨意對待尾隨斜線問題,由於perl會自動刪除尾隨斜線,以知足多種平臺對尾隨斜線的定義標準

例如,刪除一個目錄

rmdir "/tmp/test";

由於rmdir沒法刪除非空目錄,因此要刪除非空目錄,能夠採用File::Path模塊的rmtree函數。

rmtree的語法:

rmtree($dir1, $dir2,..., \%opt)

opts能夠是如下幾種:

verbose => $bool     # 是否顯示刪除信息,默認啥也不顯示
safe    => $bool     # 刪除時,跳過沒法刪除的對象。例如虛擬文件系統(VMS,如/proc)下有不少是沒法刪除的
keep_root => $bool   # 設置爲真值時,保留頂級目錄,也就是說自刪除目錄內的文件和子目錄,頂級目錄自身不刪除
result => \$res
error => \$err
use File::Path qw(rmtree);
rmtree '/test/foo1',{verbose => 1};

因爲perl沒法向shell同樣能夠直接使用"*"通配,因此若是想刪除目錄內的文件和子目錄,而保留foo1目錄自身,應該設置keep_root選項,而不是用/test/foo1/*的方式來刪除:

rmtree '/test/foo1',{verbose => 1,keep_root => 1};

實在想通配刪除,能夠使用glob來通配,關於通配,見後文:

rmtree((glob '/test/foo1/*'),{verbose => 1});

另外一種保留目錄自身的刪除方式是遍歷頂級目錄,而後迭代刪除遍歷出來的每一個子項:

foreach (</test/foo1/*>){                # 括號內等價於 glob "/test/foo1/*"
    -d $_ ? rmtree $_ : unlink $_;
}

圖方便的話,直接和shell交互也很是方便。

`rm -rf /test/foo1/*`;

chdir切換目錄

  • chdir用於切換到指定目錄,若是不給參數,則回到家目錄
  • chdir由於直接發起系統調用,因此報錯時會設置$!
  • chdir不能使用shell裏的~來表示家目錄
chdir /tmp or die "Can't change dir to /tmp: $!";

獲取當前工做目錄

perl沒有內置函數能夠直接獲取當前工做目錄,但Cwd模塊和更通用的File::Spec模塊都提供了相關工具獲取當前路徑。

例如,Cwd模塊:

use Cwd(getcwd,cwd);
print getcwd();
print cwd();

cwd()比getcwd()要更可移植,它和pwd命令基本一致。注意,cwd()會移除尾隨斜線。

修改權限

  • chmod函數修改文件、文件列表的權限值,它會直接發起系統調用,因此錯誤的話會設置$!
  • 只接受8進制的權限值,不容許使用rwx的權限字符
  • 它返回成功設置權限的數量

例如,設置目錄、文件的權限:

chmod 0700,qw(/test/foo /test/foo1/a.log);
chmod 0700,'/test/foo','/test/foo1/a.log';

修改全部者、所屬組

  • chown能夠同時修改文件/目錄、文件/目錄列表的全部者、所屬組,它會直接發起系統調用,因此報錯時會設置$!
  • chown只接受uid和gid做爲參數,不接受username和groupname
    • 但在unix上顯示的時候,仍是會顯示爲username和groupname,這是操做系統的映射機制
    • 能夠使用getpwnam和getgrnam函數將username/groupname映射爲uid/gid
  • 在uid或gid的參數位置上指定特殊值"-1",表示該位置的全部者或所屬組屬性保持不變
  • 它返回成功設置的文件數量
chown 1001,1001,glob '*.log';   # 第一個1001是user位,第二個1001是group位
chown -1,1002,'a.log';         # uid不變

若是想按照用戶名、組名來設置,使用getpwnam和getgrnam函數:

defined(my $user = getpwnam 'longshuai') or die "bad user";
defined(my $group = getgrnam 'longshuai') or die 'bad group';

chown $user,$group,glob '*.log';

修改文件時間戳屬性

關於文件的時間戳屬性,這裏簡單說明下,若是須要詳細瞭解,參看:http://www.cnblogs.com/f-ck-need-u/p/6995195.html#blog1.3

在unix系統中,要求操做系統維護atime/mtime/ctime三種文件的時間戳屬性:

  • atime:access time,文件最近一次被訪問時的時間戳
  • mtime:modify time,文件內容最近一次被修改時的時間戳
  • ctime:inode change time,文件inode數據最近一次被修改的時間戳

unix的touch命令能夠修改atime和mtime,ctime是操做系統自身維護的,沒法經過上層命令工具直接修改。

Perl的utime函數也能夠修改文件時間戳,語法以下:

utime(Atime,Mtime,FILE_LIST)
  • 也只能修改atime和mtime屬性
  • 會直接發起系統調用,因此失敗時會設置$!
  • Atime和Mtime能夠同時定義爲undef,表示修改成當前時間
    • 但若是隻有一個undef,則對應位置的時間戳會設置爲1970年
  • 它的返回值是成功修改的文件數量
  • 如想保持某項時間戳不變,可用stat函數取出當前時間戳值保存下來

例如,下面修改一堆文件的atime爲當前時間,mtime爲前一小時時間:

$atime=time;
$mtime=$atime - 60*60;
utime $atime,$mtime,glob '*.log';

實現touch文件的等同功能:將文件時間戳設置爲當前時間:

utime undef,undef,'a.txt'
    or die "touch file failed: $!";

若是隻想修改atime,不想修改mtime,則使用stat函數先將當前的mtime屬性值取出保存下來:

$mtime = (stat('a.txt'))[9];
utime time,$mtime,'a.txt'
    or die "touch failed: $!";

其中stat()[9]對應的屬性是mtime,stat()[8]對應的屬性是atime。

相關文章
相關標籤/搜索