2011-12-09 53 views
7

我想花一個目錄,併爲所有的電子郵件(*。味精)文件重命名文件,在開始刪除「RE」。我有以下代碼但重命名失敗。使用Perl在目錄

opendir(DIR, 'emails') or die "Cannot open directory"; 
@files = readdir(DIR); 
closedir(DIR); 

for (@files){ 
    next if $_ !~ m/^RE .+msg$/; 
    $old = $_; 
    s/RE //; 
    rename($old, $_) or print "Error renaming: $old\n"; 
} 
+4

如果您打印錯誤($!),你可能會得到的什麼是錯的想法... – pilcrow

+0

感謝您指出了這一點。錯誤是'沒有這樣的文件或目錄'。我很驚訝,因爲$舊文件名是準確的。 – Johnathan1

+2

請注意''rename'不享受跨平臺支持,而'File :: Copy'的'move'功能確實是 – Zaid

回答

9

如果./emails目錄包含以下文件:

1.msg 
2.msg 
3.msg 

那麼你@files看起來像('.', '..', '1.msg', '2.msg', '3.msg')但你rename希望像'emails/1.msg'名,'emails/2.msg'等,這樣你就可以chdir重命名前:

chdir('emails'); 
for (@files) { 
    #... 
} 

你可能想CH請返回chdir返回值。

或添加目錄名稱自己:

rename('emails/' . $old, 'emails/' . $_) or print "Error renaming $old: $!\n"; 
# or rename("emails/$old", "emails/$_") if you like string interpolation 
# or you could use map if you like map 

您可能需要使用grep到您的目錄閱讀和篩選相結合:

my @files = grep { /^RE .+msg$/ } readdir(DIR); 

,甚至這樣的:

opendir(DIR, 'emails') or die "Cannot open directory"; 
for (grep { /^RE .+msg$/ } readdir(DIR)) { 
    (my $new = $_) =~ s/^RE //; 
    rename("emails/$_", "emails/$new") or print "Error renaming $_ to $new: $!\n"; 
} 
closedir(DIR); 
+0

非常好。謝謝你解釋這一個。 – Johnathan1

5

你似乎假設glob般的行爲而不是類似於的行爲。

底層的readdir系統調用只返回目錄內的文件名,並將包含兩個條目...。這會在Perl中傳遞給readdir函數,只是爲了更詳細地介紹mu的答案。

或者,有沒有什麼必要使用readdir如果你收集所有的結果在一個數組反正。

@files = glob('emails/*'); 
+0

+1提醒我存在'glob'。 「glob」的缺點是由於存在「emails /」前綴而使名稱變得複雜一些,但這是一個相當小的問題,而且處理起來也是微不足道的。 –

2

如前所述,由於您期望的路徑和腳本使用的腳本不同,腳本失敗。

我建議一個更加透明的使用。硬編碼目錄不是一個好主意,IMO。正如我學會了一天,當我編寫腳本來改變一些原始文件,硬編碼路徑和我的一位同事認爲這將是一個很好的腳本來借用來改變他的副本。哎呀!

用法:

perl script.pl "^RE " *.msg 

即正則表達式,則文件水珠列表,其中該路徑被表示爲相對於腳本,例如*.msgemails/*.msg甚至/home/pat/emails/*.msg /home/foo/*.msg。 (多水珠可能)

使用絕對路徑將留給用戶無疑是哪些文件,他會被影響,也會使腳本重用。

代碼:

use strict; 
use warnings; 
use v5.10; 
use File::Copy qw(move); 

my $rx = shift; # e.g. "^RE " 

if ($ENV{OS} =~ /^Windows/) { # Patch for Windows' lack of shell globbing 
    @ARGV = map glob, @ARGV; 
} 

for (@ARGV) { 
    if (/$rx/) { 
     my $new = s/$rx//r; # Using non-destructive substitution 
     say "Moving $_ to $new ..."; 
     move($_, $new) or die $!; 
    } 
} 
+0

+1:喜歡Windows的Unix認證:) – Zaid

+0

嗨,我真的很喜歡這個想法。我似乎在我的$ new = s/$ rx // r行上發生錯誤。輸出是'裸字,發現操作員預計在's/$ rx // r'附近,並且在相同的 – Johnathan1

+0

@JP附近出現語法錯誤。哦,這可能是由於你的perl版本,你使用哪個版本? – TLP