Dateisätze mit mehreren (auch zusammengesetzten) Erweiterungen in der Shell umbenennen

Dateisätze mit mehreren (auch zusammengesetzten) Erweiterungen in der Shell umbenennen

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 8um 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:

  1. wie ziele ich vom Dateianfang bis .foo?
  2. 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:

  1. Zerlegen Sie jeden Dateinamen in einen Basisteil und Erweiterungen.
  2. Wenden Sie eine konsistente Transformation auf den Basisteil jedes Namens an.
  3. 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:foobar1-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, pswährend opensslausgefü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 zmvfür den deterministischen Fall, wobei der erste verwendet wird, .foo.um das Ende der Basis zu markieren. Die -wFlagge 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 mappinginnerhalb 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 baseals 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 -nOption (Trockenlauf) zu hinzu zmv.

zmvist 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 cmdif was previously unset, wodurch sichergestellt wird, dass Sie immer den gleichen Wert eines gegebenen erhaltenSchlüssel.

verwandte Informationen