renommer tous les fichiers d'un répertoire avec le hachage md5 de leur nom de fichier (pas le contenu)

11

Je suis très nouveau sur linux / ligne de commande et j'ai besoin de crypter les noms des fichiers 10K + (noms uniques) afin qu'ils correspondent au nom crypté MD5 dans la base de données mySQL.
J'ai vu comment vous pouvez renommer un répertoire de fichiers et comment obtenir le hachage d'un fichier ( mdsum? ) Mais je suis coincé sur la façon d'obtenir le hachage du nom de fichier, puis de renommer ce fichier en conservant le hachage généré l'extension ie

mynicepicture.jpg > fba8255e8e9ce687522455f3e1561e53.jpg 

Il semble que ce devrait être un simple changement de nom ou de mvligne, mais je ne parviens pas à comprendre.
Merci beaucoup pour vos idées

PS J'ai vu l'utilisation des fonctions Perl dans quelques exemples proches de ce que je recherche mais je n'ai aucune idée où / comment les utiliser.

BradH
la source
3
Voulez-vous vraiment ajouter un hachage au nom du fichier et non au contenu du fichier?
Anthon
12
Remarque: le hachage MD5 n'est pas un périphérique de cryptage. MD5 n'est même pas un hachage cryptographique. Un hachage, tout hachage, est une transformation unidirectionnelle d'un ensemble de données vers un nombre. Ce n'est pas réversible. Le vrai chiffrement est toujours réversible (compte tenu de la clé utilisée pour le chiffrement).
Kusalananda
1
fba8255e8e9ce687522455f3e1561e53est le hachage MD5 mynicepicture, cela signifie-t-il que l'extension doit être supprimée avant le hachage?
Kusalananda
@dessert Je veux dire qu'il n'y a pas de bénéficiaire si vous faites md5sum <<<"file name"le file namefichier existant ou non, car il le considère comme une chaîne sauf le nourrir avec le nom des fichiers existants.
αғsнιη

Réponses:

14

Vous n'avez pas dit quel shell vous voulez utiliser, donc je suppose que Bash - la réponse a besoin d'ajustements pour fonctionner avec d'autres shells.

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

Version du script:

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

Cette forboucle simple prend chaque fichier dans le répertoire courant, calcule la somme md5 de son nom et le sort. Utilisez-le pour vérifier la fonctionnalité, si vous souhaitez commencer à renommer, remplacez le second echopar mv.

Explications

  • echo -n "$i" | md5sum- calculer la somme md5 du nom de fichier complet, y compris l'extension de fichier ( tuyauterie ), pour supprimer le changement d'extension echo -n "$i"à l'un des éléments suivants:

    ${i%%.*}
    sed 's/\..*//' <<< "$i"
    echo "$i" | sed 's/\..*//'
    
  • sum=$(…)- exécuter et enregistrer la sortie dans $sum( Substitution de commande )

  • ${sum%% *}- afficher tout jusqu'au premier espace ( Substitution de paramètres ), le même que l'un des suivants:

    $(sed 's/ .*//' <<< "$sum")
    $(echo "$sum" | sed 's/ .*//')
    
  • ${i##*.} - afficher tout après le dernier point (substitution de paramètres), le même que l'un des éléments suivants:

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

Si vous devez renommer des fichiers récursivement dans différents dossiers, utilisez-les findavec l' -execoption.

dessert
la source
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

Ce bashscript utilise l' md5sumutilitaire de GNU coreutils pour calculer le hachage MD5 à partir du nom de base (sans extension) d'un nom de chemin donné. La fonction d'assistance md5nameeffectue le calcul réel et affichera le nouveau nom avec le chemin d'accès complet et l'extension.

La md5namefonction utilise awkpour assembler le nouveau nom à partir des parties du nom de chemin donné et le résultat de md5sum.

Exemples de la fonction utilisée par elle-même:

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

... où c9e89fa443d16da4b96ea858881320c9est le hachage MD5 de la chaîne file name here.

Supprimez le echodu script en haut pour renommer les fichiers. Vous voudrez peut-être enregistrer la sortie du script d'origine dans un fichier (avec le echoen place) si vous avez besoin à un moment donné de restaurer les noms de fichier à leurs originaux.

Notez que l'exécuter deux fois sur un ensemble de fichiers calculera le hachage MD5 des hachages MD5, et que le nom de fichier d'origine devient alors irrécupérable à moins que vous ne preniez soigneusement note des fichiers appelés après chaque exécution du script.

Kusalananda
la source
Tout comme un FYI, la awkpartie pourrait être remplacée par while read sum dummy ; do printf "%s/%s.%s\n' $dir $sum $ext ; done ;Vous avez besoin de dummypour capturer le «-».
Robert Benson
@RobertBenson Le problème est que les noms de fichiers contenant des espaces seraient foirés.
Kusalananda
Bon appel. Les noms de fichiers avec des espaces sont mauvais. Je awkm'amuse et il m'a fallu un certain temps pour utiliser les bashutilitaires plutôt que system()dansawk
Robert Benson
5

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

(retirer -nquand heureux).

Stéphane Chazelas
la source
Incroyable! Cela calcule la somme md5 du nom de fichier sans l'extension, maintenant que diriez-vous du nom de fichier complet? OP n'a pas dit s'il en avait besoin avec ou sans.
dessert
1
Il ne l'a pas dit, mais l'exemple qu'il donne est exactement cela.
Robert Benson
2

Pour une AWKapproche:

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

Les findcommandes modernes ne nécessitent pas de répertoire pour la saisie .est supposée, donc le [Répertoire] pourrait être laissé vide. Le -type fseul trouve des fichiers, ce qui est pratique car il md5sumn'aime pas les répertoires et changer le nom du répertoire pendant l'exécution ne serait pas une bonne idée. À utiliser -iname patternsi vous ne souhaitez utiliser que certains fichiers, par exemple -iname \*.dat, si la casse est importante, utilisez à la -nameplace de -iname.

Les match(...); sub(...)morceaux extraient des parties du nom de fichier et les remplacent dans la chaîne d'entrée. Notez que "^"et "$"sont [pre / ap] en attente pour éviter de remplacer une chaîne qui peut répéter le chemin / l'extension.

Remplacez print(com)par system(com)pour effectuer le changement de nom.

Si vous voulez utiliser le nom md5sumdu fichier réel comme nom, vous pouvez utiliser le fait que md5sumla somme et le nom de fichier d'entrée sont sortis pour faire quelque chose comme:

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

Le while read sum fileprendra 2 arguments, les résultats de la md5sumcommande, et affectera sumet les filevariables avec eux. Étant donné que le sumne devrait pas contenir d'espace, le readdevrait fonctionner correctement.

Évidemment, le [echo]devrait être supprimé lors de l'exécution, mais c'est toujours une bonne idée lors du test de toute modification de script pour tester la recherche avant de l'exécuter.

Tout cela suppose que vous courez bash. En outre, cela peut être tapé comme une longue ligne:

find -iname \*.jpg -exec md5sum '{}' ';' | while read sum file ; do mv "$file" "`dirname $file`/$sum".jpg ; done
Robert Benson
la source
1
Il semble que cela hache le contenu des fichiers. L'OP voulait hacher le nom (sans extension).
Kusalananda
Je suppose que cela aiderait si je lis entièrement la question.
Robert Benson
2

C'est une approche que j'aime souvent utiliser.

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

La commande "ls" produit un flux de lignes de texte. La commande "sed" transforme chaque ligne avec des règles de correspondance de motifs. La commande "sed" génère une commande "mv" qui est ensuite envoyée via un shell "sh" pour exécution. Les paramètres de la commande "mv" sont comme "mv oldfilename newfilename", qui renomme le fichier. Je construis le nouveau nom de fichier avec une commande sed qui prend la partie avant le dernier point, et l'écho dans l'entrée de la commande "md5sum", puis prend juste le hachage de sa sortie.

En parcourant mon processus, commencez par lister les fichiers ('head -n 3' pour ne voir que les 3 premières lignes):

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

Pensez ensuite à transformer avec sed (pas encore de piping de commandes générées via un 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

Il existe trois modèles de correspondance:

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

Je veux utiliser sed pour remplacer un nom de fichier d'entrée par "mv filename NEWfilename", mais comme je passe des commandes via un shell, je peux générer des commandes qui obtiennent le md5sum, comme ceci

echo "1000-26092016" | md5sum
    55b18a6b0add4a318b0079e18512b4e8  -

pour obtenir juste le hachage

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

Dans un shell Unix, nous pouvons utiliser des opérateurs de backtick (`some_command`) pour exécuter une sous-commande, donc par exemple

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

De retour à la commande mv, je veux que sed produise "mv here there" avec "there" remplacé par une commande backtick pour obtenir le md5sum. La chaîne à l'intérieur de la chaîne de remplacement sed commence comme ceci

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

Mais fait clairement le même hachage pour chaque nom de fichier, car la commande backticked est exécutée avant que sed ne voie la chaîne. Pour arrêter le shell exécutant la commande backtick afin que sed produise les backticks, nous devons ajouter des barres obliques (également au caractère pipe), encore une fois:

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

La sortie a également besoin que les noms de fichiers soient cités en cas d'espaces, donc

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"

Essayons donc celui-ci, en le passant à travers un shell:

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

Cela a-t-il fonctionné? j'imagine:

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

Voici une approche de recoupement; utilisez "ls" option "-i" pour sortir le i-node du système de fichiers unix (qui ne change pas avec "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"

Ou, en utilisant la commande "coller" (package '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
la source
0

J'aime cette réponse d'une ligne, mais elle se casse car elle analyse le nom de fichier. Je l'ai également un peu augmenté avec des hachages de sha.

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

Je pense que cela extrait également les fichiers et les place à la base de la saisie de la commande.

Merci.

GoofProg
la source
1
Nous devrions probablement nous référer à la réponse à partir de laquelle vous avez basé la vôtre.
Jeff Schaller