2009-08-07 31 views
6

Tôi có một thư mục có tên là "hình ảnh" chứa khoảng một triệu hình ảnh. Vâng.Linux: Di chuyển 1 triệu tệp vào thư mục được tạo tiền tố

Tôi muốn viết một lệnh shell để đổi tên tất cả những hình ảnh sang định dạng sau:

gốc:filename.jpg
mới:/f/i/l/filename.jpg

Bất kỳ lời đề nghị?

Cảm ơn,
Dan

+3

1 triệu hình ảnh eh? Đó là rất nhiều khiêu dâm! –

Trả lời

6
for i in *.*; do mkdir -p ${i:0:1}/${i:1:1}/${i:2:1}/; mv $i ${i:0:1}/${i:1:1}/${i:2:1}/; done; 

Phần ${i:0:1}/${i:1:1}/${i:2:1} lẽ có thể là một biến, hoặc ngắn hơn hoặc khác nhau, nhưng lệnh trên được công việc làm. Có thể bạn sẽ phải đối mặt với vấn đề hiệu suất nhưng nếu bạn thực sự muốn sử dụng nó, thu hẹp *.* để lựa chọn ít hơn (a*.*, b*.* hoặc những gì phù hợp với bạn)

chỉnh sửa: thêm một $ trước i cho mv, như ghi nhận của Dan

+1

FYI, cú pháp '$ {i: 0: 1}' là một bash-ism, có lẽ là OK trên Linux, nhưng chỉ trong trường hợp ... – derobert

+0

Nếu có một vài thư mục trong thư mục, vòng lặp này sẽ bao gồm chúng không? – Dan

+0

Cần một chỉnh sửa: cho i trong *. *; làm mkdir -p $ {i: 0: 1}/$ {i: 1: 1}/$ {i: 2: 1} /; mv $ i $ {i: 0: 1}/$ {i: 1: 1}/$ {i: 2: 1} /; làm xong; – Dan

2

Bạn có thể tạo ra các tên tập tin mới sử dụng, ví dụ, sed:

$ echo "test.jpg" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/' 
t/e/s/test.jpg 

Vì vậy, bạn có thể làm một cái gì đó như thế này (giả sử tất cả các thư mục đã được tạo ra) :

for f in *; do 
    mv -i "$f" "$(echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/')" 
done 

hoặc, nếu bạn không thể sử dụng cú pháp bash $(:

for f in *; do 
    mv -i "$f" "`echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'`" 
done 

Tuy nhiên, xem xét số lượng các tập tin, bạn có thể chỉ muốn sử dụng perl như đó là rất nhiều các quá trình sed và mv để đẻ trứng:

#!/usr/bin/perl -w 
use strict; 

# warning: untested 
opendir DIR, "." or die "opendir: $!"; 
my @files = readdir(DIR); # can't change dir while reading: read in advance 
closedir DIR; 
foreach my $f (@files) { 
    (my $new_name = $f) =~ s!^((.)(.)(.).*)$!$2/$3/$4/$1/; 
    -e $new_name and die "$new_name already exists"; 
    rename($f, $new_name); 
} 

Đó perl được chắc chắn giới hạn cùng hệ thống tập tin chỉ , mặc dù bạn có thể sử dụng File::Copy::move để giải quyết vấn đề đó.

+0

cảm ơn cho giải pháp đó, nó là một cách tiếp cận thú vị – Dan

+0

oh, tôi nhận thấy một điều mà thử nghiệm sẽ tìm thấy: Có cần phải là một thử nghiệm "đây có phải là một tập tin?" vì vậy nó không di chuyển các thư mục. Khá dễ sửa chữa (ví dụ: '-f $ f hoặc tiếp theo;' ở đầu vòng lặp forl, tương tự như trong vòng lặp vỏ) – derobert

1

Tôi đề xuất một tập lệnh python ngắn. Hầu hết các công cụ vỏ sẽ balk tại đầu vào đó nhiều (mặc dù xargs có thể làm các trick). Sẽ cập nhật với ví dụ trong giây lát.

#!/usr/bin/python 
import os, shutil 

src_dir = '/src/dir' 
dest_dir = '/dest/dir' 

for fn in os.listdir(src_dir): 
    os.makedirs(dest_dir+'/'+fn[0]+'/'+fn[1]+'/'+fn[2]+'/') 
    shutil.copyfile(src_dir+'/'+fn, dest_dir+'/'+fn[0]+'/'+fn[1]+'/'+fn[2]+'/'+fn) 
+0

cảm ơn, trông giống như một giải pháp tuyệt vời. Tôi cần phải chờ đợi cho các tập tin để chuyển đến máy chủ mới của tôi trước khi tôi có thể dùng thử (ETA 50 giờ lol) – Dan

2

bạn có thể làm điều đó như một kịch bản bash:

#!/bin/bash 

base=base 

mkdir -p $base/shorts 

for n in * 
do 
    if [ ${#n} -lt 3 ] 
    then 
     mv $n $base/shorts 
    else 
     dir=$base/${n:0:1}/${n:1:1}/${n:2:1} 
     mkdir -p $dir 
     mv $n $dir 
    fi 
done 

không cần phải nói, bạn có thể cần phải lo lắng về không gian và các tập tin với những cái tên ngắn.

+0

giải pháp rất tốt đẹp, cảm ơn bạn – Dan

0

Bất kỳ giải pháp được đề xuất nào sử dụng cú pháp ký tự đại diện trong trình bao sẽ có thể không thành công do số tệp tuyệt đối mà bạn có. Trong số các giải pháp được đề xuất hiện tại, giải pháp perl có lẽ là tốt nhất.

Tuy nhiên, bạn có thể dễ dàng thích nghi với bất kỳ phương pháp kịch bản để đối phó với bất kỳ số lượng các tập tin như sau:

ls -1 | \ 
while read filename 
do 
    # insert the loop body of your preference here, operating on "filename" 
done 

tôi vẫn sẽ sử dụng perl, nhưng nếu bạn bị giới hạn chỉ có các công cụ đơn giản unix xung quanh, sau đó kết hợp một trong các giải pháp shell ở trên với vòng lặp như tôi đã hiển thị sẽ đưa bạn đến đó. Tuy nhiên, nó sẽ rất chậm.

+0

Cú pháp ký tự đại diện phải là tốt, một trình bao được tích hợp sẵn và không được truyền trên dòng lệnh tới chương trình có mục đích (nếu không, chắc chắn, dòng lệnh sẽ quá dài). ví dụ, trong ví dụ 'seq 1 1000000'. – derobert

+0

Tôi vừa thử nghiệm: sử dụng 'for f in *' hoạt động tốt với 1.000.000 tệp. Chậm, nhưng nó hoạt động. – derobert

+0

cảm ơn cho bình luận của bạn, nó rất hữu ích vì tôi rất mới để shell scripting – Dan

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