Benennen Sie alle Dateien in einem Verzeichnis in den MD5-Hash ihres Dateinamens um (nicht Inhalt).

10

Ich bin sehr neu in Linux / Befehlszeile und muss die Namen von 10K + -Dateien (eindeutige Namen) verschlüsseln, damit sie mit dem MD5-verschlüsselten Namen in der mySQL-Datenbank übereinstimmen.
Ich habe gesehen, wie Sie ein Verzeichnis von Dateien umbenennen und den Hash einer Datei abrufen können ( mdsum? ), Aber ich bin nicht sicher, wie ich den Hash des Dateinamens abrufen und diese Datei dann in den generierten Hash umbenennen kann die Erweiterung dh

mynicepicture.jpg > fba8255e8e9ce687522455f3e1561e53.jpg 

Es scheint, als sollte es eine einfache Umbenennung oder mvZeile sein, aber ich kann mich nicht darum kümmern.
Vielen Dank für Ihre Erkenntnisse

PS Ich habe die Verwendung von Perl-Funktionen in einigen Beispielen gesehen, die genau dem entsprechen, wonach ich suche, habe aber keine Ahnung, wo / wie diese verwendet werden sollen.

BradH
quelle
3
Sind Sie sicher , dass Sie einen Hash aus der Datei haben wollen Namen und nicht den Inhalt der Datei?
Anthon
12
Hinweis: Der MD5-Hash ist kein Verschlüsselungsgerät. MD5 ist nicht einmal ein kryptografischer Hash. Ein Hash, ein beliebiger Hash, ist eine Einwegtransformation eines Datensatzes in eine Zahl. Es ist nicht umkehrbar. Eine echte Verschlüsselung ist immer reversibel (angesichts des für die Verschlüsselung verwendeten Schlüssels).
Kusalananda
1
fba8255e8e9ce687522455f3e1561e53Ist der MD5-Hash für mynicepicture, bedeutet das, dass die Erweiterung vor dem Hashing entfernt werden sollte?
Kusalananda
@dessert Ich meine, dass es keinen Berechtigten gibt, wenn Sie md5sum <<<"file name"die file nameDatei vorhanden machen oder nicht, weil sie als Zeichenfolge betrachtet wird, außer sie mit dem Namen der vorhandenen Datei zu füttern.
αғsнιη

Antworten:

14

Sie haben nicht gesagt, welche Shell Sie verwenden möchten, also gehe ich nur von Bash aus - die Antwort muss angepasst werden, um mit anderen Shells zu arbeiten.

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

Skriptversion:

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

Diese einfache forSchleife nimmt jede Datei im aktuellen Verzeichnis, berechnet die md5-Summe ihres Namens und gibt sie aus. Verwenden Sie diese Option, um die Funktionalität zu überprüfen. Wenn Sie mit dem Umbenennen beginnen möchten, ersetzen Sie die zweite echodurch mv.

Erklärungen

  • echo -n "$i" | md5sum- Berechnen Sie die md5-Summe des vollständigen Dateinamens einschließlich der Dateierweiterung ( Piping ), um die Erweiterungsänderung echo -n "$i"auf eine der folgenden zu reduzieren:

    ${i%%.*}
    sed 's/\..*//' <<< "$i"
    echo "$i" | sed 's/\..*//'
    
  • sum=$(…)- Ausführung ausführen und speichern in $sum( Befehlsersetzung )

  • ${sum%% *}- alles bis zum ersten Leerzeichen ausgeben ( Parametersubstitution ), das gleiche wie eines der folgenden:

    $(sed 's/ .*//' <<< "$sum")
    $(echo "$sum" | sed 's/ .*//')
    
  • ${i##*.} - alles nach dem letzten Punkt ausgeben (Parametersubstitution), wie einer der folgenden:

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

Wenn Sie Dateien müssen in verschiedenen Ordnern rekursiv umbenennen, verwenden Sie finddie -execOption.

Dessert
quelle
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

Dieses bashSkript verwendet das md5sumDienstprogramm von GNU coreutils, um den MD5-Hash aus dem Basisnamen (ohne Erweiterung) eines bestimmten Pfadnamens zu berechnen. Die Hilfsfunktion md5nameführt die eigentliche Berechnung durch und gibt den neuen Namen mit vollständigem Pfad und Erweiterung aus.

Die md5nameFunktion verwendet awk, um den neuen Namen aus den Teilen des angegebenen Pfadnamens und dem Ergebnis von zusammenzusetzen md5sum.

Beispiele für die selbst verwendete Funktion:

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

... wo c9e89fa443d16da4b96ea858881320c9ist der MD5 - Hash der Zeichenfolge file name here.

Entfernen Sie das echoaus dem Skript oben, um die Dateien tatsächlich umzubenennen. Möglicherweise möchten Sie die Ausgabe des ursprünglichen Skripts in einer Datei speichern (mit der vorhandenen echo), wenn Sie irgendwann die Dateinamen auf ihren Originalen wiederherstellen müssen.

Beachten Sie, dass durch zweimaliges Ausführen einer Reihe von Dateien der MD5-Hash von MD5-Hashes berechnet wird und der ursprüngliche Dateiname dann nicht mehr wiederhergestellt werden kann, es sei denn, Sie machen nach jeder Ausführung des Skripts sorgfältig Notizen darüber, welche Dateien wie heißen.

Kusalananda
quelle
Genau wie zu Ihrer awkInformation könnte der Teil durch ersetzt werden. while read sum dummy ; do printf "%s/%s.%s\n' $dir $sum $ext ; done ;Sie benötigen das dummy, um das '-' zu erfassen.
Robert Benson
@RobertBenson Das Problem dabei ist, dass Dateinamen, die Leerzeichen enthalten, durcheinander gebracht würden.
Kusalananda
Guter Anruf. Dateinamen mit Leerzeichen sind böse. Ich amüsiere awkmich und es hat eine Weile bashgedauert, bis ich Dienstprogramme verwendet habe, anstatt system()inawk
Robert Benson
5

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

(entfernen, -nwenn glücklich).

Stéphane Chazelas
quelle
Tolle! Dies berechnet die md5-Summe des Dateinamens ohne die Erweiterung. Wie wäre es nun mit dem vollständigen Dateinamen? OP hat nicht gesagt, ob er es mit oder ohne braucht.
Nachtisch
1
Er hat es nicht gesagt, aber das Beispiel, das er gibt, ist genau das.
Robert Benson
2

Für einen AWKAnsatz:

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

Moderne findBefehle erfordern kein Verzeichnis für die Eingabe ., daher kann das [Verzeichnis] ​​leer bleiben. Das -type feinzige findet Dateien, was praktisch ist, da md5sumes keine Verzeichnisse mag und das Ändern des Verzeichnisnamens während der Ausführung keine gute Idee wäre. Verwenden Sie, -iname patternwenn Sie nur einige Dateien verwenden möchten, zum Beispiel -iname \*.dat, wenn Fall wichtig ist, verwenden Sie -namestatt -iname.

Die match(...); sub(...)Teile extrahieren Teile des Dateinamens und ersetzen sie in der Eingabezeichenfolge. Beachten Sie, dass "^"und "$"[pre / ap] anhängig sind, um zu verhindern, dass eine Zeichenfolge ersetzt wird, die den Pfad / die Erweiterung wiederholen kann.

Ersetzen print(com)durch system(com), um die Umbenennung tatsächlich durchzuführen.

Wenn Sie md5sumdie tatsächliche Datei als Namen verwenden möchten, können Sie die Tatsache, dass md5sumdie Summe und der Eingabedateiname ausgegeben werden, verwenden, um Folgendes zu tun:

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

Das while read sum filedauert zwei Argumente, die Ergebnisse des md5sumBefehls und assign sumund fileVariablen mit ihnen. Da das sumkeine Leerzeichen enthalten readsollte , sollte das gut funktionieren.

Natürlich [echo]sollte das entfernt werden, wenn es tatsächlich ausgeführt wird, aber es ist immer eine gute Idee, wenn Sie Skriptänderungen testen, um die Suche vor dem Ausführen zu testen.

Dies alles setzt voraus, dass Sie laufen bash. Dies kann auch als eine längere Zeile eingegeben werden:

find -iname \*.jpg -exec md5sum '{}' ';' | while read sum file ; do mv "$file" "`dirname $file`/$sum".jpg ; done
Robert Benson
quelle
1
Es sieht so aus, als würde dies den Inhalt der Dateien hashen. Das OP wollte den Namen hashen (ohne Erweiterung).
Kusalananda
Ich denke, es würde helfen, wenn ich die Frage vollständig lese.
Robert Benson
2

Diesen Ansatz verwende ich oft.

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

Der Befehl "ls" erzeugt einen Strom von Textzeilen. Der Befehl "sed" transformiert jede Zeile mit Mustervergleichsregeln. Der Befehl "sed" gibt einen Befehl "mv" aus, der dann zur Ausführung durch eine Shell "sh" geleitet wird. Die Parameter des Befehls "mv" entsprechen "mv oldfilename newfilename", wodurch die Datei umbenannt wird. Ich konstruiere den neuen Dateinamen mit einem sed-Befehl, der den Teil vor dem letzten Punkt übernimmt, ihn in die Eingabe des Befehls "md5sum" überträgt und dann nur den Hash aus seiner Ausgabe entnimmt.

Gehen Sie durch meinen Prozess und listen Sie zuerst die Dateien auf ('head -n 3', um nur die ersten 3 Zeilen zu sehen):

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

Denken Sie dann an die Transformation mit sed (noch keine generierten Befehle durch eine Shell leiten)

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

Es gibt drei Übereinstimmungsmuster:

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

Ich möchte sed verwenden, um einen Eingabedateinamen durch "mv Dateiname NEWfilename" zu ersetzen, aber da ich Befehle durch eine Shell leite, kann ich Befehle generieren, die die md5sum erhalten, wie folgt

echo "1000-26092016" | md5sum
    55b18a6b0add4a318b0079e18512b4e8  -

um nur den Hash zu bekommen

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

In einer Unix-Shell können wir Backtick-Operatoren (`some_command`) verwenden, um beispielsweise einen Unterbefehl auszuführen

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

Zurück zum Befehl mv möchte ich, dass sed "mv here there" erzeugt, wobei "there" durch einen Backtick-Befehl ersetzt wird, um die md5sum zu erhalten. Die Saite innerhalb der sed replace-Saite beginnt so

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

Es wird jedoch eindeutig für jeden Dateinamen der gleiche Hash erstellt, da der Befehl backticked ausgeführt wird, bevor sed die Zeichenfolge sieht. Um zu verhindern, dass die Shell den Befehl backtick ausführt, damit sed die Backticks ausgibt, müssen wir Schrägstriche (auch dem Pipe-Zeichen) voranstellen, also noch einmal:

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

Für die Ausgabe müssen auch Dateinamen bei Leerzeichen angegeben werden

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"

Probieren wir es also aus, indem wir es durch eine Shell leiten:

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

Hat es funktioniert ? ich vermute:

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

Hier ist ein Ansatz zur Gegenprüfung. Verwenden Sie die Option "-s" "-i", um den i-Knoten des Unix-Dateisystems auszugeben (der sich mit "mv" nicht ändert):

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"

Oder verwenden Sie den Befehl "Einfügen" (Paket '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
quelle
0

Ich mag diese einzeilige Antwort, aber sie bricht ab, weil sie den Dateinamen analysiert. Ich habe es auch ein bisschen mit Sha-Hashes aufgepeppt.

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

Ich denke, es zieht auch die Dateien heraus und platziert sie an der Basis, an der der Befehl eingegeben wurde.

Vielen Dank.

GoofProg
quelle
1
Wir sollten wahrscheinlich auf die Antwort zurückgreifen, aus der Sie Ihre Antwort abgeleitet haben .
Jeff Schaller