Question

  

Problème Spécification:

     

Étant donné un répertoire, je veux parcourir le répertoire et ses sous-répertoires non cachés,
  et ajouter un hachage tourbillon dans le non caché   Les noms de fichiers.
  Si le script est réexécutez il remplacerait un vieux hachage par un nouveau.

     
    

<filename>.<extension> ==> <filename>.<a-whirlpool-hash>.<extension>

         

<filename>.<old-hash>.<extension> ==> <filename>.<new-hash>.<extension>

  

  

Question:

     

a) Comment voulez-vous faire?

     

b) Sur les toutes les méthodes disponibles pour vous, ce qui rend votre méthode? Plus appropriée

  

Verdict:

     

Merci à tous, j'ai choisi la réponse de SeigeX pour sa vitesse et la portabilité.
  Il est plus rapide que les de manière empirique les autres variantes de bash,
  et il a travaillé sans modification sur ma machine Mac OS X.

Était-ce utile?

La solution

Mise à jour pour corriger:
1. Les noms de fichiers avec « [ » ou « ] » dans leur nom (vraiment, tout caractère maintenant. Voir commentaire)
2. Traitement des md5sum lors du hachage d'un fichier avec une barre oblique inverse ou saut de ligne en son nom
3. algo de hachage de vérification Functionized de modularité
4. logique de hachage de vérification refactorisé pour enlever double-négatifs

#!/bin/bash
if (($# != 1)) || ! [[ -d "$1" ]]; then
    echo "Usage: $0 /path/to/directory"
    exit 1
fi

is_hash() {
 md5=${1##*.} # strip prefix
 [[ "$md5" == *[^[:xdigit:]]* || ${#md5} -lt 32 ]] && echo "$1" || echo "${1%.*}"
}

while IFS= read -r -d $'\0' file; do
    read hash junk < <(md5sum "$file")
    basename="${file##*/}"
    dirname="${file%/*}"
    pre_ext="${basename%.*}"
    ext="${basename:${#pre_ext}}"

    # File already hashed?
    pre_ext=$(is_hash "$pre_ext")
    ext=$(is_hash "$ext")

    mv "$file" "${dirname}/${pre_ext}.${hash}${ext}" 2> /dev/null

done < <(find "$1" -path "*/.*" -prune -o \( -type f -print0 \))

Ce code présente les avantages suivants par rapport aux autres entrées jusqu'à présent

  • Il est entièrement compatible avec les versions 2.0.2 et au-delà Bash
  • Pas d'appels superflus à d'autres binaires comme sed ou grep; utilise plutôt l'expansion des paramètres builtin
  • Utilise la substitution de processus pour « trouver » au lieu d'un tuyau, pas de sous-coque est faite de cette façon
  • Prend le répertoire pour travailler comme argument et fait une vérification de bon sens sur elle
  • Utilise $ () plutôt que `` la notation pour la substitution de commande, celle-ci est dépréciée
  • Fonctionne avec des fichiers avec des espaces
  • Fonctionne avec des fichiers avec des sauts de ligne
  • Fonctionne avec des fichiers avec plusieurs extensions
  • Fonctionne avec les fichiers sans extension
  • Ne pas traverser les répertoires cachés
  • Est-ce que pas Ignorer les fichiers pré-haché, il recalcule le hachage selon la spécification

Arbre test

$ tree -a a
a
|-- .hidden_dir
|   `-- foo
|-- b
|   `-- c.d
|       |-- f
|       |-- g.5236b1ab46088005ed3554940390c8a7.ext
|       |-- h.d41d8cd98f00b204e9800998ecf8427e
|       |-- i.ext1.5236b1ab46088005ed3554940390c8a7.ext2
|       `-- j.ext1.ext2
|-- c.ext^Mnewline
|   |-- f
|   `-- g.with[or].ext
`-- f^Jnewline.ext

4 directories, 9 files 

Résultat

$ tree -a a
a
|-- .hidden_dir
|   `-- foo
|-- b
|   `-- c.d
|       |-- f.d41d8cd98f00b204e9800998ecf8427e
|       |-- g.d41d8cd98f00b204e9800998ecf8427e.ext
|       |-- h.d41d8cd98f00b204e9800998ecf8427e
|       |-- i.ext1.d41d8cd98f00b204e9800998ecf8427e.ext2
|       `-- j.ext1.d41d8cd98f00b204e9800998ecf8427e.ext2
|-- c.ext^Mnewline
|   |-- f.d41d8cd98f00b204e9800998ecf8427e
|   `-- g.with[or].d41d8cd98f00b204e9800998ecf8427e.ext
`-- f^Jnewline.d3b07384d113edec49eaa6238ad5ff00.ext

4 directories, 9 files

Autres conseils

#!/bin/bash
find -type f -print0 | while read -d $'\0' file
do
    md5sum=`md5sum "${file}" | sed -r 's/ .*//'`
    filename=`echo "${file}" | sed -r 's/\.[^./]*$//'`
    extension="${file:${#filename}}"
    filename=`echo "${filename}" | sed -r 's/\.md5sum-[^.]+//'`
    if [[ "${file}" != "${filename}.md5sum-${md5sum}${extension}" ]]; then
        echo "Handling file: ${file}"
        mv "${file}" "${filename}.md5sum-${md5sum}${extension}"
    fi
done
  • testé sur des fichiers contenant des espaces comme 'un b'
  • Testé sur des fichiers contenant plusieurs extensions comme 'a.b.c'
  • Testé avec des répertoires contenant des espaces et / ou des points.
  • Testé sur les fichiers contenant pas d'extension dans les répertoires contenant des points, tels que 'a.b / c'
  • Mise à jour :. Mises à jour maintenant hash si les modifications du fichier

Points clés:

  • Utilisation de print0 canalisé vers while read -d $'\0', pour gérer correctement les espaces dans les noms de fichiers.
  • md5sum peut être remplacé par votre fonction de hachage préférée. Le sed supprime le premier espace et tout ce qui suit de la sortie de md5sum.
  • Le nom du fichier de base est extrait à l'aide d'une expression régulière qui trouve la dernière période qui n'est pas suivie d'une autre barre oblique (de sorte que dans les noms de répertoire ne sont pas comptés dans le cadre de l'extension).
  • L'extension est trouvée en utilisant une sous-chaîne avec un indice de départ comme la longueur du nom de fichier de base.

La logique des besoins est suffisamment complexe pour justifier l'utilisation de Python au lieu de bash. Il devrait fournir une solution plus facile à lire, extensible et maintenable.

#!/usr/bin/env python
import hashlib, os

def ishash(h, size):
    """Whether `h` looks like hash's hex digest."""
    if len(h) == size: 
        try:
            int(h, 16) # whether h is a hex number
            return True
        except ValueError:
            return False

for root, dirs, files in os.walk("."):
    dirs[:] = [d for d in dirs if not d.startswith(".")] # skip hidden dirs
    for path in (os.path.join(root, f) for f in files if not f.startswith(".")):
        suffix = hash_ = "." + hashlib.md5(open(path).read()).hexdigest()
        hashsize = len(hash_) - 1
        # extract old hash from the name; add/replace the hash if needed
        barepath, ext = os.path.splitext(path) # ext may be empty
        if not ishash(ext[1:], hashsize):
            suffix += ext # add original extension
            barepath, oldhash = os.path.splitext(barepath) 
            if not ishash(oldhash[1:], hashsize):
               suffix = oldhash + suffix # preserve 2nd (not a hash) extension
        else: # ext looks like a hash
            oldhash = ext
        if hash_ != oldhash: # replace old hash by new one
           os.rename(path, barepath+suffix)

Voici une arborescence de test. Il contient:

  • fichiers sans extension à l'intérieur des répertoires avec un point dans leur nom
  • nom de fichier qui a déjà un hachage dans ce (test sur idempotence)
  • nom de fichier avec deux extensions
  • les nouvelles lignes dans les noms
$ tree a
a
|-- b
|   `-- c.d
|       |-- f
|       |-- f.ext1.ext2
|       `-- g.d41d8cd98f00b204e9800998ecf8427e
|-- c.ext^Mnewline
|   `-- f
`-- f^Jnewline.ext1

7 directories, 5 files

Résultat

$ tree a
a
|-- b
|   `-- c.d
|       |-- f.0bee89b07a248e27c83fc3d5951213c1
|       |-- f.ext1.614dd0e977becb4c6f7fa99e64549b12.ext2
|       `-- g.d41d8cd98f00b204e9800998ecf8427e
|-- c.ext^Mnewline
|   `-- f.0bee89b07a248e27c83fc3d5951213c1
`-- f^Jnewline.b6fe8bb902ca1b80aaa632b776d77f83.ext1

7 directories, 5 files

La solution fonctionne correctement pour tous les cas.


hachage Whirlpool se trouve pas dans la stdlib de python, mais il y a deux extensions python pures et C qui le soutiennent, par exemple, python-mhash.

Pour l'installer:

$ sudo apt-get install python-mhash

Pour l'utiliser:

import mhash

print mhash.MHASH(mhash.MHASH_WHIRLPOOL, "text to hash here").hexdigest()

Sortie:     cbdca4520cc5c131fc3a86109dd23fee2d7ff7be56636d398180178378944a4f41480b938608ae98da7eccbf39a4c79b83a8590c4cb1bace5bc638fc92b3e653


Invoquer whirlpooldeep en Python

from subprocess import PIPE, STDOUT, Popen

def getoutput(cmd):
    return Popen(cmd, stdout=PIPE, stderr=STDOUT).communicate()[0]

hash_ = getoutput(["whirlpooldeep", "-q", path]).rstrip()

git peut fournir un effet de levier pour les problèmes qui doivent suivre un ensemble de fichiers en fonction de leur hash.

Je n'étais pas vraiment satisfait de ma première réponse, car comme je l'ai dit là-bas, ce problème ressemble à ce qui est le mieux résolu avec perl. Vous avez déjà dit dans une édition de votre question que vous avez perl sur la machine OS X que vous voulez exécuter sur, donc je lui ai donné un coup de feu.

Il est difficile d'obtenir tout droit en bash, à savoir d'éviter tout problème avec les noms de fichiers impairs citant, et de se comporter bien avec les noms de fichiers coin cas.

Alors là, il est en Perl, une solution complète à votre problème. Il fonctionne sur tous les fichiers / répertoires listés sur la ligne de commande.


#!/usr/bin/perl -w
# whirlpool-rename.pl
# 2009 Peter Cordes <peter@cordes.ca>.  Share and Enjoy!

use Fcntl;      # for O_BINARY
use File::Find;
use Digest::Whirlpool;

# find callback, called once per directory entry
# $_ is the base name of the file, and we are chdired to that directory.
sub whirlpool_rename {
    print "find: $_\n";
#    my @components = split /\.(?:[[:xdigit:]]{128})?/; # remove .hash while we're at it
    my @components = split /\.(?!\.|$)/, $_, -1; # -1 to not leave out trailing dots

    if (!$components[0] && $_ ne ".") { # hidden file/directory
        $File::Find::prune = 1;
        return;
    }

    # don't follow symlinks or process non-regular-files
    return if (-l $_ || ! -f _);

    my $digest;
    eval {
        sysopen(my $fh, $_, O_RDONLY | O_BINARY) or die "$!";
        $digest = Digest->new( 'Whirlpool' )->addfile($fh);
    };
    if ($@) {  # exception-catching structure from whirlpoolsum, distributed with Digest::Whirlpool.
        warn "whirlpool: couldn't hash $_: $!\n";
        return;
    }

    # strip old hashes from the name.  not done during split only in the interests of readability
    @components = grep { !/^[[:xdigit:]]{128}$/ }  @components;
    if ($#components == 0) {
        push @components, $digest->hexdigest;
    } else {
        my $ext = pop @components;
        push @components, $digest->hexdigest, $ext;
    }

    my $newname = join('.', @components);
    return if $_ eq $newname;
    print "rename  $_ ->  $newname\n";
    if (-e $newname) {
        warn "whirlpool: clobbering $newname\n";
        # maybe unlink $_ and return if $_ is older than $newname?
        # But you'd better check that $newname has the right contents then...
    }
    # This could be link instead of rename, but then you'd have to handle directories, and you can't make hardlinks across filesystems
    rename $_, $newname or warn "whirlpool: couldn't rename $_ -> $newname:  $!\n";
}


#main
$ARGV[0] = "." if !@ARGV;  # default to current directory
find({wanted => \&whirlpool_rename, no_chdir => 0}, @ARGV );

Avantages: - utilise en fait un bain à remous, de sorte que vous pouvez utiliser ce programme exact directement. (Après l'installation libperl-digest-bain à remous). Facile à changer à tout digest fonction que vous voulez, car au lieu de programmes différents avec différents formats de sortie, vous avez le perl Digest interface commune.

  • met en œuvre toutes les autres exigences: ignorer les fichiers cachés (et des fichiers dans les répertoires cachés)

  • .
  • capable de gérer un nom de fichier possible sans erreur ou problème de sécurité. (Plusieurs personnes ont ce droit dans leurs scripts shell).

  • suit les meilleures pratiques pour parcourir une arborescence de répertoires, par chdiring vers le bas dans chaque répertoire (comme ma réponse précédente, avec find -execdir). Cela évite les problèmes avec PATH_MAX, et répertoires étant rebaptisés pendant que vous êtes en cours d'exécution.

  • traitement intelligent des noms qui se terminent par. foo..txt ... -> foo..hash.txt ...

  • Poignées anciens noms de fichiers contenant hash déjà sans les renommer et les renommer en arrière. (Il supprime toute séquence de 128 chiffres hexadécimaux qui est entouré par « » caractères.) Dans le cas de tout correcte, aucune activité d'écriture de disque se produit, lit juste de chaque fichier. Votre solution actuelle s'exécute mv deux fois dans le cas déjà correctement nommé, ce qui provoque des métadonnées de répertoire écrit. Et étant plus lent, parce que ce deux processus qui doivent être execced.

  • efficace. Aucun programme ne sont fork / en lançant le programme, alors que la plupart des solutions qui fonctionnent réellement fini par avoir à quelque chose sed par fichier. Digest :: Whirlpool est mis en œuvre avec une lib partagée en mode natif compilé, il est donc pas lent-perl pur. Cela devrait être plus rapide que l'exécution d'un programme sur tous les fichiers, esp. pour les petits fichiers.

  • Perl prend en charge les chaînes UTF-8, de sorte que les noms de fichiers contenant des caractères non-ascii ne devrait pas être un problème. (Pas sûr si des séquences multi-octets en UTF-8 pourraient inclure l'octet qui signifie ASCII. »Lui-même. Si cela est possible, alors vous devez gérer les chaînes l'UTF-8. Sed ne sait pas UTF-8 . les expressions glob Bash peut.)

  • facilement extensible. Lorsque vous allez mettre cela en un véritable programme, et que vous voulez traiter plus de cas de coin, vous pouvez le faire assez facilement. par exemple. décider ce qu'il faut faire lorsque vous souhaitez renommer un fichier, mais le nom du fichier de hachage du même nom existe déjà.

  • bon rapport d'erreurs. La plupart des scripts shell ont cela, cependant, en passant le long des erreurs des progs qu'ils exécutent.

find . -type f -print | while read file
do
    hash=`$hashcommand "$file"`
    filename=${file%.*}
    extension=${file##*.}
    mv $file "$filename.$hash.$extension"
done

Vous pouvez stocker les résultats dans un fichier, comme dans

find . -type f -exec md5sum {} \; > MD5SUMS

Si vous voulez vraiment un fichier par hachage:

find . -type f | while read f; do g=`md5sum $f` > $f.md5; done

ou même

find . -type f | while read f; do g=`md5sum $f | awk '{print $1}'`; echo "$g $f"> $f-$g.md5; done

Voici mon avis sur la question, en bash. Caractéristiques: Saute fichiers non réguliers; traite correctement les fichiers avec des personnages étranges (à savoir des espaces) dans leurs noms; traite des noms de fichiers; sans extension ignore les fichiers déjà hachurées, il peut être exécuté à plusieurs reprises (bien que si les fichiers sont modifiés entre les courses, il ajoute le nouveau hachage plutôt que de remplacer l'ancien). Je l'ai écrit en utilisant -q md5 comme la fonction de hachage; vous devriez être en mesure de le remplacer par quelque chose d'autre, tant qu'il ne sort que le hachage, pas quelque chose comme nom de fichier => hachage.

find -x . -type f -print0 | while IFS="" read -r -d $'\000' file; do
    hash="$(md5 -q "$file")" # replace with your favorite hash function
    [[ "$file" == *."$hash" ]] && continue # skip files that already end in their hash
    dirname="$(dirname "$file")"
    basename="$(basename "$file")"
    base="${basename%.*}"
    [[ "$base" == *."$hash" ]] && continue # skip files that already end in hash + extension
    if [[ "$basename" == "$base" ]]; then
            extension=""
    else
            extension=".${basename##*.}"
    fi
    mv "$file" "$dirname/$base.$hash$extension"
done

Dans sh ou bash, deux versions. L'une se limite à des fichiers avec des extensions ...

hash () {
  #openssl md5 t.sh | sed -e 's/.* //'
  whirlpool "$f"
}

find . -type f -a -name '*.*' | while read f; do
  # remove the echo to run this for real
  echo mv "$f" "${f%.*}.whirlpool-`hash "$f"`.${f##*.}"
done

Test ...

...
mv ./bash-4.0/signames.h ./bash-4.0/signames.whirlpool-d71b117a822394a5b273ea6c0e3f4dc045b1098326d39864564f1046ab7bd9296d5533894626288265a1f70638ee3ecce1f6a22739b389ff7cb1fa48c76fa166.h
...

Et cette version plus complexe traite tous les fichiers simples, avec ou sans extensions, avec ou sans espaces et des caractères bizarres, etc, etc ...

hash () {
  #openssl md5 t.sh | sed -e 's/.* //'
  whirlpool "$f"
}

find . -type f | while read f; do
  name=${f##*/}
  case "$name" in
    *.*) extension=".${name##*.}" ;;
    *)   extension=   ;;
  esac
  # remove the echo to run this for real
  echo mv "$f" "${f%/*}/${name%.*}.whirlpool-`hash "$f"`$extension"
done

bain à remous est pas un hachage très commun. Vous devrez probablement installer un programme pour calculer. par exemple. Debian / Ubuntu inclut un paquet « tourbillon ». Le programme imprime le hachage d'un fichier par lui-même. apt-cache whirlpool montre que d'autres paquets prennent en charge, y compris le md5deep intéressant.

Certains des anwsers précédents échouera sur les noms de fichiers avec des espaces en eux. Si tel est le cas, mais vos fichiers ne pas avoir de nouvelles lignes dans le nom de fichier, vous pouvez utiliser en toute sécurité \ n comme séparateur.


oldifs="$IFS"
IFS="
"
for i in $(find -type f); do echo "$i";done
#output
# ./base
# ./base2
# ./normal.ext
# ./trick.e "xt
# ./foo bar.dir ext/trick' (name "- }$foo.ext{}.ext2
IFS="$oldifs"

essayez sans définir IFS pour voir pourquoi il importe.

Je vais essayer quelque chose avec IFS = « »; trouver -print0 | en lecture -a tableau, pour diviser le « » caractères, mais j'utilise normalement jamais variables de tableau. Il n'y a pas moyen facile que je vois dans la page de manuel pour insérer le hachage comme le deuxième dernier indice de tableau, et appuyez le dernier élément (l'extension de fichier, si elle en avait un.) Chaque fois que les variables de tableau bash semblent intéressants, je sais il est temps de faire ce que je fais en Perl à la place! Voir les gotchas pour l'utilisation de lecture: http://tldp.org/LDP/abs/html/gotchas.html# BADREAD0

J'ai décidé d'utiliser une autre technique que j'aime: trouver -exec sh -c. Il est le plus sûr, puisque vous n'êtes pas l'analyse syntaxique des noms de fichiers.

Cela devrait faire l'affaire:


find -regextype posix-extended -type f -not -regex '.*\.[a-fA-F0-9]{128}.*'  \
-execdir bash -c 'for i in "${@#./}";do 
 hash=$(whirlpool "$i");
 ext=".${i##*.}"; base="${i%.*}";
 [ "$base" = "$i" ] && ext="";
 newname="$base.$hash$ext";
 echo "ext:$ext  $i -> $newname";
 false mv --no-clobber "$i" "$newname";done' \
dummy {} +
# take out the "false" before the mv, and optionally take out the echo.
# false ignores its arguments, so it's there so you can
# run this to see what will happen without actually renaming your files.

-execdir bash -c après la commande factice « cmd » {} + a le mannequin arg là parce que le premier arg devient $ 0 dans les paramètres de position, ne fait pas partie de la coquille de « $ @ pour » que pour les boucles sur. J'utiliser execdir au lieu de exec donc je n'ai pas à traiter avec des noms de répertoire (ou la possibilité de dépasser PATH_MAX pour dirs imbriquées avec des noms longs, lorsque les noms réels sont tous assez court.)

-non -regex empêche cette d'être appliquée deux fois au même fichier. Bien que bain à remous est un hachage extrêmement longue et mv dit le nom de fichier trop long si je cours deux fois sans que l'enregistrement. (Sur un système de fichiers XFS.)

Les fichiers sans extension obtenir basename.hash. Je devais vérifier spécialement pour éviter une fuite annexant., Ou d'obtenir le nom de base comme l'extension. ${@#./} bandes le premier qui trouve ./ met devant chaque nom de fichier, donc il n'y a pas « » dans toute la chaîne pour les fichiers sans extension.

mv --no-clobber peut être une extension GNU. Si vous n'avez pas GNU mv, faire quelque chose d'autre si vous voulez éviter de supprimer des fichiers existants (par exemple, vous exécutez cette fois, une partie du même fichier sont ajoutés au répertoire avec leurs anciens noms;. Vous l'exécutez à nouveau) OTOH, si vous voulez que le comportement, prenez-le.

Ma solution devrait fonctionner même lorsque les noms de fichiers contiennent un saut de ligne (ils peuvent, vous savez!), Ou tout autre caractère possible. Il serait plus rapide et plus facile en Perl, mais vous demandé shell.

La solution de Wallenborn pour faire un fichier avec tous les checksums (au lieu de renommer l'original) est assez bonne, mais inefficace. Ne pas exécuter md5sum une fois par fichier, exécuter sur autant de fichiers à la fois que s'adaptera sur la ligne de commande:

dir trouver -type f -print0 | xargs -0 md5sum> dir.md5 ou trouver GNU, xargs est construit en (notez le + au lieu de « ; »)  trouver dir -type f -exec md5sum {} +> dir.md5

si vous utilisez juste trouver -print | xargs -d « \ n », vous vissable par les noms de fichiers avec des guillemets en eux, alors soyez prudent. Si vous ne savez pas ce que vous pourriez un jour les fichiers que exécuter ce script, essayez toujours d'utiliser print0 ou exec. Ce esp. vrai si les noms de fichiers sont fournis par les utilisateurs non fiables (ce pourrait être un vecteur d'attaque sur votre serveur.)

En réponse à votre question mise à jour:

  

Si quelqu'un peut commenter sur la façon dont je peux éviter de regarder dans les répertoires cachés avec mon script BASH, il serait très apprécié.

Vous pouvez éviter les répertoires cachés avec find en utilisant

find -name '.?*' -prune -o \( -type f -print0 \)

-name '.*' -prune élague « », et arrêter sans faire quoi que ce soit. : /

Je recommande toujours ma version Perl, cependant. Je mis à jour ... Vous pouvez toujours besoin d'installer Digest :: Whirlpool CPAN, cependant.

Hm, problème intéressant.

Essayez les options suivantes (la fonction mktest est juste pour le test - TDD pour bash:)

Edit:

  • Ajout du support pour les hachages bouillonnants.
  • nettoyage de code
  • mieux citer des noms de fichiers
  • array syntaxe changé pour part-- de test devrait fonctionner avec la plupart des coquilles korn semblables. Notez que pdksh ne supporte pas: l'expansion des paramètres à base (ou plutôt cela signifie autre chose)

Notez également que lorsque le mode en md5-il échoue pour les noms de fichiers avec hash tourbillon semblable, et peut-être vice-versa.

#!/usr/bin/env bash

#Tested with:
# GNU bash, version 4.0.28(1)-release (x86_64-pc-linux-gnu)
# ksh (AT&T Research) 93s+ 2008-01-31
# mksh @(#)MIRBSD KSH R39 2009/08/01 Debian 39.1-4
# Does not work with pdksh, dash

DEFAULT_SUM="md5"

#Takes a parameter, as root path
# as well as an optional parameter, the hash function to use (md5 or wp for whirlpool).
main()
{
  case $2 in
    "wp")
      export SUM="wp"
      ;;
    "md5")
      export SUM="md5"
      ;;
    *)
      export SUM=$DEFAULT_SUM
      ;;
  esac

  # For all visible files in all visible subfolders, move the file
  # to a name including the correct hash:
  find $1 -type f -not -regex '.*/\..*' -exec $0 hashmove '{}' \;
}

# Given a file named in $1 with full path, calculate it's hash.
# Output the filname, with the hash inserted before the extention
# (if any) -- or:  replace an existing hash with the new one,
# if a hash already exist.
hashname_md5()
{
  pathname="$1"
  full_hash=`md5sum "$pathname"`
  hash=${full_hash:0:32}
  filename=`basename "$pathname"`
  prefix=${filename%%.*}
  suffix=${filename#$prefix}

  #If the suffix starts with something that looks like an md5sum,
  #remove it:
  suffix=`echo $suffix|sed -r 's/\.[a-z0-9]{32}//'`

  echo "$prefix.$hash$suffix"
}

# Same as hashname_md5 -- but uses whirlpool hash.
hashname_wp()
{
  pathname="$1"
  hash=`whirlpool "$pathname"`
  filename=`basename "$pathname"`
  prefix=${filename%%.*}
  suffix=${filename#$prefix}

  #If the suffix starts with something that looks like an md5sum,
  #remove it:
  suffix=`echo $suffix|sed -r 's/\.[a-z0-9]{128}//'`

  echo "$prefix.$hash$suffix"
}


#Given a filepath $1, move/rename it to a name including the filehash.
# Try to replace an existing hash, an not move a file if no update is
# needed.
hashmove()
{
  pathname="$1"
  filename=`basename "$pathname"`
  path="${pathname%%/$filename}"

  case $SUM in
    "wp")
      hashname=`hashname_wp "$pathname"`
      ;;
    "md5")
      hashname=`hashname_md5 "$pathname"`
      ;;
    *)
      echo "Unknown hash requested"
      exit 1
      ;;
  esac

  if [[ "$filename" != "$hashname" ]]
  then
      echo "renaming: $pathname => $path/$hashname"
      mv "$pathname" "$path/$hashname"
  else
    echo "$pathname up to date"
  fi
}

# Create som testdata under /tmp
mktest()
{
  root_dir=$(tempfile)
  rm "$root_dir"
  mkdir "$root_dir"
  i=0
  test_files[$((i++))]='test'
  test_files[$((i++))]='testfile, no extention or spaces'

  test_files[$((i++))]='.hidden'
  test_files[$((i++))]='a hidden file'

  test_files[$((i++))]='test space'
  test_files[$((i++))]='testfile, no extention, spaces in name'

  test_files[$((i++))]='test.txt'
  test_files[$((i++))]='testfile, extention, no spaces in name'

  test_files[$((i++))]='test.ab8e460eac3599549cfaa23a848635aa.txt'
  test_files[$((i++))]='testfile, With (wrong) md5sum, no spaces in name'

  test_files[$((i++))]='test spaced.ab8e460eac3599549cfaa23a848635aa.txt'
  test_files[$((i++))]='testfile, With (wrong) md5sum, spaces in name'

  test_files[$((i++))]='test.8072ec03e95a26bb07d6e163c93593283fee032db7265a29e2430004eefda22ce096be3fa189e8988c6ad77a3154af76f582d7e84e3f319b798d369352a63c3d.txt'
  test_files[$((i++))]='testfile, With (wrong) whirlpoolhash, no spaces in name'

  test_files[$((i++))]='test spaced.8072ec03e95a26bb07d6e163c93593283fee032db7265a29e2430004eefda22ce096be3fa189e8988c6ad77a3154af76f582d7e84e3f319b798d369352a63c3d.txt']
  test_files[$((i++))]='testfile, With (wrong) whirlpoolhash, spaces in name'

  test_files[$((i++))]='test space.txt'
  test_files[$((i++))]='testfile, extention, spaces in name'

  test_files[$((i++))]='test   multi-space  .txt'
  test_files[$((i++))]='testfile, extention, multiple consequtive spaces in name'

  test_files[$((i++))]='test space.h'
  test_files[$((i++))]='testfile, short extention, spaces in name'

  test_files[$((i++))]='test space.reallylong'
  test_files[$((i++))]='testfile, long extention, spaces in name'

  test_files[$((i++))]='test space.reallyreallyreallylong.tst'
  test_files[$((i++))]='testfile, long extention, double extention,
                        might look like hash, spaces in name'

  test_files[$((i++))]='utf8test1 - æeiaæå.txt'
  test_files[$((i++))]='testfile, extention, utf8 characters, spaces in name'

  test_files[$((i++))]='utf8test1 - 漢字.txt'
  test_files[$((i++))]='testfile, extention, Japanese utf8 characters, spaces in name'

  for s in . sub1 sub2 sub1/sub3 .hidden_dir
  do

     #note -p not needed as we create dirs top-down
     #fails for "." -- but the hack allows us to use a single loop
     #for creating testdata in all dirs
     mkdir $root_dir/$s
     dir=$root_dir/$s

     i=0
     while [[ $i -lt ${#test_files[*]} ]]
     do
       filename=${test_files[$((i++))]}
       echo ${test_files[$((i++))]} > "$dir/$filename"
     done
   done

   echo "$root_dir"
}

# Run test, given a hash-type as first argument
runtest()
{
  sum=$1

  root_dir=$(mktest)

  echo "created dir: $root_dir"
  echo "Running first test with hashtype $sum:"
  echo
  main $root_dir $sum
  echo
  echo "Running second test:"
  echo
  main $root_dir $sum
  echo "Updating all files:"

  find $root_dir -type f | while read f
  do
    echo "more content" >> "$f"
  done

  echo
  echo "Running final test:"
  echo
  main $root_dir $sum
  #cleanup:
  rm -r $root_dir
}

# Test md5 and whirlpool hashes on generated data.
runtests()
{
  runtest md5
  runtest wp
}

#For in order to be able to call the script recursively, without splitting off
# functions to separate files:
case "$1" in
  'test')
    runtests
  ;;
  'hashname')
    hashname "$2"
  ;;
  'hashmove')
    hashmove "$2"
  ;;
  'run')
    main "$2" "$3"
  ;;
  *)
    echo "Use with: $0 test - or if you just want to try it on a folder:"
    echo "  $0 run path (implies md5)"
    echo "  $0 run md5 path"
    echo "  $0 run wp path"
  ;;
esac

en utilisant zsh:

$ ls
a.txt
b.txt
c.txt

La magie:

$ FILES=**/*(.) 
$ # */ stupid syntax coloring thinks this is a comment
$ for f in $FILES; do hash=`md5sum $f | cut -f1 -d" "`; mv $f "$f:r.$hash.$f:e"; done
$ ls
a.60b725f10c9c85c70d97880dfe8191b3.txt
b.3b5d5c3712955042212316173ccf37be.txt
c.2cd6ee2c70b0bde53fbe6cac3c8b8bb1.txt

deconstruction heureux!

Modifier: les fichiers ajoutés dans les sous-répertoires et des guillemets autour argument mv

Ruby:

#!/usr/bin/env ruby
require 'digest/md5'

Dir.glob('**/*') do |f|
  next unless File.file? f
  next if /\.md5sum-[0-9a-f]{32}/ =~ f
  md5sum = Digest::MD5.file f
  newname = "%s/%s.md5sum-%s%s" %
    [File.dirname(f), File.basename(f,'.*'), md5sum, File.extname(f)]
  File.rename f, newname
end

Poignées noms qui ont des espaces, pas d'extension, et qui ont déjà été haché.

Ignore les fichiers et répertoires cachés -. Ajouter File::FNM_DOTMATCH comme second argument de glob si c'est désiré

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top