Frage

  

Problem Spezifikation:

     

ein Verzeichnis angegeben, möchte ich durch das Verzeichnis und dessen nicht verborgenen Unterverzeichnisse,
iterieren   und fügen Sie einen Whirlpool Hash in den nicht versteckt   Datei-Namen.
  Wenn das Skript erneut ausführen würde es einen alten Hash durch einen neuen ersetzen würde.

     
    

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

         

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

  


  

Frage:

     

a) Wie würden Sie das tun?

     

b) Von den alle Methoden zur Verfügung, was macht Ihre Methode am besten geeignet?


  

Urteil:

     

Danke allen, ich habe SeigeX Antwort für seine Geschwindigkeit und Tragbarkeit gewählt.
  Es ist emprically schneller als die anderen bash Varianten,
  und es funktionierte ohne Änderung auf meinem Mac OS X-Rechner.

War es hilfreich?

Lösung

Aktualisiert zu beheben:
1. Dateinamen mit ‚[‘ oder ‚]‘ in ihrem Namen (wirklich, jetzt ein beliebiges Zeichen. Siehe Kommentar)
2. Umgang mit md5sum, wenn eine Datei mit einem umgekehrten Schrägstrich oder Newline in seinem Namen
Hashing 3. Functionized Hash-Überprüfung algo für Modularität
4. Überarbeitete Hash-Prüflogikschaltung Doppel Negative

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

Dieser Code hat folgende Vorteile gegenüber anderen Einträgen so weit

  • Es ist vollständig kompatibel mit Bash-Versionen 2.0.2 und darüber hinaus
  • Keine überflüssigen Anrufe an andere Binärdateien wie sed oder grep; verwendet builtin Parameter Expansion statt
  • Verwendet Prozess Substitution für ‚finden‘ anstelle eines Rohres, keine Unterschale ist auf diese Weise hergestellt
  • nimmt das Verzeichnis als Argument zu arbeiten und hat eine Plausibilitätsprüfung auf sie
  • Verwendet $ () statt `` Notation für Kommandosubstitution wird diese veraltet
  • Arbeiten mit Dateien mit Leerzeichen
  • Arbeiten mit Dateien mit Zeilenumbrüchen
  • Arbeiten mit Dateien mit mehreren Erweiterungen
  • Arbeiten mit Dateien ohne Erweiterung
  • Ist durchqueren nicht versteckte Verzeichnisse
  • Does nicht überspringen Pre-Hash-Dateien, wird die Hash gemäß der Spezifikation neu berechnen

Test Baum

$ 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 

Ergebnis

$ 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

Andere Tipps

#!/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
  • Getestet auf Dateien mit Leerzeichen wie 'a b'
  • Getestet auf Dateien, die mehrere Erweiterungen wie 'a.b.c'
  • enthalten
  • Getestet mit Verzeichnissen mit Leerzeichen und / oder Punkten.
  • Getestet auf Dateien keine Erweiterung in Verzeichnissen enthalten Punkte, wie ‚a.b / c‘
  • enthalten
  • Aktualisiert . Nun aktualisiert Hashes, wenn die Datei Änderungen

Kernpunkte:

  • Die Verwendung von print0 zu while read -d $'\0' geleitet, um richtig Leerzeichen in Dateinamen zu verarbeiten.
  • md5sum kann mit Ihrer Lieblings-Hash-Funktion ersetzt werden. Der sed entfernt den ersten Raum und alles, nachdem es von dem Ausgang des md5sum.
  • Der Basisdateiname extrahiert wird einen regulären Ausdruck, den die letzte Periode findet, die nicht von einem anderen Schrägstrich wird (so dass Perioden in Verzeichnisnamen nicht als Teil der Erweiterung gezählt werden).
  • Die Erweiterung wird durch die Verwendung eines Teilstrings mit dem Startindex wie die Länge des Basisdateinamen gefunden.

Die Logik der Anforderungen ist komplex genug, um die Verwendung von Python statt bash zu rechtfertigen. Es sollte eine besser lesbare, erweiterbare bieten, und wartbar Lösung.

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

Hier ist ein Testverzeichnisbaum. Es enthält:

  • Dateien ohne Erweiterung in Verzeichnissen mit einem Punkt in ihrem Namen
  • Dateiname, die bereits einen Hash drin (Test auf Idempotenz) hat
  • Dateiname mit zwei Erweiterungen
  • Zeilenumbrüche in Namen
$ tree a
a
|-- b
|   `-- c.d
|       |-- f
|       |-- f.ext1.ext2
|       `-- g.d41d8cd98f00b204e9800998ecf8427e
|-- c.ext^Mnewline
|   `-- f
`-- f^Jnewline.ext1

7 directories, 5 files

Ergebnis

$ 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

Die Lösung funktioniert richtig für alle Fälle.


Whirlpool Hash ist nicht in Python stdlib, aber es gibt sowohl reine Python und C-Erweiterungen, die es zum Beispiel unterstützen, python-mhash.

Um es zu installieren:

$ sudo apt-get install python-mhash

Um es zu verwenden:

import mhash

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

Ausgabe:     cbdca4520cc5c131fc3a86109dd23fee2d7ff7be56636d398180178378944a4f41480b938608ae98da7eccbf39a4c79b83a8590c4cb1bace5bc638fc92b3e653


Aufrufe 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 kann für die Probleme mit Hebelwirkung, die auf ihrem Hashes anhand von Dateien auf protokollierenden.

Ich war nicht sehr zufrieden mit meiner ersten Antwort, da, wie ich es gesagt, das Problem wie sieht es am besten mit Perl gelöst wird. Du hast gesagt, bereits in einer Bearbeitung Ihrer Frage, die Sie Perl auf dem OS X-Rechner haben Sie diese auf ausführen möchten, also gab ich ihm einen Schuss.

Es ist schwer, alles richtig in bash zu erhalten, das heißt alle zitieren Probleme mit ungeraden Dateinamen zu vermeiden und schön mit Ecke Fall Dateinamen verhalten.

Also hier ist es in Perl, eine komplette Lösung für Ihr Problem. Es läuft über alle Dateien / Verzeichnisse auf der Kommandozeile angezeigt.


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

Vorteile: - tatsächlich nutzt Whirlpool, so dass Sie genau dieses Programm direkt nutzen können. (Nach der Installation von libperl-digest-Whirlpool). Leicht zu jedem verdauen ändern Funktion, die Sie wollen, weil anstelle von verschiedenen Programmen mit verschiedenen Ausgabeformaten haben Sie die Perl-Digest gemeinsame Schnittstelle.

  • implementiert alle andere Anforderungen: versteckte Dateien ignorieren (und Dateien unter versteckten Verzeichnissen)

  • .
  • Lage, alle möglichen Dateinamen ohne Fehler oder Sicherheitsproblem zu behandeln. (Mehrere Menschen haben dieses Recht in ihren Shell-Skripten).

  • folgt Best Practices für einen Verzeichnisbaum durchlaufen, durch chdiring nach unten in jedes Verzeichnis (wie meine vorherige Antwort, mit find -execdir). Dies vermeidet Probleme mit PATH_MAX und mit Verzeichnissen umbenannt werden, während Sie laufen.

  • geschickter Umgang mit Dateinamen, die am Ende mit. foo..txt ... -> foo..hash.txt ...

  • Griffe alten Dateinamen sie Hashes bereits ohne sie zu umbenennen und dann die Umbenennung zurück enthält. (Es Streifen eine beliebige Folge von 128 hexadezimalen Ziffern, die durch „“ Zeichen umgeben ist.) In der alles korrigieren Fall keine Plattenschreibaktivität geschieht, liest nur jeder Datei. Ihre aktuelle Lösung läuft mv zweimal in dem bereits korrekt genannten Fall verursacht Verzeichnis Metadaten schreibt. Und da langsamer, weil die zwei Prozesse ist, die execced werden müssen.

  • effizient. Keine Programme sind Gabel / execed, während die meisten der Lösungen, die tatsächlich am Ende funktionieren würde pro-Datei zu sed etwas mit. Digest :: Whirlpool ist mit einer nativ kompilierten gemeinsamen lib implementiert, so langsam, es ist nicht rein-perl. Dies sollte schneller sein als bei jeder Datei ein Programm ausgeführt wird, esp. für kleine Dateien.

  • Perl unterstützt UTF-8-Zeichenketten, also Dateinamen mit Nicht-ASCII-Zeichen sollte kein Problem sein. (Nicht sicher, ob ein Multi-Byte-Sequenzen in UTF-8 könnte das Byte enthalten, das bedeutet ASCII ‚‘ für sich allein. Wenn das möglich ist, dann müssen Sie UTF-8 bewusst String-Handling. Sed weiß nicht UTF-8 kann. Bash glob Ausdrücke.)

  • leicht erweiterbar. Wenn Sie gehen, diese in ein richtiges Programm zu setzen, und Sie wollen mehr Ecke Fälle behandeln, können Sie dies ganz einfach tun. z.B. entscheiden, was zu tun ist, wenn Sie eine Datei umbenennen möchten aber den Hash-Namen Dateiname bereits vorhanden ist.

  • gute Fehlerberichterstattung. Die meisten Shell-Skripte haben dies jedoch, indem sie entlang Fehler aus den progs vorbei sie ausgeführt werden.

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

Sie können die Ergebnisse in einer Datei, wie in

speichern möchten
find . -type f -exec md5sum {} \; > MD5SUMS

Wenn Sie wirklich wollen, eine Datei pro hash:

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

oder auch

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

Hier ist mein nehmen auf sie, in bash. Features: überspringen nicht reguläre Dateien; richtig behandelt Dateien mit seltsamen Zeichen (das heißt Leerzeichen) in ihrem Namen; befasst sich mit erweiterungsDateiNamen; überspringt bereits gehasht Dateien, so kann es immer wieder ausgeführt werden (obwohl, wenn Dateien zwischen den Läufen geändert werden, den neuen Hash anstatt ersetzt die alten hinzufügt). Ich schrieb es md5 -q als Hash-Funktion verwendet wird; Sie sollten dies sonst mit nichts ersetzen können, solange es nur den Hash gibt, nicht so etwas wie Dateiname => 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

sh oder bash, zwei Versionen. Man beschränkt sich auf Dateien mit den Erweiterungen ...

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

Testing ...

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

Und diese komplexere Version verarbeitet alle normalen Dateien mit oder ohne Erweiterungen, mit oder ohne Leerzeichen und ungerade Zeichen, 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

Whirlpool ist nicht ein sehr verbreiteter Hash. Sie werden wahrscheinlich ein Programm zu berechnen, es zu installieren. z.B. Debian / Ubuntu gehören ein "Whirlpool" -Paket. Das Programm druckt den Hash einer Datei von selbst aus. apt-cache search Whirlpool zeigt, dass einige andere Pakete unterstützen es, einschließlich der interessanten md5deep.

Einige der früheren anwsers auf Dateinamen mit Leerzeichen in ihnen scheitern. Wenn dies der Fall ist, aber Ihre Dateien haben noch keine Zeilenumbrüche in den Dateinamen, dann können Sie sicher \ n als Trennzeichen verwenden.


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"

versuchen, ohne IFS Einstellung zu sehen, warum es wichtig ist.

Ich wollte etwas mit IFS = versuchen „“; finden -print0 | während -a-Array gelesen, aufzuspalten on "" Zeichen, aber ich normalerweise nie Array-Variablen verwenden. Es gibt keine einfache Möglichkeit, dass ich in der Manpage den Hash als zweit letzten Array-Index einzufügen, und drücken Sie das letzte Element nach unten (der Dateierweiterung, wenn es einen hat.) Jedes Mal, bash Array-Variablen interessant aussehen, ich weiß, es ist Zeit zu tun, was ich stattdessen in perl zu tun! Siehe den gotchas für die Verwendung von Lese: http://tldp.org/LDP/abs/html/gotchas.html# BADREAD0

Ich entschied, eine andere Technik zu verwenden Ich mag: finden -exec sh -c. Es ist die sicherste, da Sie nicht die Dateinamen Parsen.

Das sollte es tun:


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 'cmd' dummy {} + hat die Dummy arg dort, weil die ersten arg nach dem Befehl in dem Positionsparameter Shell $ 0 wird, nicht Teil von "$ @", die für Schleifen über. Ich benutze execdir statt exec so dass ich nicht mit Verzeichnisnamen zu tun haben (oder die Möglichkeit einer Überschreitung PATH_MAX für verschachtelte Verzeichnisse mit langen Namen, wenn die tatsächlichen Dateinamen alle kurz genug sind.)

-keine -regex verhindert, dass diese zweimal auf die gleiche Datei angelegt wird. Obwohl Whirlpool ist eine extrem lange Hash und mv sagt Dateiname zu lang, wenn ich es laufen zweimal ohne diese zu überprüfen. (Auf einem XFS-Dateisystem).

Dateien ohne Erweiterung bekommen basename.hash. Ich hatte speziell zu überprüfen, um zu vermeiden, ein nachgestelltes anhängen., Oder die Basisnamen als Erweiterung zu bekommen. ${@#./} abstreift die führende ./ das bringt vor jedem Dateinamen finden, so gibt es kein „“ in der gesamten Zeichenfolge für Dateien ohne Erweiterung.

mv --no-clobber kann eine GNU-Erweiterung sein. Wenn Sie nicht GNU mv haben, etwas anderes tun, wenn Sie vorhandene Dateien vermeiden wollen Löschen (zB Sie laufen dieses eine Mal, werden einige der gleichen Datei in das Verzeichnis mit ihren alten Namen hinzugefügt;. Sie führen Sie es erneut) OTOH, wenn Sie dieses Verhalten wollen, nur nehmen Sie es aus.

Meine Lösung sollte auch funktionieren, wenn die Dateinamen eine neue Zeile enthalten (sie können, wissen Sie!), Oder jede andere mögliche Zeichen. Es wäre einfacher und schneller in Perl, aber man fragt nach Shell.

Wallen-Lösung eine Datei mit allen Prüfsummen für die Herstellung von (anstelle der ursprünglichen umbenennen) ist ziemlich gut, aber ineffizient. laufen md5sum nicht einmal pro Datei, führen Sie es auf so viele Dateien auf einmal, wie auf seiner Kommandozeile passen:

finden dir -type f -print0 | xargs -0 md5sum> dir.md5 oder mit GNU finden, ist xargs eingebaut (beachten Sie die + statt ‚;‘)  finden dir -type f -exec md5sum {} +> dir.md5

, wenn Sie nur -Druck verwenden finden | xargs -d ‚\ n‘, werden Sie von Dateinamen mit Anführungszeichen in ihnen, so vorsichtig sein, vermasselt. Wenn Sie nicht wissen, welche Dateien Sie könnten einen Tag dieses Skript ausführen auf, immer versuchen, print0 oder -exec zu verwenden. Dies ist besonders. true, wenn die Dateinamen von nicht vertrauenswürdigen Benutzern selbst eingegeben werden (d könnte ein Angriffsvektor auf dem Server sein.)

Als Antwort auf Ihre Frage aktualisiert:

  

Wenn jemand kommentieren kann, wie ich vermeiden kann, in versteckten Verzeichnissen mit meinem BASH Skript suchen, wäre es sehr geschätzt werden.

Sie können mit find versteckte Verzeichnisse vermeiden, indem Sie mit

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

-name '.*' -prune wird beschneiden „“, und zu stoppen, ohne etwas zu tun. : /

Ich würde immer noch meine Perl-Version empfehlen, though. Ich kann es aktualisiert ... Sie können immer noch Digest :: Whirlpool von CPAN installieren müssen, though.

Hm, interessantes Problem.

Versuchen Sie, die folgenden (die mktest Funktion nur zum Testen ist - TDD für bash:)

Edit:

  • Unterstützung für Whirlpool-Hashes.
  • Code Bereinigung
  • besser unter Angabe von Dateinamen
  • geändert Array-Syntax für Test part-- sollte jetzt mit den meisten Korn artigen Schalen arbeiten. Beachten Sie, dass pdksh nicht unterstützt: -basierte Parameter Expansion (oder besser gesagt es bedeutet etwas anderes)

Beachten Sie auch, dass, wenn in md5-Modus für Dateinamen mit Whirlpool artigen Hashes ausfällt, und möglicherweise umgekehrt.

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

zsh mit:

$ ls
a.txt
b.txt
c.txt

Die 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

Happy Dekonstruktion!

Edit: hinzugefügt Dateien in Unterverzeichnissen und Anführungszeichen um mv Argument

Rubin:

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

Griffe Dateinamen, die Leerzeichen enthalten, keine Erweiterung, und das bereits gehasht worden.

Ignoriert versteckte Dateien und Verzeichnisse -. File::FNM_DOTMATCH als zweites Argument von glob hinzufügen, wenn dies gewünscht ist

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top