2011-12-09 26 views
7

Tôi muốn lấy một thư mục và cho tất cả các tệp (* .msg) email, loại bỏ 'RE' ở đầu. Tôi có mã sau nhưng đổi tên không thành công.Sử dụng Perl để đổi tên các tệp trong thư mục

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

Nếu bạn in lỗi ($!), Bạn có thể nhận được một ý tưởng về những gì là sai ... – pilcrow

+0

Cảm ơn bạn đã chỉ ra rằng. Lỗi là 'Không có tệp hoặc thư mục nào'. Tôi ngạc nhiên vì tên tập tin $ cũ là chính xác. – Johnathan1

+2

Lưu ý rằng 'đổi tên' không được hỗ trợ đa nền tảng, trong khi chức năng' di chuyển 'Sao chép' của không có – Zaid

Trả lời

9

Nếu thư mục ./emails của bạn có chứa những tập tin này:

1.msg 
2.msg 
3.msg 

sau đó bạn @files sẽ giống như thế ('.', '..', '1.msg', '2.msg', '3.msg') nhưng rename bạn muốn những cái tên như 'emails/1.msg', 'emails/2.msg', vv Vì vậy, bạn có thể chdir trước khi đổi tên:

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

Có thể bạn muốn ch eck giá trị trả lại là chdir.

Hoặc thêm tên thư mục mình:

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 

Bạn có thể muốn kết hợp đọc thư mục của bạn và lọc sử dụng grep:

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

hoặc ngay cả điều này:

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

Tuyệt vời. Cảm ơn bạn đã giải thích điều này. – Johnathan1

5

Bạn dường như giả định glob hành vi giống như thay vì readdir hành vi giống như.

Lệnh gọi hệ thống readdir cơ bản chỉ trả lại tên tệp trong thư mục và sẽ bao gồm hai mục nhập .... Điều này chuyển tới hàm readdir trong Perl, chỉ để cung cấp thêm một chút chi tiết về câu trả lời của mu.

Cách khác, không có nhiều điểm để sử dụng readdir nếu bạn đang thu thập tất cả các kết quả trong một mảng anyways.

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

+1 để nhắc tôi rằng 'glob' tồn tại. Nhược điểm của 'glob' là nó làm phức tạp tên mangling một chút do sự hiện diện của tiền tố' 'email /' ', đó là một vấn đề khá nhỏ và tầm thường để giải quyết. –

2

Như đã đề cập, tập lệnh của bạn không thành công do đường dẫn bạn mong đợi và sử dụng tập lệnh không giống nhau.

Tôi sẽ đề xuất sử dụng minh bạch hơn. Hardcoding một thư mục không phải là một ý tưởng hay, IMO. Như tôi đã học một ngày khi tôi thực hiện một kịch bản để thay đổi một số tập tin gốc, với đường dẫn mã hóa cứng, và một đồng nghiệp của tôi nghĩ đây sẽ là một kịch bản tốt đẹp để mượn để thay đổi các bản sao của anh ấy. Rất tiếc!

Cách sử dụng:

perl script.pl "^RE " *.msg 

ví dụ: regex, sau đó một danh sách tập tin glob, nơi con đường được ký hiệu liên quan đến kịch bản, ví dụ *.msg, emails/*.msg hoặc thậm chí /home/pat/emails/*.msg /home/foo/*.msg. (nhiều bóng tối có thể)

Sử dụng đường dẫn tuyệt đối sẽ khiến người dùng không nghi ngờ gì về việc tệp nào sẽ ảnh hưởng đến nó và cũng sẽ làm cho tập lệnh có thể sử dụng lại được.

Code:

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: Gotta yêu Unix-ification của Windows :) – Zaid

+0

Xin chào, tôi thực sự thích ý tưởng này. Dường như tôi đang gặp lỗi trên dòng $ new = s/$ rx // r. Đầu ra là 'bareword được tìm thấy nơi toán tử được mong đợi gần "s/$ rx // r" và lỗi cú pháp gần giống nhau – Johnathan1

+0

@JP. Oh, đó có thể là do phiên bản perl của bạn .. phiên bản nào bạn sử dụng? – TLP

Các vấn đề liên quan