Ich habe eine Liste von Dateisätzen mit vielen Erweiterungen, aber eindeutigen Namen.
filename-1.foo.001
...
filename-1.foo.020
filename-1.foo.baz
filename-1.foo.bar1-2.baz
...
filename-1.foo.bar7-8.baz
another_filename.foo.001
another_filename.foo.002
...
another_filename.foo.009
another_filename.foo.baz
another_filename.foo.bar1-2.baz
another_filename.foo.bar3-4.baz
another_filename.foo.bar4-5.baz
another_filename.foo.bar7-8.baz
yet.a.different.file.name.foo.001
yet.a.different.file.name.foo.002
...
yet.a.different.file.name.foo.287
yet.a.different.file.name.foo.baz
yet.a.different.file.name.foo.bar1-2.baz
yet.a.different.file.name.foo.bar3-4.baz
yet.a.different.file.name.foo.bar4-5.baz
yet.a.different.file.name.foo.bar7-8.baz
moreFileNaming.foo.001
...
moreFileNaming.foo.009
moreFileNaming.foo.baz
moreFileNaming.foo.bar1-2.baz
moreFileNaming.foo.bar3-4.baz
moreFileNaming.foo.bar4-5.baz
moreFileNaming.foo.bar7-8.baz
Ich möchte diese mithilfe der Ausgabe umbenennen, openssl rand -hex 8
um für jeden Satz einen zufälligen Dateinamen zu erhalten, etwa wie folgt:
9874f7187c914ea9.foo.001
...
9874f7187c914ea9.foo.020
9874f7187c914ea9.foo.baz
9874f7187c914ea9.foo.bar1-2.baz
...
9874f7187c914ea9.foo.bar7-8.baz
2f54a0b6528e3927.foo.001
2f54a0b6528e3927.foo.002
...
2f54a0b6528e3927.foo.009
2f54a0b6528e3927.foo.baz
2f54a0b6528e3927.foo.bar1-2.baz
2f54a0b6528e3927.foo.bar3-4.baz
2f54a0b6528e3927.foo.bar4-5.baz
2f54a0b6528e3927.foo.bar7-8.baz
71ad0aa90148b2f5.foo.001
71ad0aa90148b2f5.foo.002
...
71ad0aa90148b2f5.foo.287
71ad0aa90148b2f5.foo.baz
71ad0aa90148b2f5.foo.bar1-2.baz
71ad0aa90148b2f5.foo.bar3-4.baz
71ad0aa90148b2f5.foo.bar4-5.baz
71ad0aa90148b2f5.foo.bar7-8.baz
3721323156e921b5.foo.001
...
3721323156e921b5.foo.009
3721323156e921b5.foo.baz
3721323156e921b5.foo.bar1-2.baz
3721323156e921b5.foo.bar3-4.baz
3721323156e921b5.foo.bar4-5.baz
3721323156e921b5.foo.bar7-8.baz
Ich habe es versucht for name (*.(<->|baz|bar<->.baz) result=$(openssl rand -hex 16) && mv $name $result
(das funktioniert möglicherweise nicht, da es mehrere Iterationen her ist), aber als es funktionierte, gab esjede einzelne Dateiein zufälliger Name, ich möchte nur, dass jedes Set den gleichen Namen behält, nur zufällig und mit der gleichen Größe. Sha1sum oder jede andere Methode wäre auch in Ordnung.
Wie erreiche ich das? Insbesondere für die Dateien.foo.bar-*.baz ?
Wenn wir foo fallen lassen
3721323156e921b5.001
...
3721323156e921b5.009
3721323156e921b5.baz
3721323156e921b5.bar1-2.baz
3721323156e921b5.bar3-4.baz
3721323156e921b5.bar4-5.baz
3721323156e921b5.bar7-8.baz
Wäre auch ok. Noch ein paar Fragen:
- wie ziele ich vom Dateianfang bis .foo?
- wie führe ich eine Schleife mit der erstellten Variable aus,
result=$(openssl rand -hex 8)
um sie beispielsweise bei Umbenennungen zu verwenden, sie erst dann erneut zuzuweisen, wenn ein Satz fertig ist, und sie dann zu durchlaufen, bis der nächste Satz fertig ist usw.?
Danke!
Antwort1
Dieses Problem besteht aus mehreren Teilen:
- Zerlegen Sie jeden Dateinamen in einen Basisteil und Erweiterungen.
- Wenden Sie eine konsistente Transformation auf den Basisteil jedes Namens an.
- Benennen Sie die Dateien entsprechend der gewählten Transformation des Basisteils um und behalten Sie dabei die Erweiterungen bei.
1. Dateinamen zerlegen
Aus Ihren Beispielnamen geht nicht ganz klar hervor, was Sie als Basisteil eines Dateinamens betrachten. Das Trennzeichen ist offensichtlich ein Punkt, aber in einem Beispiel wie yet.a.different.file.name.foo.bar1-2.baz
, welcher Punkt? Sie erwähnen einen Versuch mit , bei dem oder *.(<->|baz|bar<->.baz)
nicht als Erweiterung behandelt würde . Eine Optimierung, die sie als Erweiterung zulässt, ist . Dann können Sie einen Dateinamen wie folgt aufteilen:foo
bar1-2
.(foo|<->|baz|bar<->(|-<->).baz)
$f
setopt extended_glob
base=${f%%(.(<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
Alternativ ist die Zerlegung einfacher, wenn es akzeptabel ist, die Basis als alles bis zum ersten (ausschließlich) zu definieren .foo.
:
base=${f%*.foo.*}; extensions=${f#$base}
2. Eine konsistente Transformation anwenden
Wenn Sie eine deterministische Transformation anwenden möchten, können Sie einfach jedes Mal neu berechnen. Sie können beispielsweise ein pseudozufälliges Ergebnis erhalten, indem Sie einen MAC des Namens mit einem geheimen Schlüssel nehmen und jedes Mal denselben geheimen Schlüssel verwenden.
secret=$(openssl rand -hex 32)
for … # Loop over the files as per (3.), set $base and $extensions as per (1.)
new_base=${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}
(Hinweis: Der geheime Schlüssel ist für andere Benutzer sichtbar, wenn er ausgeführt wird, ps
während openssl
ausgeführt wird. Ich gehe davon aus, dass dies in Ihrem Fall kein Problem darstellt, aber zukünftige Leser sollten sich dessen bewusst sein.)
Wenn Sie eine zufällige Transformation anwenden möchten, müssen Sie sich merken, worauf jede Basis abgebildet wird. Dazu gibt es zwei Möglichkeiten:
- Sie können Dateien nach ihrer Basis gruppieren und dann jeweils eine Basis verarbeiten.
- Sie können die Dateien einzeln verarbeiten, aber merken Sie sich, worauf jede Basis abgebildet wird, und generieren Sie nur dann eine neue Abbildung, wenn die Basis noch nicht gesehen wurde.
Die zweite Methode ist einfacher und die erste Methode hat keinen besonderen Vorteil, deshalb zeige ich nur die zweite Methode.
Bauen Sie einassoziatives ArrayZuordnung von Basen zu neuen Basen.
typeset -A mapping
mapping=()
for … # Loop over the files as per (3.), set $base and $extensions as per (1.)
if ((!$+mapping[$base])); then
mapping[$base]=$(openssl rand -hex 8)
fi
new_base=$mapping[$base]
3. Umbenennen der Dateien
Zsh bringt ein sehr nützliches Tool zum Umbenennen von Dateien mit:zmv
. Die Transformation, die Sie durchführen möchten, ist so komplex, dass zmv sie nicht trivial macht: Sowohl die Dateinamenzerlegung als auch die Transformation erfordern zusätzliche Arbeit. Selbst in Ihrem Fall bietet zmv einige kleinere Vorteile. Insbesondere wird zmv einen Fehler ausgeben, wenn es zu einem Konflikt kommt (was aufgrund zufälliger Faktoren äußerst unwahrscheinlich ist, es sei denn, Sie verwenden kürzere Längen). Aufgrund der Schwierigkeit bei der Namenstransformation ist die Verwendung von zmv jedoch umständlich und eine einfache Schleife ist leichter zu schreiben.
Hier ist ein vollständiger Ausschnitt mit zufälligen Namen.
setopt extended_glob
typeset -A mapping
mapping=()
for f in *.(foo|<->|baz|bar<->(|-<->).baz); do
base=${f%%(.(foo|<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
if ((!$+mapping[$base])); then
mapping[$base]=$(openssl rand -hex 8)
fi
new_base=$mapping[$base]
mv -i -- $f $new_base$extensions
done
Hier ist ein vollständiger Ausschnitt mit deterministischen Namen für einen gegebenen Wert von $secret
.
setopt extended_glob
secret=$(openssl rand -hex 32)
for f in *.(foo|<->|baz|bar<->(|-<->).baz); do
base=${f%%(.(foo|<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
new_base=${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}
mv -i -- $f $new_base$extensions
done
Und hier ist ein Einzeiler zmv
für den deterministischen Fall, wobei der erste verwendet wird, .foo.
um das Ende der Basis zu markieren. Die -w
Flagge hilft bei der Aufschlüsselung.
autoload zmv
secret=$(openssl rand -hex 32)
zmv -w '*.foo.*' '${"$(openssl dgst -sha256 -hmac $secret <<<$1)"[-16,-1]}.foo.$2'
Die Verwendung von zmv im randomisierten Fall ist schwieriger, da wir Informationen von einem Transformationsschritt zum nächsten beibehalten müssen. Wir können einfach etwas Code in eine Befehlssubstitution packen, zmv … '$(…; if …; then mapping[$base]=…; …)'
da die Zuweisung mapping
innerhalb der Befehlssubstitutions-Subshell erfolgen würde und daher nur innerhalb der Subshell Auswirkungen hätte. Wir können jedoch eine bedingte Parameterzuweisung verwenden${name=word}
, nur festlegen, mapping[$base]
wenn es nicht festgelegt ist.
typeset -A mapping; mapping=()
zmv -w '*.foo.*' '${mapping[${1}]=$(openssl rand -hex 16)}.foo.$2'
Die Verwendung von zmv mit einer Zerlegung, die nicht die Vorteile von nutzt .foo
, wie das komplexere Beispiel in (1.) oben, führt zu viel komplexerem Code. Nur als Beispiel hier ein zmv-Aufruf für den deterministischen Fall, bei dem base
als temporäre Variable verwendet wird, um den Basisnamen zu speichern. Es verwendet, ${name::=word}
um während der Parametererweiterung einer Variablen zuzuweisen und ${…}[0]
diesen Teil aus der Erweiterung zu unterdrücken ( [0]
nimmt den Teilstring vom 0. Zeichen, der nicht existiert, da zsh beginnt, Array-Elemente und String-Zeichen bei 1 zu nummerieren; etwas wie [2,1]
würde auch funktionieren).
zmv '*.(<->|baz|bar<->.baz)' '${${base::=${f%%(.(<->|baz|bar<->(|-<->).baz))#}}[0]}${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}.${f#$base}'
Antwort2
Sie könnten beispielsweise Folgendes tun:
autoload -Uz zmv # best in ~/.zshrc
typeset -A rand
zmv '(*).foo(.*)' '${rand[$1]=$(openssl rand -hex 8)}$2'
oder '(*)(.foo.*)'
nicht fallen lassen .foo
.
Um zunächst einen Test durchzuführen, fügen Sie die -n
Option (Trockenlauf) zu hinzu zmv
.
zmv
ist ein Tool zur Stapelumbenennung, das als automatisch ladbare Funktion implementiert ist.
Das erste Argument ist ein erweitertes Glob-Muster und das zweite Argument eine Zeichenfolge, die Worterweiterungen erfährt, die bestimmen, wie die Dateien mit $1
, $2
… entfernt werden, wobei auf die entsprechenden (...)
s im Muster verwiesen wird.
${rand[$1]=$(cmd)}
oben setzt das Mitglied des assoziativen Arrays für dieSchlüsselist das, was von ganz rechts übrig bleibt, .foo.
bis zur Ausgabe von cmd
if was previously unset, wodurch sichergestellt wird, dass Sie immer den gleichen Wert eines gegebenen erhaltenSchlüssel.