PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : das ultimative rename script



Zico
27.07.2005, 23:36
hallo leute

ich weiss, ich hätte vorher googeln sollen, aber es is schon spät und vllt. hat ja einer schon was passendes. andernfalls hoffe ich, dass ich mit eurer hilfe meine scriptkenntnisse etwas verbessern kann. dazu bringts mir auch wenig, wenn ich nur bissl rumgoogel und nicht genau weiss, was ich dann mache.

aber erstmal zum punkt:

über die letzten 10 jahre hab ich meine komplette cd sammlung digitalisiert.
das problem was sich mir jetzt stellt ist, dass beinahe kein album nach der selben datei schreibweise formatiert ist wie das andere (nachlässigkeit, programm/systemwechsel etc.)

nun habe ich mir folgende aufgabe gestellt:

alle mp3s sollen - soweit möglich mit hilfe eines scriptes umbenannt werden.
dieses script soll folgende aufgaben erfüllen:

* entfernung sämtlicher ID3 (v1 und v2) tags
* komplette umstellung aller möglichen grossbuchstaben in kleinbuchstaben [Bla-Track1 --> bla-track1]
* ersetzen von leerzeichen (ggf auch unterstriche) zu punkte [test - test --> test.-.test]

das würde ich vllt. noch selbst hinbekommen, aber wär dennoch dankbar für jede erklärung, hilfe, tipps

jetzt kommt ein schwieriger punkt:
* ein spezieller dateicheck.

einige dateien haben folgendes format:
artist-track (sprich keine leerzeichen bei trennstrich)
andere so:
artist - track (leerzeichen bei trennstrich)
gibt es ne möglichkeit, das script so zu schreiben, dass es, egal welches der beiden formate vorhanden ist so formatiert:
artist.-.track (mit punkten zwischen trennstrich)?

ich weiss, ich bin wohl sehr faul, da ich direkt nachfrag ohne, vorher mal rumzusehen, ob ich auch alleine was finde, aber wenn ich mir von mehreren stellen was suche, weiss ich bei meiner koordinationsfähigkeit, dass später garnix draus wird und ich auch nix gelernt habe.

so plz :hilfe2: :)
vielen dank an euch

Cyber
28.07.2005, 14:33
Hossa, da hast aber einiges vor. Also die ID3-Tags sind am Ende eines mp3 Files, nämlich die letzten (AFAIR) 128 Bytes, wobei die tatsächlich letzten 3 Bytes fix sind. Ich hab mal ein script gebastelt dass die infos ausliest, kann ich Dir gerne geben. Im Prinzip reicht es diese Bytes zu nullen.

Grossbuchstaben in Kleinbuchstaben machst am besten mit nem

for i in `ls $TARGETDIR`;
do echo $i | mv $TARGETDIR/$i $TARGETDIR/`tr '[A-Z]' '[a-z]'`;
done
exit 0;

in einer schleife.

Ersetzen von Leerzeichen mit regexp, z.B.
echo "$files" | sed -e 's/\ /\./g'. Das ganze natürlich in obigen Konstrukt eingebaut.

Datei-check:
Sry, beim nachdenken bekomm ich nen Buffer-Overflow.

Wolfgang
28.07.2005, 14:38
Hallo
Zu den IDV2/IDV3 Tags kann ich im Moment nix beitragen, da ich kein Multimediamensch bin und mich um die Struktur noch nicht bemüht habe.

Ist sicher aber nicht so problematisch, eventuell entweder auch mit perl
( pack vs umpack und sysread) oder sogar mit dd machbar.

Zu den reanme habe ich aus vergangenem Anlass mal ein solch ähnliches Problem zu lösen gahabt. Da musste ich für einen WindoZe Nutzer eine komplette HD auslesen und duplizieren, da seine OS das Zeitliche gesegnet hatte und die HD langsam den Bach runterging.
Dabei bin ich auf Probleme mit diversen unter *nix nicht richtig darstellbare Sonderzeichen aus der M$Welt <- wohl irgend so eine verkorkste Codepage -> in den Dateinamen gestoßen.
Da habe ich mir dann ein schnelles kleines Script geschrieben, was die meisten Probleme beim Umkopieren behoben hat.

Ich werde es hier mal zur Verfügung stellen, das kannst du leicht an deine Bedürfnisse anpassen.
Ich bevorzuge perl, weil ich mich da besser auskenne, und es leicht portierbar ist.


#!/usr/bin/perl -w

# Entfernt nicht darstellbare Umlaute und Leerzeichen aus Dateibaum
# und benennt Dateien um.
# Aufruf Scriptname Startverzeichnis
# ohne Startverzeichnis wird das aktuelle genommen

use strict;
use File::Find;

if (@ARGV == 0) { push (@ARGV, "."); }
finddepth(\&modify, @ARGV);



sub modify {
# Verzeichnisse ignorieren
return unless -f $_;
# Returnvariable sichern und Uppercase to Lowercase
my $new=$_;
$new=~s|([A-Z])*|lc$1|eg;
#Leerzeichen raus kannst du hier aber anpassen
#z.B Leerzeichen durch Punkt dann hier auskommentieren
#$new=~s|\s+|.|ig;
$new=~s|\s+||ig;
#eventuelle haessliche WindoZe - Probleme mit Umlauten udgl. ersetzen
if ($new=~/[\204\224\201\216\231\232\341\202]/)
{
$new =~ tr/\204\224\201\216\231\232\341\202/äöüÄÖÜßé/;
}
if ( -f $new){
print "$new existiert bereits, wird nicht ersetzt\n";
}
else{
print "Verschiebe Datei alt ",$File::Find::dir, ": $_ nach neu -> $new\n";
rename $_, $new or die "cannot rename $_ to $new: $!";
}

return $_;
}


Ist ziemlich dirty , aber ist auch nur als Vorlage gedacht.
Keine weitere Fehlerabfrage oder solch Dinge, halt nur so schnell hingeschrieben.
Schreibrechte brauchst du natürlich dort, weil es sonst latürnich abbricht.
:D
Hoffe es nutzt ein wenig.

Gruß Wolfgang
Das geht auch mit einem Einzeiler, aber das wird dann recht cryptisch und unübersichtlich, was ich dir hier mal ersparen will. :D
Edit / habe noch eine Klammer umgesetzt, damit alle dateien und nicht nur die mit kaputten Umlauten umbenannt werden ( das war bei mir ausreichend, aber du willst ja alle ;-)

Zico
28.07.2005, 14:56
Das sind ja schonmal super tipps von euch. ich danke euch dafür.

@cyber
Das klingt interessant. mit ls kann ich so wohl auch rekursiv umbenennen. das passt immer :)
Für den Bindestrich müsste es ja fast nur sein, dass er ALLE "-" zu ".-." umbenennt.... ok das ganze dürfte dann aber auch nur einmal durchlaufen... sonst krieg ich nen buffer overflow :D

@Wolfgang_1
Danke auch für dein Script. Das werd ich mir auch mal ansehen und sehen ob ich verstehe wie das alles funktioniert.

ID tags kann ich soweit ich weiss auch mit nem Prog ändern. Ich weiss nur nicht, wie ich ne Schleife baue, dass es alle dateien einmal als option nimmt.
Beispiel:
idtag_programm -switches $all_files
Ansonsten isses nciht so schlimm... viele ID Tags sind in ordnung... die müsste ich dann halt per hand ändern... mit amaroK is das reativ gut zu machen, vor allem weil es aus dem Dateinamen ID Tags selbst bauen kann.
Dennoch sind weitere Tipps willkommen.
Lerneffekt wird bei mir groß geschrieben :)

Wolfgang
28.07.2005, 16:02
Hallo
Also eine Schleife um ein bestimmten Aufruf zu basteln ist sehr einfach, besonders wenn du es noch um nur bestimmte Dateien willst.

for i in $(find /startverzeichnis/ -iname "*mp3" -typef); do
echo "Ich mache was mit $i";
done

oder direkt mit find, hier musst du aber aufpassen, dass du nicht zu viele Prozesse auf einmal startest.
Deshalb ist die Verwendung von xargs statt exec zu empfehlen
find /startverzeichnis -iname "*mp3" -print0 |xargs -0 -n1 befehl
Die -0 bei xargs und das -printf0 in find sorgen dafür, dass die Dateinamen nicht an Leerzeichen zerhackt werden, und die Argumentliste durch \0 getrennt wird.
Das sind nur zwei von vielen Möglichkeiten.
Probier das einfach mal mit echo aus.
Gruß Wolfgang
PS Um mein obiges Script nur auf mp3 zu verwenden, muss noch folgende Zeile eingefügt werden:


...
return unless -f $_;
return unless ($_=~/*.mp3$/ig);

...
Wobei das Rote die neue Zeile ist .

Gruß Wolfgang

ChrisMD
28.07.2005, 17:13
Ich möchte nur eine kleine Anmerkung tätigen und zwar ist es wohl schon allgemein Standard eher alles groß zu schreiben und anstatt einem . ein _ zu verwenden.
Dann kannst du auch alles Taggen, es gibt gute Masstagger die auch solche umbenennungshilfen haben. Wie das bei easytag aussieht weiß ich leider nich aber das kann das bestimmt.

Zico
28.07.2005, 17:53
Ich möchte nur eine kleine Anmerkung tätigen und zwar ist es wohl schon allgemein Standard eher alles groß zu schreiben und anstatt einem . ein _ zu verwenden.
Dann kannst du auch alles Taggen, es gibt gute Masstagger die auch solche umbenennungshilfen haben. Wie das bei easytag aussieht weiß ich leider nich aber das kann das bestimmt.

Kannst du mir vllt. ein paar gute Masstagger nennen? ich komm immer nur wieder zu weiteren MP3 Playern.

Zico
28.07.2005, 18:55
Hehe hab mal beide Scripts angetestet und bin jetzt auf nen lustigen Fehler gestoßen :)


zico@b30wulf:~/tmp/mp3_test/test$ for i in $(ls -RQ1); do echo $i; done
".":
"bla"/
"rename.sh"
"rename.sh~"
"./bla":
"AC-DC,
Def
Leppard,
Bon
Jovi
&
Guns
N'
Roses..-Techno
Mix.MP3"

das Problem liegt an den Leerzeichen. echo macht nach jedem Leerzeichen eine newline...

Cyber
28.07.2005, 19:04
Du musst ja auch ein echo "$i" machen :)

Zico
28.07.2005, 19:13
hat leider den selben effekt :p

Wolfgang
28.07.2005, 20:16
Hallo

Hast du meinen zweiten Beitrag richtig gelesen?
:oldman
Dort gehe ich auf genau diese Problematik ein.
find /verz -iname "*mp3" -print0| xargs -0 -n1 echo

Außerdem sollte es mit meinem Script nicht zu solchen Problemen kommen.

Gruß Wolfgang

Wolfgang
28.07.2005, 20:31
Hehe hab mal beide Scripts angetestet und bin jetzt auf nen lustigen Fehler gestoßen :)

das Problem liegt an den Leerzeichen. echo macht nach jedem Leerzeichen eine newline...

Nein das liegt nicht am echo, sondern in der bash werden diie Argumente per default an space|tab|newline getrennt.
Also werden die gefundenen Dateinamen mit leerzeichen an genau diesen als einzelne Argumente zerstückelt, welche echo dann braf einzeln ausgibt. Dabei ist es egal ob diese einzelnen Stücke mit echo $i oder echo "$i" ausgegeben werden.
Eine Lösung besteht wie schon von mir beschrieben in der Verwendung von -print0 in VebBindung mit xargs -0 , wobei find den ausgegebenen string mit einem \0 terminiert und xargs mit Option -0 alles bis zu dieser NULL als ein Argument interpretiert.
Auch andere Programme wie
perl -0
könnten somit damit etwas anfangen...
Siehe auch man find man xargs perldoc perlrun
Gruß Wolfgang

Zico
28.07.2005, 20:44
@Wolfgang_1

Ich weiss. Dein Script hätte mir in einem die komplette Arbeit abgenommen. Das Problem an all dem ist, dass ich es unbedingt selbst herausfinden will, was, wie und warum so funktioniert.
Bei deinem Script hat sich mir irgendwie alles zu getan. Da steig ich derzeit garnicht mal durch.
Daher habe ich mich erst einmal nur auf BASH an sich eingerschränkt.

und es ist sogar schon was rausgekommen.

Sicher auch nicht 100% clean und auch nicht für alle Fälle gedacht. Dennoch vllt ein post wert:


for i in `find -name "*.[mM][pP]3" -printf "%p\n" | sed -e 's/\ /~/g'`;
do
QUELLE=`echo "$i" | sed -e 's/~/\ /g'`
# erst groß zu klein, dann BLANK zu DOT, dann UNDERLINE zu DOT, dann - zu .-. (aufpassen, wenn script mehrmals rennt, dann gibts noch viel mehr DOTs)
echo $QUELLE | mv "$QUELLE" "`tr '[A-Z]' '[a-z]' | sed -e 's/\ /\./g' | sed -e 's/\_/\./g' | sed -e 's/\-/\.-./g'`" ;
done


for k in `find -name "*.mp3" -printf "%p\n"`;
do
id3 -d $k # entfernt alle id tags von $k - benoetigt id3
done

das ist ersteinmal genau das was ich brauche.
Ich kopier grad all meine Musik in ein Test Verzeichnis und lass es dann drüberrennen.

P.S. Wenn ich mehr Zeit habe, Wolfgang_1 dann seh ich mir dein Script noch einmal ganz genau an. Dann kann ich mir vllt. was schreiben, was mir ständig bei solchen rename problemen hhilft und das man auch variabel konfigurieren kann. Im Moment fehlt mir noch die Erfahrung, aber ich gelobe Besserung.

Wolfgang
28.07.2005, 21:06
Hallo
Es geht nicht darum, dass du mein Script verwendest. ;)
Ich wollte dir nur klar machen, dass es nicht so trivial ist wie es auf den ersten Blick ausschaut. Leerzeichen, ja sogar ganz unübliche Zeichen die nicht darstellbar sind, können in solch ( sorry for ;) WindoZe-verseuchten und nicht nur da ) Dateinamen zu Problemen führen.

Außerdem hast du noch ein paar Sonderfälle nicht beachtet, die durchaus vorkommen könnten:
erstens: Du solltest bedenken, dass es auch vorkommen kann , dass die Dateinamenserweiterung in Upper-case aka MP3 vorkommen kann, deshalb verwende ich im find bei solchen Fällen immer -iname statt -name.
zweitens: Solltest du eventuell noch die Option -type f dazunehen, damit du nicht ausversehen Verzeichnisse (. und .. sind auch Verzeichnisse) erwischt.
drittens kann es ja sein, dass du nachdem du den Namen modifiziert hast eine Datei mit selbigen schon existiert (Großbuchstabe zu Klein wird ja nun eventuell zu einen Namen der vorher schon dort existierte).
Um ein gnadenloses Überschreiben zu verhindern, solltest du den test schon einbauen, bevor du umbenennst.
Also z.B indem du den gebildeten Namen auf eine existierende Datei überprüfst
if [ -e $new ]; then echo "Datei $new existiert schon\n"; else rename... fi

Das nur noch als Tipp am Rande dazu.
;)
Mein Perlscript ist übrigens recht trivial und leicht zu verstehen.
Bei Bedarf kann ich das ja mal im Einzelnen erklären was es Schritt für Schritt macht.

Gruß Wolfgang

Cyber
29.07.2005, 06:54
find -name "*.[mM][pP]3" -printf "%p\n"

Moin Wolfgang,

also die File-Suffixe (Upper/Lower-Case) werden durch den regexp "*.[mM][pP]3"
abgedeckt. der printf bei find gibt nur dateien mit pfadangaben aus, %f gibt nur Datein aus und %h nur das Verzeichnis. :)
Der dritte Fall, dass gnadenlos überschrieben wird stimmt, da fehlt wirklich noch ne Kontrolle.

Zico
29.07.2005, 09:50
Der dritte Fall, dass gnadenlos überschrieben wird stimmt, da fehlt wirklich noch ne Kontrolle.

In dem Punkt hab ich jedoch glück, dass ich keinerlei Duplikate drin habe :)
Also isses relativ egal, ebenso weil eine gute Ordnerstruktur existiert. Diese hat jedoch ein kleines Problem verursacht:
durch die übergabe der Dateien UND verzeichnisse mit sed wurden die verzeichnisse ebenfalls mit dem neuen Standard benannt, was mv dann natürlich verwirrt hat. Ein umbenennen der Verzeichnisse (10 minutem im Konqueror) hats dann aber gebracht und das Script lief fehlerfrei durch.