переименуйте все файлы в каталоге в хэш md5 их имени файла (не содержимого)

11

Я очень новичок в Linux / командной строке и мне нужно зашифровать имена файлов 10K + (уникальные имена), чтобы они соответствовали зашифрованному имени MD5 в базе данных mySQL.
Я видел, как вы можете переименовать каталог файлов и как получить хеш файла ( mdsum? ), Но я застрял на том, как получить хэш имени файла, а затем переименовать этот файл в сгенерированный хеш, сохраняя расширение то есть

mynicepicture.jpg > fba8255e8e9ce687522455f3e1561e53.jpg 

Кажется, это должно быть простое переименование или mvстрока, но я не могу обойти это.
Большое спасибо за ваши идеи

PS Я видел использование функций Perl в нескольких примерах, близких к тому, что я ищу, но понятия не имею, где и как их использовать.

BradH
источник
3
Вы уверены , что хотите иметь хэш из файла имя , а не содержимое файла?
Anthon
12
Примечание. Хэш MD5 не является устройством шифрования. MD5 даже не криптографический хеш. Хеш, любой хеш, является односторонним преобразованием набора данных в число. Это не обратимо. Настоящее шифрование всегда обратимо (учитывая ключ, используемый для шифрования).
Кусалананда
1
fba8255e8e9ce687522455f3e1561e53для чего используется MD5 mynicepicture, означает ли это, что расширение должно быть удалено перед хэшированием?
Кусалананда
@dessert Я имею в виду , что нет грантополучателей , если вы md5sum <<<"file name"в file nameфайл существующего или нет, потому что он рассматривает в качестве строки , за исключением подачи его с именем существующих файлов.
αғsнιη

Ответы:

14

Вы не сказали, какую оболочку вы хотите использовать, поэтому я предполагаю, что Bash - ответ требует корректировки для работы с другими оболочками.

for i in *; do sum=$(echo -n "$i"|md5sum); echo -- "$i" "${sum%% *}.${i##*.}"; done

Версия скрипта:

for i in *; do
  sum=$(echo -n "$i" | md5sum)
  echo -- "$i" "${sum%% *}.${i##*.}"
done

Этот простой forцикл берет каждый файл в текущем каталоге, вычисляет сумму md5 его имени и выводит его. Используйте это, чтобы проверить функциональность, если вы хотите начать переименование заменить второеecho на mv.

Пояснения

  • echo -n "$i" | md5sum- вычислить сумму md5 полного имени файла, включая расширение файла ( Piping ), чтобы исключить изменение расширения echo -n "$i"одним из следующих:

    ${i%%.*}
    sed 's/\..*//' <<< "$i"
    echo "$i" | sed 's/\..*//'
  • sum=$(…)- выполнить и сохранить вывод в $sum( Подстановка команд )

  • ${sum%% *}- выводить все до первого пробела ( подстановка параметров ), так же, как один из следующих:

    $(sed 's/ .*//' <<< "$sum")
    $(echo "$sum" | sed 's/ .*//')
  • ${i##*.} - вывести все после последней точки (Подстановка параметров), так же, как одно из следующего:

    $(sed 's/.*\.//' <<< "$i")
    $(echo "$i" | sed 's/.*\.//')

Если вам нужно рекурсивно переименовывать файлы в разных папках, используйте findс -execопцией.

Десерт
источник
6
#!/bin/bash

md5name () {
    local base=${1##*/}
    local ext=${base##*.}
    local dir=${1%/*}

    printf '%s' "${base%.$ext}" | md5sum |
    awk -v dir="$dir" -v ext="$ext" '{ printf("%s/%s.%s\n", dir, $1, ext) }'
}

dir=$HOME  # where your files are

for pathname in "$dir"/*; do
    test -f "$pathname" || continue
    echo mv "$pathname" "$( md5name "$pathname" )"
done

Этот bashскрипт использует md5sumутилиту из GNU coreutils для вычисления MD5-хеша из базового имени (без расширения) любого заданного пути. Вспомогательная функция md5nameвыполняет фактические вычисления и выводит новое имя с полным путем и расширением.

md5nameФункция используется , awkчтобы собрать новое имя из частей данного имени пути и результата от md5sum.

Примеры использования самой функции:

$ md5name '/some/path/file name here.extension'
/some/path/c9e89fa443d16da4b96ea858881320c9.extension

... где c9e89fa443d16da4b96ea858881320c9хеш MD5 строки file name here.

Удалите echoиз скрипта вверху, чтобы фактически переименовать файлы. Возможно, вы захотите сохранить выходные данные исходного скрипта в файл (с echoуказанием на месте), если вам в какой-то момент потребуется восстановить имена файлов до их оригиналов.

Обратите внимание, что при выполнении этого дважды для набора файлов будет вычисляться хеш MD5 хешей MD5, и что исходное имя файла затем станет невосстановимым, если вы не сделаете тщательные записи о том, какие файлы называются и что после каждого запуска сценария.

Кусалананда
источник
Так же, как к вашему сведению, awkчасть может быть заменена на « while read sum dummy ; do printf "%s/%s.%s\n' $dir $sum $ext ; done ;Вам нужно», dummyчтобы получить «-».
Роберт Бенсон
@RobertBenson Проблема в том, что имена файлов, содержащие пробелы, будут перепутаны.
Кусалананда
Хороший звонок. Имена файлов с пробелами - это зло. Я люблю awkсебя , и это мне потребовалось некоторое время , чтобы использовать bashкоммунальные услуги , а не system()вawk
Роберт Бенсон
5

С perlх rename:

find . -name '*.jpg' -type f -exec rename -n '
  BEGIN{use Digest::MD5 qw(md5_hex)}
  my ($dir, $name, $ext) = m{(.*)/(.*)\.(.*)}s;
  $_ = "$dir/" . md5_hex($name) . ".$ext"' {} +

(удалить, -nкогда счастлив).

Стефан Шазелас
источник
Удивительно! Это вычисляет сумму md5 имени файла без расширения, теперь как насчет полного имени файла? ОП не сказал, нужно ли ему это или нет.
десерт
1
Он не сказал этого, но пример, который он приводит, является именно этим.
Роберт Бенсон
2

Для AWKподхода:

find [Directory] -type f [various other find options] | 
     awk '{orig=$0; 
           match($0,/^.*\//,path); sub("^"path[0], "");
           match($0, /.[[^.]+$/,ext); sub(ext[0]"$", "");
           ("echo \"" $0 "\"|md5sum") | getline;
           com=sprintf("mv \"%s\" \"%s%s%s\"", orig, p[0], $1, ext[0]);
           print(com)
           }'

Для современных findкоманд не требуется каталог для ввода ., поэтому [Каталог] можно оставить пустым. -type fТолько находит файлы, что очень удобно , так как md5sumне любит каталогов и изменять имя каталога во время работы не будет хорошей идеей. Используйте, -iname patternесли вы хотите использовать только некоторые файлы, например -iname \*.dat, если важен регистр, используйте -nameвместо -iname.

Эти match(...); sub(...)куски извлечение частей файла и заменить их во входной строке. Обратите внимание, что "^"и "$"[pre / ap] отложено для предотвращения замены строки, которая может повторять путь / расширение.

Заменить print(com)с system(com)фактически выполнить переименование.

Если вы хотите использовать md5sumфактический файл в качестве имени, вы можете использовать тот факт, что md5sumвыводит сумму и ввод имени файла, чтобы сделать что-то вроде:

 find -type f -exec md5sum '{}' ';' | 
     while read sum file ; do 
       [echo] mv "$file" "`dirname $file`/$sum".extension ; 
     done

while read sum fileБудет принимать 2 аргумента, результаты md5sumкоманды, а также назначить sumи fileпеременные с ними. Поскольку в нем sumне должно быть пробелов, оно readдолжно работать нормально.

Очевидно, что его [echo]следует удалить при запуске, но всегда полезно при тестировании любых изменений в сценариях проверять поиск перед запуском.

Это все предполагает, что вы работаете bash. Кроме того, это может быть напечатано одной длинной строкой:

find -iname \*.jpg -exec md5sum '{}' ';' | while read sum file ; do mv "$file" "`dirname $file`/$sum".jpg ; done
Роберт Бенсон
источник
1
Похоже, что это будет хэшировать содержимое файлов. ОП хотел хэшировать имя (без расширения).
Кусалананда
Я думаю, это поможет, если я полностью прочитаю вопрос.
Роберт Бенсон
2

Это в подходе, который я часто люблю использовать.

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 \\`echo \1 \| md5sum \| cut -d' ' -f 1\\`.\2|" | sh -

Команда "ls" создает поток текстовых строк. Команда "sed" преобразует каждую строку с правилами сопоставления с образцом. Команда «sed» выводит команду «mv», которая затем передается через оболочку «sh» для выполнения. Параметры команды "mv" похожи на "mv oldfilename newfilename", которое переименовывает файл. Я создаю новое имя файла с помощью команды sed, которая принимает часть перед последней точкой и выводит ее на вход команды "md5sum", а затем берет только хеш из ее вывода.

Проходя через мой процесс, сначала перечислите файлы списка ('head -n 3', чтобы увидеть только первые 3 строки):

ls | head -n 3
    1000-26092016.xml
    1000-27092016.xml
    12312-28092016.xml

Затем подумайте о преобразовании с помощью sed (еще не передавая сгенерированные команды через оболочку)

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 \1.\2|" | head -n 3
    mv 1000-26092016.xml 1000-26092016.xml
    mv 1000-27092016.xml 1000-27092016.xml
    mv 12312-28092016.xml 12312-28092016.xml

Есть три модели соответствия:

^\(.*\)      = match from start-of-line up to a dot
\.           = matches a single dot
\([^\.]*\)$  = match 0-or-more non-dot chars from end of line

Я хочу использовать sed, чтобы заменить имя входного файла на «mv filename NEWfilename», но, поскольку я передаю команды через оболочку, я могу генерировать команды, которые получают md5sum, как это

echo "1000-26092016" | md5sum
    55b18a6b0add4a318b0079e18512b4e8  -

чтобы получить только хэш

echo "1000-26092016" | md5sum | cut -d' ' -f 1
    55b18a6b0add4a318b0079e18512b4e8

В оболочке Unix мы можем использовать операторы backtick (`some_command`) для запуска подкоманды, например,

echo "howdy date there"
    howdy date there
echo "howdy `date` there"
    howdy Fri Sep 15 18:39:00 IST 2017 there

Возвращаясь к команде mv, я хочу, чтобы sed выдавал «mv here there», где «there» заменяется командой backtick, чтобы получить md5sum. Строка внутри sed replace-string начинается следующим образом

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 `echo \1 | md5sum | cut -d' ' -f 1`.\2|" | head -n 3
    mv 1000-26092016.xml     b026324c6904b2a9cb4b88d6d61c81d1.xml
    mv 1000-27092016.xml     b026324c6904b2a9cb4b88d6d61c81d1.xml
    mv 12312-28092016.xml    b026324c6904b2a9cb4b88d6d61c81d1.xml

Но явно создает один и тот же хеш для каждого имени файла, так как команда backticked-запускается до того, как sed увидит строку. Чтобы остановить оболочку, выполняющую команду backtick, чтобы sed выдавал обратные черты, мы должны добавить косую черту (также к символу канала), и снова:

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 \`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2|" | head -n 3
    mv 1000-26092016.xml     `echo 1000-26092016 | md5sum | cut -d' ' -f 1`.xml
    mv 1000-27092016.xml     `echo 1000-27092016 | md5sum | cut -d' ' -f 1`.xml
    mv 12312-28092016.xml    `echo 12312-28092016 | md5sum | cut -d' ' -f 1`.xml

Выходные данные также требуют имен файлов в кавычках в случае пробелов, поэтому

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \"\1.\2\" \"\`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2\"|" | grep trick
    mv "a trick€€ fíle nÁme.xml" "`echo a trick€€ fíle nÁme | md5sum | cut -d' ' -f 1`.xml"

Итак, давайте попробуем это, пропустив через оболочку:

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \"\1.\2\" \"\`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2\"|" | grep trick | sh -

Это сработало? Я полагаю:

echo "a trick€€ fíle nÁme" | md5sum
    629db9c3071928ba0746f18444713b65  -
ls 629db9c3071928ba0746f18444713b65*
    629db9c3071928ba0746f18444713b65.xml

Вот подход к перекрестной проверке; используйте параметр «ls» «-i» для вывода i-узла файловой системы unix (который не изменяется с «mv»):

ls -1i | sort -n > .before
ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \"\1.\2\" \"\`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2\"|" | sh -
ls -1i | sort -n > .after
cut -d' ' -f 1 .before | while read I ; do echo "mv'd \"`grep ${I} .before`\" to \"`grep ${I} .after`\"" | sed "s| *$I *||g" ; done | head -n 3
    mv'd "1000-26092016.xml" to "55b18a6b0add4a318b0079e18512b4e8.xml"
    mv'd "1000-27092016.xml" to "b1baa80d99d5edf85c8aeb98185dd440.xml"
    mv'd "12312-28092016.xml" to "2b2d692bd047b64c99f7b9161349d430.xml"

Или, используя команду "paste" (пакет 'coreutils')

paste .before .after | head -n 3
    36703389 1000-26092016.xml  36703389 55b18a6b0add4a318b0079e18512b4e8.xml
    36703390 1000-27092016.xml  36703390 b1baa80d99d5edf85c8aeb98185dd440.xml
    36703391 12312-28092016.xml 36703391 2b2d692bd047b64c99f7b9161349d430.xml
jmullee
источник
0

Мне нравится этот однострочный ответ, но он ломается, потому что он разбирает имя файла. Я также немного столкнулся с ша хэшами.

find -iname "*.jpg" -exec sha1sum '{}' ';' | while read sum file ; do mv -v "$file" "`dirname '$file'`/$sum".jpg ; done

Я думаю, что он также вытаскивает файлы и помещает их в базу, где была введена команда.

Спасибо.

GoofProg
источник
1
Вероятно, нам следует вернуться к ответу, на основании которого вы основали свой ответ .
Джефф Шаллер