Domanda

  

Problema Specifica:

     

Data una directory, voglio scorrere l'elenco e la sua sub-directory non nascoste,
  e aggiungere un hash idromassaggio nella non-nascosto   i nomi di file.
  Se lo script è ri-eseguire sarebbe sostituirebbe un vecchio hash con uno nuovo.

     
    

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

         

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

  

  

Domanda:

     

a) Come si dovrebbe fare questo?

     

b) Dei tutti i metodi disponibili per voi, ciò che rende il metodo più adatto?

  

Verdetto:

     

Grazie a tutti, ho scelto la risposta di SeigeX per la sua velocità e la portabilità.
  È emprically più veloce rispetto alle altre varianti bash,
  e ha funzionato senza alterazione sulla mia macchina Mac OS X.

È stato utile?

Soluzione

Aggiornamento per risolvere:
1. I nomi dei file con '[' o ']' nel nome (in realtà, qualsiasi carattere ora. Vedere commento)
2. Gestione dei md5sum quando hashing un file con un backslash o di nuova riga nel suo nome
3. Functionized algo hash-controllo per la modularità
4. logica hash-checking riscritta per rimuovere doppie negazioni

#!/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 \))

Questo codice ha i seguenti vantaggi rispetto ad altre voci finora

  • E 'pienamente compatibile con le versioni Bash 2.0.2 e oltre
  • Nessuna chiamata superfluo altri binari come sed o grep; utilizza l'espansione di parametro incorporato al posto
  • Utilizza sostituzione di processo per 'trovare' invece di un tubo, non sub-shell è fatto in questo modo
  • è del directory di lavorare su come argomento e fa un controllo di integrità su di esso
  • Utilizza $ (), piuttosto che `` notazione per la sostituzione di comando, quest'ultimo è deprecato
  • funziona con i file con spazi
  • Funziona con i file con a capo
  • Funziona con i file con estensioni multiple
  • Funziona con i file senza estensione
  • Non attraversare le directory nascoste
  • non saltare file pre-hash, si ricalcola l'hash come da spec

Prova Albero

$ 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 

Risultato

$ 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

Altri suggerimenti

#!/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
  • Testato su file che contengono spazi come 'un b'
  • Testato su file contenenti più estensioni come 'a.b.c'
  • Testato con le directory contenenti spazi e / o punti.
  • Testato su file che contengono alcuna estensione all'interno directory che contengono i puntini, come 'a.b / c'
  • Aggiornamento :. Ora aggiorna hash se il file modifiche

Punti chiave:

  • L'utilizzo di print0 convogliato a while read -d $'\0', per gestire correttamente gli spazi nei nomi di file.
  • md5sum può essere sostituito con la funzione di hash preferito. La sed rimuove il primo spazio e tutto quello che segue dall'uscita del md5sum.
  • Il nome file base viene estratto utilizzando un'espressione regolare che trova nell'ultimo periodo non è seguita da un'altra barra (in modo che i periodi nei nomi di directory non sono contati come parte dell'estensione).
  • L'estensione viene determinato usando una sottostringa con indice di partenza come lunghezza del file di base.

La logica dei requisiti è abbastanza complessa da giustificare l'uso di Python al posto di bash. Esso dovrebbe fornire una soluzione più leggibile, estensibile e mantenibile.

#!/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)

Ecco un albero di directory di prova. Esso contiene:

  • file senza estensione all'interno di directory con un punto nel nome
  • il nome del file che ha già un hash in esso (prova su idempotenza)
  • nome di file con due estensioni
  • a capo nei nomi
$ tree a
a
|-- b
|   `-- c.d
|       |-- f
|       |-- f.ext1.ext2
|       `-- g.d41d8cd98f00b204e9800998ecf8427e
|-- c.ext^Mnewline
|   `-- f
`-- f^Jnewline.ext1

7 directories, 5 files

Risultato

$ 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 soluzione funziona correttamente per tutti i casi.


Whirlpool hash non è in stdlib di Python, ma ci sono entrambe le estensioni puri Python e C che lo supportano per esempio, python-mhash.

Per installarlo:

$ sudo apt-get install python-mhash

Per usarlo:

import mhash

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

Output:     cbdca4520cc5c131fc3a86109dd23fee2d7ff7be56636d398180178378944a4f41480b938608ae98da7eccbf39a4c79b83a8590c4cb1bace5bc638fc92b3e653


Invocare whirlpooldeep in 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 in grado di fornire con leva per i problemi che hanno bisogno di insieme della pista di file in base alle loro hash.

Non ero soddisfatto della mia prima risposta, poiché, come ho detto ci, questo problema sembra che sia meglio risolto con perl. È già detto in una modifica della tua domanda di avere perl sulla macchina OS X che si desidera eseguire questo su, così ho dato un colpo.

E 'difficile ottenere tutto a destra in bash, cioè evitando gli eventuali problemi che citano con nomi strani, e di comportarsi bene con angolo-caso i nomi dei file.

Così qui è in Perl, una soluzione completa per il vostro problema. Funziona su tutti i file / directory elencate sulla sua linea di comando.


#!/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 );

Vantaggi: - in realtà utilizza idromassaggio, in modo da poter utilizzare direttamente questo programma esatto. (Dopo l'installazione di libperl-digest-idromassaggio). Facile cambiare a qualsiasi digest funzione che si desidera, perché invece di diversi programmi con diversi formati di output, si ha l'interfaccia comune perl Digest.

  • implementa tutti gli altri requisiti: ignorare i file nascosti (e file in cartelle nascoste)

  • .
  • in grado di gestire ogni possibile nome del file senza errori o problema di sicurezza. (Molte persone hanno ottenuto questo diritto nei loro script di shell).

  • segue le migliori pratiche per attraversare un albero di directory, per chdiring giù in ogni directory (come la mia risposta precedente, con find -execdir). Questo consente di evitare problemi con PATH_MAX, e con gli indici di essere rinominate mentre si sta eseguendo.

  • la gestione intelligente di nomi di file che terminano con. foo..txt ... -> foo..hash.txt ...

  • Maniglie vecchi nomi di file contenenti gli hash già senza di loro rinominare e poi li rinomina indietro. (Si spoglia qualsiasi sequenza di cifre esadecimali 128 che è circondato da caratteri "".) Nel caso tutto-corretto, alcuna attività di scrittura del disco accade, appena legge di ogni file. La vostra soluzione attuale corre mv due volte nel caso già correttamente nome, causando metadati directory scrive. Ed essendo più lento, perché è due processi che devono essere execced.

  • efficiente. Nessun programma sono forcella / execed, mentre la maggior parte delle soluzioni che funzionano davvero finito per dover qualcosa sed per file. Digest :: Whirlpool è implementata con un lib condiviso nativamente-compilato, quindi non è lento pure-perl. Questo dovrebbe essere più veloce di esecuzione di un programma su ogni file, esp. per file di piccole dimensioni.

  • Perl supporta stringhe UTF-8, in modo da nomi di file con caratteri non ASCII non dovrebbe essere un problema. (Non so se eventuali sequenze multi-byte in UTF-8 potrebbe includere l'byte che significa ASCII '' per conto suo. Se questo è possibile, allora avete bisogno di UTF-8 consapevole gestione delle stringhe. Sed non sa UTF-8 . espressioni glob di Bash può.)

  • facilmente estensibile. Quando si va a mettere questo in un programma vero e proprio, e si desidera gestire più casi d'angolo, è possibile farlo abbastanza facilmente. per esempio. decidere che cosa fare quando si desidera rinominare un file, ma il nome del file hash nome esiste già.

  • buona segnalazione degli errori. La maggior parte degli script di shell hanno questo, però, passando lungo gli errori dei progs che corrono.

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

Si potrebbe desiderare di memorizzare i risultati in un unico file, come in

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

Se si vuole veramente un file per ogni hash:

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

o anche

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

Ecco il mio prendere su di esso, in bash. Caratteristiche: ignora i file non regolari; si occupa in modo corretto con i file con caratteri strani (cioè spazi) nei loro nomi; si occupa di nomi di file senza estensione; ignora i file già hash, in modo che possa essere eseguito più volte (anche se i file vengono modificati tra le esecuzioni, si aggiunge la nuova hash invece di sostituire quello vecchio). L'ho scritto usando md5 q come funzione hash; si dovrebbe essere in grado di sostituire questo con qualsiasi altra cosa, purché emette solo l'hash, non qualcosa come nomefile => hash.

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

In sh o bash, due versioni. Uno si limita a file con estensioni ...

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
...

E questa versione più complessa elabora tutti i file semplici, con o senza le estensioni, con o senza spazi e caratteri strani, ecc, ecc ...

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

Whirlpool non è un hash molto comune. Probabilmente dovrete installare un programma per calcolarlo. per esempio. Debian / Ubuntu includono un pacchetto di "idromassaggio". Il programma stampa l'hash di un file da solo. apt-cache search idromassaggio dimostra che alcuni altri pacchetti supportano, tra cui il md5deep interessante.

Alcuni dei anwsers precedenti non riuscirà a nomi di file con spazi tra loro. Se questo è il caso, ma i file non hanno alcun a capo nel nome del file, quindi è possibile utilizzare in modo sicuro \ n come delimitatore.


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"

provare senza impostare IFS per vedere perché è importante.

Stavo per provare qualcosa con IFS = ""; trovare -print0 | mentre lettura -a matrice, per dividere il "" personaggi, ma io di solito non usano mai le variabili di matrice. Non c'è modo più semplice che vedo nella pagina man per inserire l'hash come penultima indice di array, e spingere verso il basso l'ultimo elemento (l'estensione del file, se avesse uno.) Ogni volta che le variabili di matrice bash aspetto interessante, lo so è il momento di fare quello che sto facendo in perl invece! Vedere i grattacapi per l'utilizzo di lettura: http://tldp.org/LDP/abs/html/gotchas.html# BADREAD0

ho deciso di usare un'altra tecnica che mi piace: trovare -exec sh -c. E 'il più sicuro, dal momento che non stai analisi i nomi dei file.

Questo dovrebbe fare il trucco:


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.
manichino

-execdir bash -c 'cmd' {} + ha l'arg manichino lì perché il primo arg dopo il comando diventa $ 0 parametri posizionali della shell, non fa parte del "$ @" che i cicli for over. Io uso execdir al posto di exec in modo da non avere a che fare con i nomi di directory (o la possibilità di superare PATH_MAX per dirs nidificate con nomi lunghi, quando i nomi dei file reali sono abbastanza tutto a breve.)

-non -regex impedisce questo venga applicato due volte lo stesso file. Sebbene idromassaggio è una lunghissima hash, e MV dice Nome file troppo lungo se l'eseguo due volte senza che il check. (Su un filesystem XFS.)

I file senza estensione ottenere basename.hash. Ho dovuto controllare appositamente per evitare l'aggiunta di un finale., O di ottenere il nome di base come l'estensione. ${@#./} estrae il principale ./ che trovano mette di fronte a ogni nome di file, quindi non c'è "" in tutta la stringa per i file senza estensione.

mv --no-clobber può essere un'estensione GNU. Se non si dispone di GNU mv, fare qualcosa di diverso, se si vuole evitare l'eliminazione di file esistenti (ad esempio, si esegue questo, una volta, un po 'dello stesso file vengono aggiunti alla directory con i loro vecchi nomi;. Lo si esegue di nuovo) OTOH, se si desidera che il comportamento, basta tirarla fuori.

La mia soluzione dovrebbe funzionare anche quando i nomi dei file contengono un ritorno a capo (possono, si sa!), O di qualsiasi altro carattere possibile. Sarebbe più semplice e veloce in Perl, ma è chiesto shell.

La soluzione di wallenborn per fare un file con tutti i checksum (invece di rinominare l'originale) è abbastanza buono, ma inefficiente. Non eseguire md5sum una volta per file, eseguirlo su tutti i file in una sola volta, come si inserisce sulla sua linea di comando:

trova dir -type f print0 | xargs -0 md5sum> dir.md5 o con GNU trovare, xargs è costruito in (notare il + invece di ';')  trovare dir tipo md5sum exec f {} +> dir.md5

se si utilizza trovare -print | xargs -d '\ n', sarete avvitato da nomi di file con le virgolette in loro, quindi fate attenzione. Se non si sa quali file si potrebbe un giorno eseguire questo script su, cerca sempre di usare print0 o -exec. Questo è esp. vero se i nomi dei file sono forniti dagli utenti non attendibili (cioè potrebbe essere un vettore di attacco sul vostro server.)

In risposta alla tua domanda aggiornato:

  

Se qualcuno può commentare come posso evitare di guardare in cartelle nascoste con la mia BASH Scrittura, sarebbe molto apprezzato.

È possibile evitare le directory nascoste con find utilizzando

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

-name '.*' -prune sarà potare "", e smettere senza fare nulla. : /

I consiglio comunque vivamente la mia versione di Perl, però. Ho aggiornato che ... Si può ancora bisogno di installare Digest :: Whirlpool da CPAN, però.

Hm, problema interessante.

Provare la seguente (la funzione mktest è solo per i test - TDD per bash:!)

Modifica:

  • Aggiunto il supporto per gli hash idromassaggio.
  • il codice di pulitura
  • meglio citando dei nomi di file
  • cambiato array-sintassi per prova part-- ora dovrebbe funzionare con la maggior parte delle shell Korn-like. Si noti che pdksh non supporta: espansione dei parametri based (o meglio, vuol dire qualcosa d'altro)

Si noti anche che quando è in md5-mode non riesce per i nomi di file con hash idromassaggio-like, e possibilmente viceversa.

#!/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

usando zsh:

$ ls
a.txt
b.txt
c.txt

La magia:

$ 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

decostruzione Felice!

Modifica: aggiunta di file in sottodirectory e virgolette intorno argomento mv

Rubino:

#!/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

Maniglie nomi di file che non hanno spazi, senza estensione, e che sono già state hash.

ignora i file e le directory nascosti -. Aggiungere File::FNM_DOTMATCH come secondo argomento di glob se questo è desiderato

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top