Linux: переместите 1 миллион файлов в созданные на основе префиксов папки - PullRequest
6 голосов
/ 07 августа 2009

У меня есть каталог под названием "images", заполненный около миллиона изображений. Да.

Я хочу написать команду оболочки для переименования всех этих изображений в следующий формат:

оригинал: filename.jpg
новый: /f/i/l/filename.jpg

Есть предложения?

Спасибо
Dan

Ответы [ 5 ]

7 голосов
/ 07 августа 2009
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*.* или что вам подходит)

edit: добавлено $ перед i для mv, как отметил Dan

2 голосов
/ 07 августа 2009

Вы можете сделать это как скрипт 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

Излишне говорить, что вам, возможно, придется беспокоиться о пробелах и файлах с короткими именами.

2 голосов
/ 07 августа 2009

Вы можете создать новое имя файла, используя, например, 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 $(:

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

Однако, учитывая количество файлов, вы можете просто захотеть использовать perl, так как в нем много процессов sed и mv для порождения:

#!/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, чтобы обойти это.

1 голос
/ 07 августа 2009

Я предлагаю короткий скрипт на 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 голосов
/ 07 августа 2009

Любое из предложенных решений, использующих подстановочный синтаксис в оболочке, скорее всего завершится ошибкой из-за большого количества файлов, которые у вас есть. Из текущих предлагаемых решений Perl, вероятно, является лучшим.

Однако вы можете легко адаптировать любой из методов сценария оболочки для работы с любым количеством файлов, таким образом:

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

Я бы по-прежнему использовал Perl, но если вы ограничены только простыми инструментами Unix, то объединение одного из вышеперечисленных решений оболочки с циклом, как я показал, должно привести вас туда. Это будет медленно, однако.

...