ganti nama semua file dalam direktori menjadi hash md5 dari nama file mereka (bukan konten)

11

Saya sangat baru di linux / command line dan perlu mengenkripsi nama file 10K + (nama unik) sehingga cocok dengan nama terenkripsi MD5 dalam database mySQL.
Saya telah melihat bagaimana Anda dapat mengubah nama direktori file dan cara mendapatkan hash file ( mdsum? ) Tapi saya terjebak pada bagaimana untuk mendapatkan hash dari nama file dan kemudian mengubah nama file itu menjadi hash yang dihasilkan. ekstensi yaitu

mynicepicture.jpg > fba8255e8e9ce687522455f3e1561e53.jpg 

Sepertinya itu harus berupa penggantian nama atau mvgaris yang sederhana tetapi saya tidak bisa mengerti.
Terima kasih banyak atas wawasan Anda

PS Saya telah melihat penggunaan fungsi Perl dalam beberapa contoh dekat dengan apa yang saya cari tetapi tidak tahu di mana / bagaimana menggunakannya.

BradH
sumber
3
Apakah Anda yakin ingin memiliki hash dari nama file dan bukan isi file?
Anthon
12
Catatan: Hash MD5 bukan perangkat enkripsi. MD5 bahkan bukan hash kriptografi. Hash, hash apa pun, adalah transformasi satu arah dari kumpulan data ke angka. Itu tidak bisa dibalik. Enkripsi nyata adalah reversibel, selalu (mengingat kunci yang digunakan untuk mengenkripsi).
Kusalananda
1
fba8255e8e9ce687522455f3e1561e53adalah hash MD5 untuk mynicepicture, apakah itu berarti ekstensi harus dihapus sebelum hashing?
Kusalananda
@dessert saya berarti bahwa tidak ada penerima jika Anda melakukan md5sum <<<"file name"yang file namefile yang ada atau tidak, karena mempertimbangkan sebagai tali kecuali makan dengan nama file yang ada.
αғsнιη

Jawaban:

14

Anda tidak mengatakan shell mana yang ingin Anda gunakan, jadi saya hanya berasumsi Bash - jawabannya perlu penyesuaian untuk bekerja dengan shell lain.

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

Versi skrip:

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

forLoop sederhana ini mengambil setiap file dalam direktori saat ini, menghitung jumlah md5 dari namanya dan menampilkannya. Gunakan ini untuk memeriksa fungsionalitas, jika Anda ingin mulai mengganti nama ganti yang kedua echodengan mv.

Penjelasan

  • echo -n "$i" | md5sum- menghitung jumlah md5 dari nama file lengkap termasuk ekstensi file ( Perpipaan ), untuk menghapus perubahan ekstensi echo -n "$i"ke salah satu dari yang berikut:

    ${i%%.*}
    sed 's/\..*//' <<< "$i"
    echo "$i" | sed 's/\..*//'
  • sum=$(…)- jalankan dan simpan output dalam $sum( Pergantian Perintah )

  • ${sum%% *}- Keluarkan semuanya sampai spasi pertama ( Substitusi Parameter ), sama dengan salah satu dari yang berikut:

    $(sed 's/ .*//' <<< "$sum")
    $(echo "$sum" | sed 's/ .*//')
  • ${i##*.} - Keluarkan semuanya setelah titik terakhir (Substitusi Parameter), sama seperti salah satu dari berikut ini:

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

Jika Anda perlu mengganti nama file secara rekursif di folder yang berbeda, gunakan finddengan -execopsi.

pencuci mulut
sumber
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

bashSkrip ini menggunakan md5sumutilitas dari GNU coreutils untuk menghitung hash MD5 dari nama dasar (ekstensi sans) dari setiap nama path yang diberikan. Fungsi helper md5namemelakukan perhitungan aktual dan akan menampilkan nama baru dengan jalur dan ekstensi lengkap.

The md5nameFungsi menggunakan awkuntuk merakit nama baru dari bagian-bagian dari path yang diberikan dan hasil dari md5sum.

Contoh fungsi yang digunakan dengan sendirinya:

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

... di mana c9e89fa443d16da4b96ea858881320c9hash MD5 dari string file name here.

Hapus echoskrip dari atas untuk benar-benar mengganti nama file. Anda mungkin ingin menyimpan output dari skrip asli ke file (dengan echodi tempat) jika Anda pada suatu titik perlu mengembalikan nama file ke aslinya.

Perhatikan bahwa menjalankan ini dua kali pada satu set file akan menghitung hash MD5 hash MD5, dan bahwa nama file asli kemudian menjadi tidak dapat dipulihkan kecuali Anda membuat catatan yang hati-hati tentang file apa yang disebut apa setelah setiap kali skrip dijalankan.

Kusalananda
sumber
Sama seperti FYI, awkporsinya bisa diganti dengan while read sum dummy ; do printf "%s/%s.%s\n' $dir $sum $ext ; done ;Kamu perlu dummymenangkap '-'.
Robert Benson
@RobertBenson Masalahnya adalah bahwa nama file yang mengandung spasi akan kacau.
Kusalananda
Panggilan yang bagus. Nama file dengan spasi jahat. Saya menikmati awkdiri saya dan butuh beberapa saat untuk menggunakan bashutilitas daripada system()diawk
Robert Benson
5

Dengan perl's 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"' {} +

(hapus -nsaat senang).

Stéphane Chazelas
sumber
Luar biasa! Ini menghitung jumlah md5 dari nama file tanpa ekstensi, sekarang bagaimana dengan nama file lengkap? OP tidak mengatakan apakah dia membutuhkannya dengan atau tanpa itu.
hidangan penutup
1
Dia tidak mengatakannya, tapi contoh yang dia berikan adalah persis seperti itu.
Robert Benson
2

Untuk suatu AWKpendekatan:

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)
           }'

findPerintah modern tidak memerlukan direktori untuk input .diasumsikan, sehingga [Direktori] dapat dibiarkan kosong. The -type fhanya menemukan file, yang berguna karena md5sumtidak menyukai direktori dan mengubah nama direktori saat menjalankan tidak akan menjadi ide yang baik. Gunakan -iname patternjika Anda hanya ingin menggunakan beberapa file, misalnya -iname \*.dat, jika kasing penting, gunakan -namesebagai ganti -iname.

The match(...); sub(...)potongan mengekstrak bagian dari nama file dan menggantikan mereka dalam string masukan. Catat itu "^"dan "$"[pra / ap] tertunda untuk mencegah penggantian string yang mungkin mengulang jalur / ekstensi.

Ganti print(com)dengan system(com)untuk benar-benar melakukan penggantian nama.

Jika Anda ingin menggunakan md5sumfile aktual sebagai nama, Anda dapat menggunakan fakta yang md5summenampilkan jumlah dan masukkan nama file untuk melakukan sesuatu seperti:

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

The while read sum fileakan mengambil 2 argumen, hasil dari md5sumperintah, dan menetapkan sumdan filevariabel dengan mereka. Karena sumseharusnya tidak memiliki ruang di dalamnya, readseharusnya berfungsi dengan baik.

Jelas itu [echo]harus dihapus ketika benar-benar berjalan, tetapi itu selalu ide yang baik ketika menguji perubahan skrip untuk menguji pencarian sebelum menjalankan.

Ini semua menganggap Anda sedang berjalan bash. Juga, ini dapat diketik sebagai satu garis gondrong:

find -iname \*.jpg -exec md5sum '{}' ';' | while read sum file ; do mv "$file" "`dirname $file`/$sum".jpg ; done
Robert Benson
sumber
1
Sepertinya ini akan hash isi file. OP ingin hash nama (tanpa ekstensi).
Kusalananda
Saya kira itu akan membantu jika saya sepenuhnya membaca pertanyaannya.
Robert Benson
2

Dalam pendekatan ini saya sering suka menggunakan.

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

Perintah "ls" menghasilkan aliran baris teks. Perintah "sed" mengubah setiap baris dengan aturan pencocokan pola. Perintah "sed" menampilkan perintah "mv" yang kemudian disalurkan melalui shell "sh" untuk dieksekusi. Parameter perintah "mv" seperti "mv oldfilename newfilename", yang mengubah nama file. Saya membangun nama file baru dengan perintah sed yang mengambil bagian sebelum titik terakhir, dan menggemakannya menjadi input dari perintah "md5sum", dan kemudian hanya mengambil hash dari output itu.

Berjalan melalui proses saya, daftar file pertama ('head -n 3' untuk hanya melihat 3 baris pertama):

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

Kemudian pikirkan tentang mentransformasikan dengan sed (belum memipakan perintah apa pun yang dihasilkan melalui shell)

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

Ada tiga pola kecocokan:

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

Saya ingin menggunakan sed untuk mengganti input nama file dengan "mv filename NEWfilename", tetapi karena saya mem-pipkan perintah melalui shell, saya dapat menghasilkan perintah yang mendapatkan md5sum, seperti ini

echo "1000-26092016" | md5sum
    55b18a6b0add4a318b0079e18512b4e8  -

untuk mendapatkan hash

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

Dalam shell unix, kita dapat menggunakan operator backtick (`some_command`) untuk menjalankan sub-perintah, jadi misalnya

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

Kembali ke perintah mv, saya ingin sed untuk menghasilkan "mv di sini di sana" dengan "di sana" diganti dengan perintah backtick untuk mendapatkan md5sum. String di dalam sed-replace-string dimulai seperti ini

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

Tetapi jelas membuat hash yang sama untuk setiap nama file, karena perintah backticked dijalankan sebelum sed melihat string. Untuk menghentikan shell yang menjalankan perintah backtick sehingga sed akan menampilkan backtick, kita harus menambahkan dulu slash (juga ke karakter pipa), jadi sekali lagi:

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

Outputnya juga perlu nama file untuk dikutip dalam kasus spasi, jadi

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"

Jadi mari kita coba yang ini, dengan menyalurkannya melalui shell:

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

Apa itu bekerja ? saya tebak:

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

Berikut ini adalah pendekatan untuk cross-check; gunakan opsi "ls" "-i" untuk menampilkan simpul-sistem file unix (yang tidak berubah dengan "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"

Atau, menggunakan perintah "tempel" (paket '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
selamat tinggal
sumber
0

Saya suka jawaban satu baris, tetapi rusak karena mem-parsing nama file. Saya juga menabraknya sedikit dengan hash sha.

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

Saya pikir itu menarik file juga dan meletakkannya di dasar di mana perintah itu dimasukkan.

Terima kasih.

GoofProg
sumber
1
Kami mungkin harus merujuk kembali ke jawaban Anda.
Jeff Schaller