ubuntuusers.de

Rekursives Umbenennen von Dateien

Status: Gelöst | Ubuntu-Version: Ubuntu 18.04 (Bionic Beaver)
Antworten |

leineart

Anmeldungsdatum:
19. April 2021

Beiträge: Zähle...

Hallo liebe Linux - Gurus,

ich habe eine große Sammlung mit mitgeschnittenen Dokumentationen im MKV - Format. Diese befinden sich in einigen Unterordnern. Nun möchte ich diese Videodateien z.B. "Terra.X.Name.mkv" in "Terra_X.Name.mkv" umbenennen. Hat jemand eine Idee, wie man das auf der Kommandozeile erledigen kann?

Beste Grüße M. Weber

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1247

Hallo leineart,

deine Frage lässt vermuten, dass du nicht so häufig derartige Aufgabe mit der bash shell erledigst. Daher würde ich dir empfehlen, zuerst eine Parallelstruktur der Dateinamen anzulegen, bevor du den Befehl auf die originalen Dateien loslässt.

Ich nehme mal an, dass deine Videos unter dem Pfad "src=Videos" strukturiert abgelegt sind. Dann erzeugst du die Parallelstruktur unter Ordner "tst" mit:

$ src="Videos" # wenn dein Verzeichnis anders lautet, ist der Pfad hier einzutragen (ohne / am Ende)
$ find $src/ -type d -exec mkdir -p tst/{} \; # dies legt die Ordnerstruktur an
$ find $src/ -type f | while read f; do touch "tst/$f"; done # erzeugt die Dateien der Länge 0 in der Parallelstruktur

Hierbei werden alle Dateien unter $src auch als Namen unter "tst" erzeugt. Allerdings haben alle Dateien die Länge 0 (um Platz und Zeit zu sparen). Wenn deine Dateien stets von "Terra.X.Name.mkv" in "Terra_X.Name.mkv" umbenannte werden sollen (d.h.der erste "." im Dateiname ein "_" werden soll), kannst du im tst Verzeichnis folgenden Befehl verwenden:

$ find tst/$src/ -name "*.mkv" | while read f; do d=$(dirname "$f"); n=${f#$d}; mv "$f" "$d${n/./_}"; done

Hierauf kannst du die Umbenennungen der Dateien in der Parallelstruktur prüfen. Wenn alles ok ist, kannst du die Parallelstruktur löschen und den Befehl auf die Original-Dateien loslassen:

$ rm -r tst  # dies löscht die Parallelstruktur und die testweise umbenannten Dateien der Länge 0
$ find $src/ -name "*.mkv" | while read f; do d=$(dirname "$f"); n=${f#$d}; mv "$f" "$d${n/./_}"; done

Viel Erfolg.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13174

Willkommen hier im Forum!

Oft wird hier eine Kombination aus find und rename vorgeschlagen. Man kann das auch ohne rename machen:

Ich nehme mal an, er erste Punkt soll durch einen Unterstrich ersetzt werden.

1
find -type f -name '*.*.mkv' -exec bash -c 'for f; do b="${f##*/}"; mv "$f" "${f%/*}/${b/./_}"; done' -- {} +

Anmerkung: das funktioniert so, weil find immer Dateipfade ausspuckt, die mindestens einen "/" enthalten. Falls das nicht der Fall wäre, bräuchte man für die Extraktion des Verzeichnisnamens dirname.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13174

shiro schrieb:

deine Frage lässt vermuten, dass du nicht so häufig derartige Aufgabe mit der bash shell erledigst. Daher würde ich dir empfehlen, zuerst eine Parallelstruktur der Dateinamen anzulegen, bevor du den Befehl auf die originalen Dateien loslässt.

Man kann dem mv auch einfach ein echo voranstellen.

Wenn deine Dateien stets von "Terra.X.Name.mkv" in "Terra_X.Name.mkv" umbenannte werden sollen (d.h.der erste "." im Dateiname ein "_" werden soll), kannst du im tst Verzeichnis folgenden Befehl verwenden:

$ find tst/$src/ -name "*.mkv" | while read f; do d=$(dirname "$f"); n=${f#$d}; mv "$f" "$d${n/./_}"; done

Die gefundenen Namen der Shell als Argumente zu übergeben ist robuster als die Leseschleife mit read. Mindestens müsstest Du read -r verwenden, aber selbst dann gibt es das Problem mit Newlines im Dateinamen. Das wiederum kann man in den Griff bekommen, indem man "-print0" verwendet und mit read -d einen Trenner angibt:

1
find tst/$src/ -name "*.mkv" -print0 | while read -r -d '' f; do d=$(dirname "$f"); n=${f#$d}; mv "$f" "$d${n/./_}"; done

Aber das finde ich ganz schön umständlich.

leineart

(Themenstarter)

Anmeldungsdatum:
19. April 2021

Beiträge: 2

Es hat funktioniert! 😉 Vielen, Vielen Dank! Ihr seid die Größten! 👍 Beste Grüße. 😊

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17604

Wohnort: Berlin

Schön, wenn es geklappt hat.

Basierend auf rklms Lösung würde ich folgendes machen:

1
find -name "Terra.X.*.mkv" -execdir bash -c 'f=$1; echo mv $f ${f/Terra.X/Terra_X}' -- {} ";"

Unterschiede: Die Suche spezifisch schon auf Terra.X* beschränken und bei der Namensersetzung spricht auch nichts dagegen, genau Terra.X durch Terra_X zu ersetzen.

Das Filtern nach -type f finde ich dagegen grenzwertig - wer hat schon Verzeichnisse namens "*.mkv"?

Und die Konstruktion mit "for f ..." und "+" ist m.E. overengineered mit unnötiger Komplexität. Das Geschwindigkeit das erste Ziel ist, war nicht gesagt und fällt in die Kategorie premature optimization. Wie lange kann es dauern, 10.000 Dateien umzubenennen? Gibt es überhaupt so viele Folgen?

Konstruktionen mit find ... -(exec|ok)(dir)? sind solchen mit | (while|for|xargs) aber in der Regel vorzuziehen, weil man weniger Bohei machen muss, um gefährliche Dateinamen sauber zu behandeln.

Mit -okdir statt -execdir wirst Du für jede Datei gefragt, ob Du für sie die Aktion ausführen willst - ein Sicherheitsnetz von fragwürdiger Qualität, würde ich sagen:

1
2
3
4
5
6
7
touch "Terra.X."{"Name","Vulkane","Orkane","Corona","1.April"}".mkv" 
find -name "Terra.X.*.mkv" -okdir bash -c 'f=$1; mv $f ${f/Terra.X/Terra_X}' -- {} ";"
< bash ... ./Terra.X.Name.mkv > ? y
< bash ... ./Terra.X.Vulkane.mkv > ? y
< bash ... ./Terra.X.Orkane.mkv > ? y
< bash ... ./Terra.X.Corona.mkv > ? y
< bash ... ./Terra.X.1.April.mkv > ? y

Man muss für jede Datei "y" wie "yes" klicken und das ist dann die Sicherheit.

Bei 4, 5 Dateien machbar, aber bei 200 Dateien will man nicht jede manuell prüfen und ist ab Nr. 5 dann eh im y-y-y-y-Modus und hat schneller abgenickt, als kontrolliert.

Dagegen ist mv -i hilfreich. Es verhindert, dass eine Datei überschrieben wird.

1
find -name "Terra.X.*.mkv" -execdir bash -c 'f=$1; mv -i $f ${f/Terra.X/Terra_X}' -- {} ";"

Shiros Idee, eine Parallelstruktur zum Testen zu machen, fand ich aber auch gut. ☺

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13174

user_unknown schrieb:

Unterschiede: Die Suche spezifisch schon auf Terra.X* beschränken und bei der Namensersetzung spricht auch nichts dagegen, genau Terra.X durch Terra_X zu ersetzen.

Wir wissen gar nicht, ob nur solche Dateinamen gemeint sind, oder auch welche mit mehreren Punkten aber ohne "Terra" vorkommen.

Du verwendest die überflüssige Variable f. Das geht auch direkt mit $1.

Und die Konstruktion mit "for f ..." und "+" ist m.E. overengineered mit unnötiger Komplexität. Das Geschwindigkeit das erste Ziel ist, war nicht gesagt und fällt in die Kategorie premature optimization. Wie lange kann es dauern, 10.000 Dateien umzubenennen? Gibt es überhaupt so viele Folgen?

Dafür läuft Deine Ersetzung Gefahr "Terra.X" irgendwo im Pfad in "Terra_X" zu ändern. Und Du nutzt keine doppelten Anführungsstriche um den Dateinamen. Jaja, ich weiß, das ist Cargo-Kult für Dich, aber der Ärger und die Fehlersuche, die man sich dadurch sparen kann, sind es mir allemal wert. Zumal das gar kein zusätzlicher Aufwand für mich ist.

Konstruktionen mit find ... -(exec|ok)(dir)? sind solchen mit | (while|for|xargs) aber in der Regel vorzuziehen, weil man weniger Bohei machen muss, um gefährliche Dateinamen sauber zu behandeln.

xargs ist genau so sicher wie -exec, wenn man find ... -print0 und xargs -r0 nutzt. Wie Du hinter der Pipe for nutzen willst, ist mir allerdings nicht klar. while read ist wohl die andere Option und da stimme ich Dir zu; hatte ich ja bereits geschrieben.

Dagegen ist mv -i hilfreich. Es verhindert, dass eine Datei überschrieben wird.

Da nehme ich lieber "-n". Ansonsten hast Du das gleiche Problem mit den vielen Bestätigungen, das Du weiter oben anmerkst.

Shiros Idee, eine Parallelstruktur zum Testen zu machen, fand ich aber auch gut. ☺

Wozu? Du brauchst doch vor dem mv nur ein echo zu platzieren und die Ausgabe für die Analyse nach less pipen. Da siehst Du dann doch genau, was nach was umbenannt wird.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17604

Wohnort: Berlin

rklm schrieb:

user_unknown schrieb:

Unterschiede: Die Suche spezifisch schon auf Terra.X* beschränken und bei der Namensersetzung spricht auch nichts dagegen, genau Terra.X durch Terra_X zu ersetzen.

Wir wissen gar nicht, ob nur solche Dateinamen gemeint sind, oder auch welche mit mehreren Punkten aber ohne "Terra" vorkommen.

Du hast die Frage sorgfältiger gelesen als ich und Recht: Terra.X ist als Beispiel, nicht als einziges Muster genannt worden. Mein Fehler.

Du verwendest die überflüssige Variable f. Das geht auch direkt mit $1.

1
mv $1 ${1/Terra.X/Terra_X}' -- {} ";"

Ja. An ${1/muster/replace} kann ich mich schlecht gewöhnen. Ist nat. kein überzeugendes Argument.

Und die Konstruktion mit "for f ..." und "+" ist m.E. overengineered mit unnötiger Komplexität. Das Geschwindigkeit das erste Ziel ist, war nicht gesagt und fällt in die Kategorie premature optimization. Wie lange kann es dauern, 10.000 Dateien umzubenennen? Gibt es überhaupt so viele Folgen?

Dafür läuft Deine Ersetzung Gefahr "Terra.X" irgendwo im Pfad in "Terra_X" zu ändern.

Nein. find -name MUSTER findet MUSTER nicht im Pfad, und -execdir überreicht dem Kommando keinen Pfad, sondern nur den Dateinamen (bzw. "./NAME"). Der Punkt geht an mich. ☺

Und Du nutzt keine doppelten Anführungsstriche um den Dateinamen. Jaja, ich weiß, das ist Cargo-Kult für Dich, aber der Ärger und die Fehlersuche, die man sich dadurch sparen kann, sind es mir allemal wert. Zumal das gar kein zusätzlicher Aufwand für mich ist.

Wenn es Cargokult ist, dann braucht man keine, aber hier hast Du recht - man braucht welche:

1
find -name "Terra.X.*.mkv" -execdir bash -c 'f=$1; mv -i "$f" "${f/Terra.X/Terra_X}"' -- {} ";"

Solche Medienproduktionen haben ja häufig Leerzeichen im Titel, und Downloadtools generieren gerne den Namen automatisch aus dem Titel - hier sind Leerstellen im Dateinamen fast vorprogrammiert, daher ist es nötig, Sorgfalt walten zu lassen.

Automatische Konversion der Leerstellen wäre natürlich noch besser. ☺

Konstruktionen mit find ... -(exec|ok)(dir)? sind solchen mit | (while|for|xargs) aber in der Regel vorzuziehen, weil man weniger Bohei machen muss, um gefährliche Dateinamen sauber zu behandeln.

xargs ist genau so sicher wie -exec, wenn man find ... -print0 und xargs -r0 nutzt.

Ich sagte "vorzuziehen", nicht "sicherer", und die Begründung war "Bohei", und "-print0" und "-r0" ist eben Bohei. ☺

Wie Du hinter der Pipe for nutzen willst, ist mir allerdings nicht klar. while read ist wohl die andere Option und da stimme ich Dir zu; hatte ich ja bereits geschrieben.

Will ich ja nicht, aber Du hast Recht, for kommt hinter der Pipe nicht in Frage.

1
for f in $(find -name "Terra.X.*.mkv"); do echo mv -i "$f" "${f/Terra.X/Terra_X}"; done ;

wäre eine Möglichkeit, die aber auch weiteren Boheis bedarf, um Leerzeichen zu behandeln.

Dagegen ist mv -i hilfreich. Es verhindert, dass eine Datei überschrieben wird.

Da nehme ich lieber "-n". Ansonsten hast Du das gleiche Problem mit den vielen Bestätigungen, das Du weiter oben anmerkst.

Nein, bei -okdir werde ich für jede Datei befragt, unabhängig davon, ob bereits eine gleichnamige da ist. Das mv -i fragt mich aber nur, wenn es einen möglichen Nameclash gibt und verschiebt/umbenennt sonst mucksmäuschenstill tausende Files.

Shiros Idee, eine Parallelstruktur zum Testen zu machen, fand ich aber auch gut. ☺

Wozu? Du brauchst doch vor dem mv nur ein echo zu platzieren und die Ausgabe für die Analyse nach less pipen. Da siehst Du dann doch genau, was nach was umbenannt wird.

Wenn jmd. zum ersten Mal find nutzt, und womöglich wenig Shellerfahrung hat, dann kann ja weit mehr schiefgehen. Da die Teststruktur selbst mit find erzeugt wird, beißt sich hier die Katze in den Schwanz, aber generell ist Testen zu begrüßen und das heißt nicht, das es nicht bessere Tests gibt.

Übrigens wissen viele User nicht, dass Thunar für's Massenumbenennen (>= 2 Dateien) einen Dialog anbietet, bei dem man auch reguläre Ausdrücke für die Umbenennung spezifizieren kann und auch eine Vorschau bietet, was wie umbenannt werden wird.

Nachteil: Steigt nicht rekursiv in Unterverzeichnisse ab - oh, dann hättest Du ja doch Recht, mit der Befürchtung, mein Befehl würde Verzeichnisnamen umbenennen - (Mist!); Vorteil: Man kann Dateien cherrypicken, die dem Umbenenennen unterzogen werden.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13174

user_unknown schrieb:

rklm schrieb:

Ja. An ${1/muster/replace} kann ich mich schlecht gewöhnen. Ist nat. kein überzeugendes Argument.

Ja, das ist in der Tat gewöhnungsbedürftig.

Dafür läuft Deine Ersetzung Gefahr "Terra.X" irgendwo im Pfad in "Terra_X" zu ändern.

Nein. find -name MUSTER findet MUSTER nicht im Pfad, und -execdir überreicht dem Kommando keinen Pfad, sondern nur den Dateinamen (bzw. "./NAME"). Der Punkt geht an mich. ☺

Stimmt! Ich benutze -execdir zu selten. Sollte ich mir vielleicht mal angewöhnen.

Automatische Konversion der Leerstellen wäre natürlich noch besser. ☺

Ich weiß gar nicht, was Du gegen Leerzeichen im Dateinamen hast. Liest sich doch oft viel besser. 😉

Konstruktionen mit find ... -(exec|ok)(dir)? sind solchen mit | (while|for|xargs) aber in der Regel vorzuziehen, weil man weniger Bohei machen muss, um gefährliche Dateinamen sauber zu behandeln.

xargs ist genau so sicher wie -exec, wenn man find ... -print0 und xargs -r0 nutzt.

Ich sagte "vorzuziehen", nicht "sicherer", und die Begründung war "Bohei", und "-print0" und "-r0" ist eben Bohei. ☺

Achso.

1
for f in $(find -name "Terra.X.*.mkv"); do echo mv -i "$f" "${f/Terra.X/Terra_X}"; done ;

wäre eine Möglichkeit, die aber auch weiteren Boheis bedarf, um Leerzeichen zu behandeln.

Abgesehen davon meine ich der Ansatz hat auch ein Problem, das -exec und -execdir nicht haben: wenn richtig viele Dateinamen gefunden werden, könnte das zu lang werden. Oder trifft das hier nicht zu, weil es keine Kommandozeile ist?

Dagegen ist mv -i hilfreich. Es verhindert, dass eine Datei überschrieben wird.

Da nehme ich lieber "-n". Ansonsten hast Du das gleiche Problem mit den vielen Bestätigungen, das Du weiter oben anmerkst.

Nein, bei -okdir werde ich für jede Datei befragt, unabhängig davon, ob bereits eine gleichnamige da ist. Das mv -i fragt mich aber nur, wenn es einen möglichen Nameclash gibt und verschiebt/umbenennt sonst mucksmäuschenstill tausende Files.

Ja, da hätte ich sorgfältiger lesen sollen.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17604

Wohnort: Berlin

rklm schrieb:

1
for f in $(find -name "Terra.X.*.mkv"); do echo mv -i "$f" "${f/Terra.X/Terra_X}"; done ;

wäre eine Möglichkeit, die aber auch weiteren Boheis bedarf, um Leerzeichen zu behandeln.

Abgesehen davon meine ich der Ansatz hat auch ein Problem, das -exec und -execdir nicht haben: wenn richtig viele Dateinamen gefunden werden, könnte das zu lang werden. Oder trifft das hier nicht zu, weil es keine Kommandozeile ist?

Kannst Du aus dem Kopf sagen, wie sich "richtig zuviele" in eine arabische Zahl übersetzt?

Ich hatte 2x in meinem Leben bisher Gelegenheit die Meldung "too many Arguments" zu studieren. Das eine mal war es im Zusammenhang mit locate und grep und einem sehr durchlässig gewählten Filter, das andere Mal ging es um eine globale Datenbank von Musik-CDs, ich meine > 100.000 Stück. Und dann bekommt man eine Fehlermeldung und das Kommando bricht ab. Daher ist das Risiko, dass der Fehler auftritt, gering und dass er dann unumkehrbare Seiteneffekte hat nochmal geringer.

Ich weiß gar nicht, was Du gegen Leerzeichen im Dateinamen hast. Liest sich doch oft viel besser. 😉

Wäre Mac-OS oder Windows nicht was für Dich? Oder ganz was anderes - Schafzucht? 😉

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13174

user_unknown schrieb:

rklm schrieb:

1
for f in $(find -name "Terra.X.*.mkv"); do echo mv -i "$f" "${f/Terra.X/Terra_X}"; done ;

wäre eine Möglichkeit, die aber auch weiteren Boheis bedarf, um Leerzeichen zu behandeln.

Abgesehen davon meine ich der Ansatz hat auch ein Problem, das -exec und -execdir nicht haben: wenn richtig viele Dateinamen gefunden werden, könnte das zu lang werden. Oder trifft das hier nicht zu, weil es keine Kommandozeile ist?

Kannst Du aus dem Kopf sagen, wie sich "richtig zuviele" in eine arabische Zahl übersetzt?

Leider nicht. Aber es ist so viel, dass ich das, wie Du, sehr selten beobachtet habe. Kann man aber relativ einfach austesten.

Ich weiß gar nicht, was Du gegen Leerzeichen im Dateinamen hast. Liest sich doch oft viel besser. 😉

Wäre Mac-OS oder Windows nicht was für Dich? Oder ganz was anderes - Schafzucht? 😉

Schafe haben wir hier 100m entfernt stehen. Da muss ich gar nicht selber züchten. 😛

Antworten |