2009-08-07 10 views
6

約100万の画像で埋められた「画像」というディレクトリがあります。うんLinux:100万ファイルをプレフィックスベースの作成済みフォルダに移動

私は、次の形式にそれらの画像のすべての名前を変更するシェルコマンドを書きたい:

元:filename.jpg
新しい:/f/i/l/filename.jpg

任意の提案ですか?

おかげで、
ダン

+3

100万画像ええ?それはたくさんのポルノです! –

答えて

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; 

${i:0:1}/${i:1:1}/${i:2:1}部分はおそらく、変数、または短いか、または異なるかもしれないが、上記のコマンドは、仕事を取得します。おそらく、パフォーマンスの問題に直面していますが、実際にそれを使用したい場合は、少数のオプションに*.*を狭めるよ

編集(a*.*b*.*または何をフィット):ダン

で述べたように、 mvため i$を追加しました
+1

FYI、 '$ {i:0:1}'の構文はbash-ismです。 Linuxの場合だけで... – derobert

+0

フォルダにいくつかのディレクトリがある場合、このループにも同様に含まれますか? – Dan

+0

修正が必要:*。*のiのための ; mkdir -p $ {i:0:1}/$ {i:1:1}/$ {i:2:1} /;を実行します。 mv $ i $ {i:0:1}/$ {i:1:1}/$ {i:2:1} /;完了しました。 – Dan

2
あなたは例えば、sedは、使用して新しいファイル名を生成することができ

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

だから、あなたはこのような何かを行うことができます(すべてのディレクトリが既に作成されていると仮定) :

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

か、あなたはbashの$(構文を使用できない場合:

をそれが産卵するSEDとMVプロセスの多くだとして
for f in *; do 
    mv -i "$f" "`echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'`" 
done 

しかし、ファイルの数を考えると、あなたは、Perlを使用したいだけの場合があります。

#!/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は確実に同じファイルシステムに限定されていることが唯一の、あなたはそれを回避するためにFile::Copy::moveを使用することができますが。

+0

その解決策に感謝して、それは面白いアプローチです – Dan

+0

ああ、私はテストが見つけただろうことに気づいた:テストが必要です "これはファイルですか?ディレクトリを移動することはありません。非常に簡単に修正することができます(例えばシェルループ内のperl foreachループの最上部にある '-f $ fまたはnext;')。 – derobert

1

私は短いpythonスクリプトを提案します。ほとんどのシェルツールはその多くの入力を嫌うでしょう(xargsがトリックをするかもしれませんが)。例では秒で更新されます。

#!/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

ありがとう、すばらしい解決策のようです。私は試してみる前に、ファイルを私の新しいサーバに転送するのを待つ必要があります(ETA 50時間LOL) – Dan

2

あなたは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 

言うまでもないを言って、あなたは、スペースと短い名前のファイルを心配する必要がある場合があります。

+0

とてもいい解決策です、ありがとうございます – Dan

0

シェルでワイルドカード構文を使用する提案された解決策のいずれかが、あなたが持っているファイルの数のせいで失敗する可能性があります。現在提案されている解決法のうち、perlがおそらく最高です。

しかし、あなたは簡単にこのように任意の数のファイルを扱うためにシェルスクリプトのいずれかの方法を適応させることができます。

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

私はまだPerlを使用しますが、あなただけの単純なUNIXツールを有することに限定している場合このシェルソリューションの1つを、私が示したようなループと組み合わせると、そこにあなたがいるはずです。しかし、それは遅くなるでしょう。

+0

ワイルドカードの構文は細かいものでなければなりません。そのシェルは内蔵されており、目的のプログラムにコマンドラインで渡されません(そうでなければ、コマンドラインが長すぎます)。たとえば、「seq 1 1000000」のiが動作します。 – derobert

+0

私はちょうどテストしました: 'for f in * 'を使うと、1,000,000個のファイルでうまく動作します。遅いですが、機能します。 – derobert

+0

あなたのコメントのおかげで、シェルスクリプトの新機能として役に立ちました – Dan

関連する問題