rinominare tutti i file in una directory con l'hash md5 del loro nome file (non contenuto)


11

Sono molto nuovo di Linux / riga di comando e ho bisogno di crittografare i nomi dei file 10K + (nomi univoci) in modo che corrispondano al nome crittografato MD5 nel database mySQL.
Ho visto come puoi rinominare una directory di file e come ottenere l'hash di un file ( mdsum? ) Ma sono bloccato su come ottenere l'hash del nome del file e quindi rinominare quel file nel mantenimento dell'hash generato l'estensione cioè

mynicepicture.jpg > fba8255e8e9ce687522455f3e1561e53.jpg 

Sembra che dovrebbe essere una semplice ridenominazione o mvlinea, ma non riesco a pensarci.
Mille grazie per i tuoi approfondimenti

PS Ho visto l'uso delle funzioni Perl in alcuni esempi vicini a quello che sto cercando, ma non ho idea di dove / come usarli.


3
Sei sicuro di voler avere un hash dal nome del file e non dal contenuto del file?
Anthon,

12
Nota: l'hash MD5 non è un dispositivo di crittografia. MD5 non è nemmeno un hash crittografico. Un hash, qualsiasi hash, è una trasformazione unidirezionale di un set di dati in un numero. Non è reversibile. La crittografia reale è sempre reversibile (data la chiave utilizzata per la crittografia).
Kusalananda

1
fba8255e8e9ce687522455f3e1561e53è l'hash MD5 per mynicepicture, significa che l'estensione dovrebbe essere rimossa prima dell'hash?
Kusalananda

@dessert Intendo dire che non esiste alcun beneficiario se si fa md5sum <<<"file name"il file namefile esistente o meno, perché lo considera come una stringa se non l'alimentazione con il nome del file esistente.
αғsнιη,

Risposte:


14

Non hai detto quale shell vuoi usare, quindi sto solo supponendo che Bash - la risposta ha bisogno di aggiustamenti per funzionare con altre shell.

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

Versione dello script:

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

Questo semplice forciclo prende tutti i file nella directory corrente, calcola la somma md5 del suo nome e lo emette. Utilizzare questo per verificare la funzionalità, se si desidera iniziare a rinominare sostituire il secondo echocon mv.

spiegazioni

  • echo -n "$i" | md5sum- calcola la somma md5 del nome completo del file, inclusa l'estensione del file ( Piping ), per eliminare la modifica dell'estensione echo -n "$i"in uno dei seguenti modi:

    ${i%%.*}
    sed 's/\..*//' <<< "$i"
    echo "$i" | sed 's/\..*//'
  • sum=$(…)- esegue e salva l'output in $sum( Sostituzione comando )

  • ${sum%% *}- produce tutto fino al primo spazio ( Sostituzione parametri ), uguale a uno dei seguenti:

    $(sed 's/ .*//' <<< "$sum")
    $(echo "$sum" | sed 's/ .*//')
  • ${i##*.} - produce tutto dopo l'ultimo punto (Sostituzione parametri), uguale a uno dei seguenti:

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

Se è necessario rinominare i file in modo ricorsivo in cartelle diverse, utilizzare findcon l' -execopzione.


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

Questo bashscript utilizza l' md5sumutilità dei coreutils GNU per calcolare l'hash MD5 dal nome di base (estensione sans) di un determinato percorso. La funzione helper md5nameesegue il calcolo effettivo e genererà il nuovo nome con percorso ed estensione completi.

La md5namefunzione utilizza awkper assemblare il nuovo nome dalle parti del nome percorso specificato e il risultato da md5sum.

Esempi della funzione in uso da sola:

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

... dov'è c9e89fa443d16da4b96ea858881320c9l'hash MD5 della stringa file name here.

Rimuovi echodallo script in alto per rinominare effettivamente i file. È possibile che si desideri salvare l'output dello script originale su file (con il echoposto in atto) se ad un certo punto è necessario ripristinare i nomi dei file sui loro originali.

Si noti che eseguendolo due volte su una serie di file si calcolerà l'hash MD5 degli hash MD5 e che il nome file originale diventerà irrecuperabile a meno che non si annoti attentamente quali file vengono chiamati e dopo ogni esecuzione dello script.


Proprio come un FYI, la awkparte potrebbe essere sostituita con while read sum dummy ; do printf "%s/%s.%s\n' $dir $sum $ext ; done ;È necessario il dummyper catturare il '-'.
Robert Benson,

@RobertBenson Il problema è che i nomi dei file contenenti spazi sarebbero incasinati.
Kusalananda

Ottima scelta. I nomi di file con spazi sono cattivi. Mi diverto awke mi ci è voluto un po 'di tempo per usare le bashutility piuttosto che system()inawk
Robert Benson,

5

Con 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"' {} +

(rimuovere -nquando felice).


Sorprendente! Questo calcola la somma md5 del nome del file senza l'estensione, ora che ne dici del nome completo del file? OP non ha detto se ne ha bisogno con o senza.
Dessert

1
Non l'ha detto, ma l'esempio che fa è esattamente questo.
Robert Benson,

2

Per un AWKapproccio:

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

Si presume che i findcomandi moderni non richiedano una directory per l'input ., quindi la [Directory] potrebbe essere lasciata vuota. Il -type fsolo trova i file, il che è utile poiché md5sumnon ama le directory e cambiare il nome della directory durante l'esecuzione non sarebbe una buona idea. Utilizzare -iname patternse si desidera utilizzare solo alcuni file, ad esempio -iname \*.dat, se il caso è importante, utilizzare -nameinvece di -iname.

I match(...); sub(...)pezzi stanno estraendo parti del nome file e le sostituiscono nella stringa di input. Si noti che "^"e "$"sono [pre / ap] in sospeso per impedire la sostituzione di una stringa che potrebbe ripetere il percorso / estensione.

Sostituisci print(com)con system(com)per eseguire effettivamente la ridenominazione.

Se si desidera utilizzare il nome md5sumdel file effettivo come nome, è possibile utilizzare il fatto che md5sumgenera la somma e il nome del file di input per fare qualcosa del tipo:

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

L' while read sum fileavrà 2 argomenti, i risultati del md5sumcomando, e assegnare sume filevariabili con loro. Dal momento sumche non dovrebbe contenere spazi, readdovrebbe funzionare bene.

Ovviamente [echo]dovrebbe essere rimosso quando è effettivamente in esecuzione, ma è sempre una buona idea testare eventuali modifiche con script per testare la ricerca prima di eseguire.

Tutto questo presuppone che tu stia correndo bash. Inoltre, questo può essere digitato come una riga longish:

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

1
Sembra che questo hash il contenuto dei file. L'OP ha voluto hash il nome (senza estensione).
Kusalananda

Immagino che sarebbe di aiuto se leggessi appieno la domanda.
Robert Benson,

2

Questo approccio mi piace spesso usare.

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

Il comando "ls" produce un flusso di righe di testo. Il comando "sed" trasforma ogni riga con regole di corrispondenza del modello. Il comando "sed" genera un comando "mv" che viene quindi reindirizzato attraverso una shell "sh" per l'esecuzione. I parametri del comando "mv" sono come "mv oldfilename newfilename", che rinomina il file. Costruisco il nuovo nome-file con un comando sed che prende la parte prima dell'ultimo punto e lo fa eco nell'input del comando "md5sum", quindi prende solo l'hash dal suo output.

Seguendo il mio processo, elenco dei primi file ('head -n 3' per vedere solo le prime 3 righe):

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

Quindi pensa a trasformarti con sed (non eseguire il piping di alcun comando generato attraverso una 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

Esistono tre modelli di corrispondenza:

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

Voglio usare sed per sostituire un nome file di input con "mv nomefile NEWfilename", ma mentre eseguo il piping dei comandi attraverso una shell, posso generare comandi che ottengono il md5sum, come questo

echo "1000-26092016" | md5sum
    55b18a6b0add4a318b0079e18512b4e8  -

per ottenere solo l'hash

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

In una shell unix, possiamo usare operatori backtick (`some_command`) per eseguire un comando secondario, quindi per esempio

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

Tornando al comando mv, voglio che sed produca "mv here there" con "there" sostituito con un comando backtick per ottenere md5sum. La stringa all'interno della stringa di sostituzione sed inizia in questo modo

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

Ma sta chiaramente creando lo stesso hash per ogni nome file, dato che il comando backticked viene eseguito prima che sed veda la stringa. Per impedire alla shell di eseguire il comando backtick in modo che sed emetta i backtick, dobbiamo anteporre barre (anche al carattere pipe), quindi di nuovo:

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

L'output ha anche bisogno di nomi di file da quotare in caso di spazi, quindi

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"

Quindi proviamo questo, eseguendo il piping attraverso una shell:

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

Ha funzionato ? suppongo:

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

Ecco un approccio al controllo incrociato; usa l'opzione "ls" "-i" per generare l'i-node del filesystem unix (che non cambia con "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"

Oppure, usando il comando "incolla" (pacchetto '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

0

Mi piace quella risposta a una riga, ma si interrompe perché analizza il nome file. L'ho anche gonfiato un po 'con hash sha.

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

Penso che tira fuori anche i file e li mette alla base di dove è stato inserito il comando.

Grazie.


1
Dovremmo probabilmente fare riferimento alla risposta da cui hai basato la tua.
Jeff Schaller
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.