MP3-Dateien in einem Verzeichnis mit For-Schleife automatisch umbenennen - Wie geht das genau?

cabriofahrer

Well-Known Member
Leider sind meine Programmierkenntnisse recht spärlich und mit allgemeinen Lehrbuchbeispielen komme ich hier nicht weiter. Ich möchte gerne alle meine MP3-Dateien Verzechnis für Verzeichnis in eine bestimmte Namenstruktur bringen und zwar innerhalb eines Verzeichnisses sollen alle mp3's dann nach dem Muster "01 - Songtitel" heißen. Dafür wäre natürlich eine For-Schleife angebracht, im Ansatz etwa so:

for file in *.mp3; do
ja was denn genau?;
done

Also angenommen, ich befinde mich in einem Verzeichnis und der Inhalt sieht so aus:

Code:
$ cd work/Raise\ Your\ Fist\ And\ Yell/
$ ls
Alice Cooper - 01 - Freedom.mp3
Alice Cooper - 02 - Lock Me Up.mp3
Alice Cooper - 03 - Give The Radio Back.mp3
Alice Cooper - 04 - Step On You.mp3
Alice Cooper - 05 - Not That Kind Of Love.mp3
Alice Cooper - 06 - Prince Of Darkness.mp3
Alice Cooper - 07 - Time To Kill.mp3
Alice Cooper - 08 - Chop, Chop, Chop.mp3
Alice Cooper - 09 - Gail.mp3
Alice Cooper - 10 - Roses On White Lace.mp3

Die For-Schleife müsste jetzt also für jede Datei eine Umbenennung durchführen dergestalt, dass überall "Alice Cooper" vorne wegkommt und dann nur jeweils "01 - Freedom", "02 - Lock Me Up", usw. übrigbleibt. Umbenennung geht mittles dem Kommando "mv", oder vorsichtshalber "cp", wenn man die alte Datei nicht löschen will.
Als Versuch habe ich manuell einfach mal

Code:
$ cp Alice\ Cooper\ -\ 01\ -\ Freedom.mp3 "01 - Freedom.mp3"

Und hier weiß ich nicht mehr weiter. Das Kommando müsste also jede Datei lesen und die Teile "Tracknummer" und "Songtitel" mit Leerzeichen-Leerzeichen als neuen Dateinamen wieder zusammensetzten. Ich nehme an, hier kommen dann Variablen ins Spiel?
 
Das geht mit misc/mmv sehr elegant. Aber sei gewarnt, dass das bei falscher Eingabe/was übersehen auch Dateien löschen kann. Daher prüfe es doppelt nach oder kopier das Verzeichnis vorher temporär nochmal woanders hin.

Dein Beispiel sollte konkret so lauten:
mmv "*Alice Cooper - *" "#1#2"
 
for file in *.mp3; do
ja was denn genau?;
done
Der Ansatz mit der For-Schleife ist schon nicht verkehrt.
file ist ja hier die Variable die bei jeder Iteration den jeweiligen Dateinamen annimmt.
Es gibt da den Mechanismus der Parameter Expansion (siehe z.B: auch das dazugehörige Kapitel in der Manpage von sh).
Danach wäre eine Vorgehensweise einfach ein:
Bash:
for file in *.mp3
do
   mv "$file" "${file#Alice Cooper - }"
done
zu machen.
Das sorgt dafür, das Alice Cooper -
am Anfang rausgestrichen wird, falls es existiert. Das mv-Kommando wird aber auf jede Datei angewendet. Was bei mv aber kein Problem ist, weil ein mv auf sich selbst folgenlos bleibt. Wenn Du ein cp nimmst kopiert er nicht (bleibt also auch folgenlos)

Die Anführungsstriche zu setzen ist wichtig, weil die Dateinamen Leerzeichen enthalten und sie sonst bei der Übergabe an mv, cp (oder was auch immer) mehrere Argumente wahrgenommen werden.
 
Wenn du viele Dateien bearbeiten musst, könnte sich auch ein Blick auf audio/picard lohnen. Da kann man datenbankgestützt Albendaten ziehen (ähnlich CDDB, nur viel besser) und von Quellverzeichnis auf Zielverzeichnis seine persönliche Ausgabemaske legen, so wie man das haben möchte. Hat gleichzeitig noch den Vorteil, dass man optional Tags inkl. Cover schreibt und die Gewissheit, dass das Album komplett ist bez. Trackzahl und Tracklänge.
Erfordert etwas Einarbeitungszeit, spart aber hintenraus enorm viel Zeit bei wirklich vielen Dateien.
 
Es kommt mir etwas wirr vor, aber ich kopiere mal hier so ein Script hin, wie ich das immer benutze.
Zum Hintergrund: mir geht es wie dem John Schnee und ich weiß gar nichts. Deshalb behalte ich mir Scripts, die einmal funktioniert hatten und modifiziere sie für neue Aufgaben dann leicht um. Das Grundgerüst bleibt einfach gleich und manchmal ist das Ergebnis dann unhandlicher, als, wenn man etwas Einfaches schnell neu tippt. Außerdem ist das Konzept auch anfällig für Fehler.
Beim schnellen Durchsehen sind mir da einige Dinge gar nicht mehr so klar und ich meine auch, Fehler zu sehen, investiere aber nun nicht weiter.
Außerdem wäre es womöglich angeraten, noch einen Filter nach mp3 zu setzen, weil Verzeichnisse mit Musik manchmal auch noch andere Dateien enthalten.
Zuletzt hatte ich das benutzt, um Bild-Dateien von meinem Handy neu zu benennen.
Es ist langsam, weil "interaktiv", dafür kann man aber beinahe zusehen und auch an manchen Stellen noch eingreifen. Auch das kann man ja abschalten.
Dann bleibt zum Schluss nur noch die Zeile mit dem sed...

Zuvor noch die Frage: easytag gibt es nicht mehr?
Das ist doch eine verdammt gute "Umbennungs"-SW und kann gleichzeitig auch die Tags, oder erinnere ich mich falsch?

Nun das Script:
Code:
 #!/bin/csh

# wir rufen das nur und immer mit sh auf
# wenn ich recht sehe, suchen wir in einem Verzeichnis nach allen Dateien
# und ersetzen OSTRING mit NSTRING im Namen.

IFS='
'

WO=
OSTRING=
NSTRING=
NEXT=ende
ABFRAGE="Schluß hier!"
OK=

# Routine fuer den Fall "ctrl-c"
trap ende 2

#ende
ende ()
{
    echo ""
    echo "GOOD BYE !"
    sleep 3
    clear
    exit 0
}

#immer wiederkehrende Funktionen
obweiter ()
{
    #auf dem Screen:
    echo "Ist das OK ?"
#    echo ""
    echo "-> Nein = "$ABFRAGE""
#    echo ""
    echo -n "-> "
}

readok ()
{   
    read OK
    echo "Eingabe ist: "$OK""
    [ "$OK" = "Nein" ] && "$NEXT"
}

weiter ()
{
    echo "also weiter..."
    echo ""
}

rename ()
{
for i in $( find ./ -iname ""$OSTRING"*" | sort )
do
newname=$( echo "$i" | sed -e "s/^.*$OSTRING/$NSTRING/")
echo ""$i" -> "$newname""
ABFRAGE="Schluß hier oder neuen Name Eingeben"
obweiter
readok
weiter
[ "$OK" ] && newname="$OK"
[ -e $i ] && mv -i "$i" "$newname"
done
}

delete ()
{
for i in $( find ./ -iname ""$OSTRING"*" | sort )
do
[ -e $i ] && echo ""$i" -> wird gelöscht"
ABFRAGE="Schluß hier!"
obweiter
readok
weiter
[ -e $i ] && rm "$i"
done
}

DIRNAME=$(dirname $0)
echo ""
echo "das Verzeichnis, in dem wir gestartet sind, wird nun vorzugsweise gesetzt und ist:"
echo "$DIRNAME"
echo ""
echo "fast immer wollen wir ein neues Verzeichnis setzen und zwar:"
echo -n "-> "
read WO
echo ""
echo ""
[ "$WO" = "" ] && WO="$DIRNAME"
[ ! -e "$WO" ] && echo ""$WO" gibt es gar nicht, Schluss ist." && echo "" && exit 1
[ ! -d "$WO" ] && echo ""$WO" ist doch kein Verzeichnis! Und Schluss." && echo "" && exit 1
echo "WO ist "$WO"" && echo ""
obweiter
echo "es kann der alte bestätigt, oder einmal ein neuer String gesetzt werden:"
echo -n "-> "
readok
[ "$OK" ] && WO="$OK" && echo "WO soll nun "$WO" sein" && echo ""
[ ! -e "$WO" ] && echo ""$WO" gibt es gar nicht, Schluss ist." && echo "" && exit 1
[ ! -d "$WO" ] && echo ""$WO" ist doch kein Verzeichnis! Und Schluss." && echo "" && exit 1
weiter

cd "$WO"


echo ""
echo "Nun lesen wir, was ersetzt werden soll:"
echo ""
echo -n "-> "
read OSTRING
echo ""
echo "der alte String ist: $OSTRING"
echo ""
obweiter
echo "es kann der alte bestätigt, oder einmal ein neuer String gesetzt werden:"
echo -n "-> "
readok
[ "$OK" ] && OSTRING="$OK"
weiter

echo ""
echo "Nun lesen wir, womit ersetzt werden soll:"
echo "ein - wird automatisch nach dem String eingefügt"
echo ""
echo -n "-> "
read NSTRING
echo ""
NSTRING=$(echo $NSTRING-)
echo "der neue String ist: $NSTRING"
echo ""
obweiter
echo "es kann das gleich bestätigt, oder einmal ein neuer String gesetzt werden"
echo -n "-> "
readok
[ "$OK" ] && NSTRING="$OK"
weiter

rename

ende
 
Zuvor noch die Frage: easytag gibt es nicht mehr?
Gibt es noch. Hatte ich auch lange zufriedenstellend benutzt und rannte zum Glück nicht in den Bug rein, der damals Dateien mit Vorbis-Tag irreparabel grillte.
Das ist doch eine verdammt gute "Umbennungs"-SW und kann gleichzeitig auch die Tags, oder erinnere ich mich falsch?
Ja, aber picard (https://picard.musicbrainz.org/) ist besser. Es erkennt automatisch die ihm angereichten Dateien und gleicht die Daten gegen https://musicbrainz.org/ ab. Es erkennt auch unterschiedliche Versionen von Alben, z.B. bei Bonustracks und Videotracks aka CD-Extra. Ebenfalls wird auch nach Medium (Tape, Vinyl, Digital, CD...) unterschieden. Jede Version hat eine eindeutige ID und somit auch (oft) ein eindeutiges Cover. Editoren (https://musicbrainz.org/user/mr44er ;) ) sind angehalten, möglichst exakt und präzise wie möglich einzutragen. Neu eingereichte Daten durchlaufen ein Votingverfahren, sodass möglichst viele das sehen und abstimmen können. Murks wird aussortiert.
Die Datenbank wächst stetig, sodass auch Nischenmusik gefunden wird. Selbst wenn man ein ungetaggtes und unbenanntes Album reinschiebt, kann man manuell suchen und es sortiert anhand der Tracklänge inkl. Toleranz dann durch. Es gibt noch viel mehr, aber das sind so die Highlights. :)

Sähe für Raise Your Fist and Yell so aus: https://musicbrainz.org/release-group/741dd18e-ec06-3411-ba12-eff8481a3265
 
Das kann auch manch Datei-Manager, z.B. der Nautilus, ganz gut. Nur falls Du mal keine Lust auf Shell-Programmierung hast.

Vor vielen, vielen Jahren hab ich mir auch mal so ein script geschrieben, ganz auf die Schnelle, und damit ca. 8 GB Musikdateien umbenannt. Gab auch keine Datenverluste, aber danach hiessen meine Dateien z.B. cqqw.mp3 oder libpod32.mp3. Manche wurden aber auch korrekt umbenannt.
Ein prüfender Blick auf mein script, und ich wusste warum. Also Obacht.
;)
 
ich muss nochmal nachtreten, denn wenn du nun das Script von oben so benutzt, dann denkst du wohl, dass ich dein Problem gar nicht verstanden habe.
Das kann natürlich auch sein.
Aber, so war das auch gar nicht gemeint.
In dem Teil rename kann man einfach neue Kombinationen, den String zu verändern, einfach setzen. Mittels sed kann zB auf dein Beispiel bezogen einfach alles aus dem Text geschnitten werden, bis zum Block mit Nummern, oder alles, was gleich ist mit "Alice Cooper -", oder eine Anzahl von Zeichen vom Anfang des Strings. Außerdem kann man auch einfach mit cut arbeiten.
Und dann kann man einfach auch neue Strings benutzen und nicht interaktiv abfragen usw.
Das habe ich gemeint.
Das Grundgerüst behalte ich quasi immer irgendwie bei und passe es dann einfach nur so an, wie ich es gerade lieber hätte.
In deinem Beispiel von oben könnte etwa
Code:
sed -e "s/^.*Alice Cooper\ -\ //"
gut wirken.
Du siehst sicher, wie einfach der Ausdruck zu verändern wäre und wie man dann das den Variablen auch mitgeben könnte (wenn man will). Vielleicht sollte man irgendwo noch eine Ausgabe von ls einfügen, damit man ganz einfach den Teil im Namen markieren und dann mit mittlerer Maus einfügen kann, der ersetzt werden soll. Und so weiter...
 
sed -e "s/^.*Alice Cooper\ -\ //"
Ich verstehe nicht, warum Du den regulären Ausdruck so aufbaust.
Denn quasi auf den String-Anfang mit ^ zu matchen, um danach dann aber ein .* leuchtet mir grad' nicht ein.
Warum dann nicht gleich sed -e "s/Alice Cooper\ -\ //" ?
Oder war das im Rahmen des Scripts gedacht? Falls ja: Ich habs mir jetzt nicht im Detail angesehen, sondern nur überflogen (ist etwas unübersichtlich). Da wird ja irgendwie mit find gearbeitet. Da macht es einen gewissen Sinn, weil find einen Pfad mit zurückliefert. Das schneidet den Pfad mit raus. Ist aber unsicher, weil wenn Du ein Unterverzeichnis mit dem Namen Alice Cooper hast, gibt das Ärger weil er an der falschen Stelle "matcht". Außerdem passen die Pfade für die Zieldatei nicht mehr, sollte es Unterverzeichnisse geben in die find rein "traversiert".
 
Wie man sieht gibts da verschiende Ansätze. Für die Bash, und unter Linux gerade auch getestet:

Code:
for i in `ls *.mp3`
do
IFS=$'\n'
newfile=`echo $i |sed -r 's/(.*)\ \-\ ([0-9]+)\ \-\ (.*)/\2 - \3/g'`
mv $i $newfile
done

oder als Einzeiler:

Code:
for i in `ls *.mp3`; do IFS=$'\n'; newfile=`echo $i |sed -r 's/(.*)\ \-\ ([0-9]+)\ \-\ (.*)/\2 - \3/g'`; mv $i $newfile; done
 
Zuletzt bearbeitet:
Das ist natürlich korrekt, das .mp3 gehört an dieser Stelle einfach weg, danke für die Korrektur. Ich hatts zuerst anders escaped da hats auch geklappt weils ohnehin ignoriert wurde^^

Edit: Habs jetzt auch im ursprünglichen Post korregiert!
 
Ich verstehe nicht, warum Du den regulären Ausdruck so aufbaust.
Denn quasi auf den String-Anfang mit ^ zu matchen, um danach dann aber ein .* leuchtet mir grad' nicht ein.
keine Ahnung, vielleicht hatte ich mal Namen mit führenden Leerzeichen?

Es bleibt mir nur, nochmals zu betonen, dass ich keine Ahnung habe und gar nicht programmieren kann und auch gar nicht Scripts beherrsche!

Was ich hier veröffentliche, soll nicht ein Beispiel sein, wie man etwas gut und richtig macht!
Das kann ich gar nicht.

Trotzdem hat mir dieses (zugegeben wirre) Script schon ganz oft geholfen und ich benutze es deshalb immer wieder gerne und verändre halt, was gerade aktuell gefordert ist.
Ich weiß gerade auch gar nicht, wo ich es ( zumindest in Teilen ) abgeschrieben habe.
So wirr es auch ist, komme ich halt damit klar und kann es auch jeweils meinen Zwecken anpassen.

Wobei vielleicht gerade in der kurz zuvor veröffentlichen Version auch extra viele Fehler stecken können, weil ich eben nicht immer genau darauf schaue, wo Fehler noch lauern können.

Weil ich das eben nicht gut kann und schon gar nicht beherrsche, unternehme ich gar keine neuen Anstrengungen.
Ich setze ein paar Versuche mit cat und sed und grep und cut ab, und wenn es gerade passt, dann nehme ich diesen Befehl halt in das Script und lasse es mal zur Probe laufen.
Allermeist passt das dann schon recht gut.
 
Es bleibt mir nur, nochmals zu betonen, dass ich keine Ahnung habe und gar nicht programmieren kann und auch gar nicht Scripts beherrsche!
Ist ja auch alles in Ordnung. Das war von meiner Seite auch nicht als Kritik/Angriff gemeint.
Wir alle sind ja hier, um was zu lernen. Und im dem Zuge kann man ja ruhig mal solche Dinge aufgreifen, anmerken, hinweisen, nachfragen, finde ich.

Wobei vielleicht gerade in der kurz zuvor veröffentlichen Version auch extra viele Fehler stecken können, weil ich eben nicht immer genau darauf schaue, wo Fehler noch lauern können.
Sagen wir mal so, es waren mehrere Dinge über die ich gestolpert bin und was man möglicherweise anders machen könnte. Ich kann das ja mal so sagen. Wie gesagt. Nicht als Angriff oder so, sondern so eher im Sinne von Tipps & Hinweise und wenn man mich wiederum jemand korrigiert, dann ist das umso besser. :-)


Die Zeile ist gar nicht notwendig, wenn Du das Skript via sh (also sh myscript.sh) aufrufst. Die ist nur dann vonnöten, wenn Du das Skript ausführbar machst. Und wenn Du in dem Fall willst, das es via sh ausgeführt wird, dann sollte das da auch so stehen:
Bash:
#!/bin/sh

Das mit dem Arbeitsverzeichnis, welches ja mit
Bash:
DIRNAME=$(dirname $0)
usw.
abgefragt wird. Das würde ich so nicht machen. Auf $0 zurückzugreifen bedeutet ja das Verzeichnis zu nehmen, in dem das Skript liegt. Das will man ja in den allermeisten Fällen nicht. Idealerweise möchte man ja das Verzeichnis als Vorgabe haben, in dem man sowieso schon drin ist. Und das kriegt man mit pwd

Man kann ja dann trotzdem nach einem alternativen Verzeichnis fragen. Da würde ich aber einiges rausstreichen so nach dem Motto "simpler ist besser". Ich würde so skript-typisch das aktuelle Verzeichnis als Vorgabe machen und entweder der Benutzer bestätigt das mit ENTER oder tippt halt was ein.

Umständliche Fehlerbehandlung inkl. Fehlermeldungen würde ich auch nicht machen, sondern das zum Großteil cd überlassen.
So kann man die ganzen Zeilen dazu eindampfen auf:
Bash:
DIRNAME=$(pwd)
read -p "In welchem Verzeichnis soll ich tätig werden? [ $DIRNAME ]: " WO
cd ${WO:-$DIRNAME} || exit 1
Schlägt cd fehl, wird via exit das Script verlassen.
${WO:-$DIRNAME} ist die hier im Thread schon angesprochene 'Parameter Expansion' und bewirkt so sinngemäß: Wenn WO keinen Wert hat, dann nimm das was in DIRNAME drin steht.

Übrigens kann mehrzeile Zeichenketten auch tatsächlich mehrzeilig schreiben. Sowas wie
Bash:
echo ""
echo "Nun lesen wir, womit ersetzt werden soll:"
echo "ein - wird automatisch nach dem String eingefügt"
echo ""
kann man auch schreiben als
Bash:
echo "
Nun lesen wir, womit ersetzt werden soll:
ein - wird automatisch nach dem String eingefügt
"
Was echos und Anführungsstriche spart.

Weil ich das eben nicht gut kann und schon gar nicht beherrsche
Gerade deshalb würde ich Skripte so kurz/einfach wie möglich halten und entsprechend auch zu kommentieren, damit man auch später noch nachvollziehen kann, was wo warum gemacht wurde.
Ich hab einfach mal Dein Skript genommen und so ein bisschen umgebaut, um mal zu zeigen, wie das aussehen könnte. Evtl. hilft das Dir weiter bzw. Du kannst es gut als Grundlage für Anpassungen nehmen.
Wenn Du Fragen hast, dann frage gerne nach.
Bash:
#!/bin/sh

# aktuelles Verzeichnis merken
DIRNAME=$(pwd)

# --------- Funktionen --------------

# :: Funktion:
# Wird beim beenden des Skriptes aufgerufen
ende()
{
    echo -e "\n\nSkript wurde beendet!"
    # ggf. zum vorherigen Verzeichnis zurückkehren
    cd "$DIRNAME"
    exit 0
}

# :: Funktion:
# Verlangt eine Eingabe vom Nutzer die nicht leer ist
#  1.Parameter: Variable, in der die Eingabe gespeichern werden soll
#  2.Parameter: Prompt-Text
eingabe()
{
    local EINGABESTRING=""
    until [ -n "$EINGABESTRING" ] # Solange fragen, bis Eingabe nicht 
                                  # mehr leer ist
    do
        read -p "$2: " EINGABESTRING
    done
    eval "$1=\"$EINGABESTRING\""    # Eingabe an übergebene Variable
}

# --------- Beginn --------------

trap ende SIGINT # ende() auch bei Strg-C aufrufen

echo "
Dieses Skript benennt Dateien in einem Verzeichnis um.
Du kannst es jederzeit mit STRG-C abbrechen
"

# --------- Abfrage der notwendigen Parameter --------------

# Arbeitsverzeichnis abfragen. Vorgabe ist das aktuelle Verzeichnis
read -p "In welchem Verzeichnis soll ich tätig werden? [ $DIRNAME ]: " WO
cd "${WO:-$DIRNAME}" || ende

# Nach Dateinamensmuster fragen und in 
#  DATEIEN
# speichern
eingabe DATEIEN "Dateinamensmuster"

# nach alten Dateinamensteil fragen und in 
#  OSTRING
# speichern
eingabe OSTRING "Dateinamensteil der ersetzt werden soll"

# nach neuen Dateinamensteil fragen und in 
#  NSTRING
# speichern (Leere Eingabe akzeptieren)
read -p "'$OSTRING' wird ersetzt durch: " NSTRING 

echo "" # Leerzeile einfügen

# --------- eingegebene Parameter verarbeiten  --------------

ABFRAGE="N"
for OLDNAME in $DATEIEN   # über alle Dateien iterieren
do
    NEWNAME=$( echo "$OLDNAME" | sed -e "s/^.*$OSTRING/$NSTRING/")
    # Einzelabfrage überspringen?
    if [ ! $(expr "$ABFRAGE" : '[Aa]') -eq 1 ]
    then
        eingabe ABFRAGE "Datei '$OLDNAME' umbenennen in '$NEWNAME' (J/N/A)?"
    fi
    if [ $(expr $ABFRAGE : '[YyJjAa]') -eq 1 ]
    then # OK, wir dürfen umbenennen falls Datei $NEWNAME 
         # nicht schon existiert (Parameter -n):
        mv -n "$OLDNAME" "$NEWNAME" && echo " '$OLDNAME' umbenannt in '$NEWNAME'"
    else
        echo "'$OLDNAME' übersprungen"
    fi
done

echo -e "\nFertig!"

ende
Relevante Manpages: sh, mv, expr, pwd, test (das [ was insbesondere bei if-Abfragen vorkommt ist ein Alias für test)
Wenn jemand Kritik hat, dann natürlich auch gerne.
Ich kenne mich nicht besonders gut mit Shell-Skripts aus und hab bestimmt Schnitzer drin. Und dann freue ich mich auf ne Chance dazu zu lernen.
 
Sobald es an die Bearbeiitung von Strings geht (hier: alles bis zum ersten Bindestrich kappen, danach ein trim auf den String wegen möglichem Leerzeichen zu Beginn) greife ich zu Scriptsprachen - selbst PHP wäre mir hierfür lieber als ein Shellscript. Das hier wäre eine funktionierende Lösung, und benennt alles im aktuellen Verzeichnis entsprechend um:
Code:
#!/usr/local/bin/tclsh8.6
set lFiles [ glob * ]
foreach sFile $lFiles {
    if { [ file isfile $sFile ] } {
        set sRename ""
        set bSkip true
        set sDash ""
        foreach sPart [ split $sFile "-" ] {
            if { $bSkip } {
                set bSkip false
            } else {
                append sRename $sDash [ string trim $sPart ]
                set sDash " - "
            }
        }
        file rename $sFile $sRename
    }
}
"lang/tcl86" muss natürlich auch installiert sein… Testen lässt sich auch schmerzfrei, indem man einfach nicht gleich das "richtige" Verzeichnis überbügelt, sondern eine Kopie davon. Und das Ganze lässt sich natürlich noch leicht ausbauen…
 
Das hier wäre eine funktionierende Lösung, und benennt alles im aktuellen Verzeichnis entsprechend um
Ich bin jetzt absolut kein Kenner was tcl angeht. Den Einsatz von split verstehe ich. Aber warum setzt Du dann so umständlich via Schleife den Rest wieder zusammen? Wäre es da nicht praktischer einfach lrange zu nehmen?
Und was ist mit dem Fall, das kein Bindestich im Dateinamen drin ist?
Könnte man dann evtl. auch so schreiben:
Bash:
#!/usr/local/bin/tclsh8.6
set lFiles [ glob * ]
foreach sFile $lFiles {
    if { [ file isfile $sFile ] } {
        set lParts [ split $sFile "-" ]
        # Test, ob kein - im Dateiname
        if "[ llength $lParts ] > 1" {
           # erstes Element aus $lParts ignorieren und zu String zusammen setzen:
           set sRename [ string trim [ join [ lrange $lParts 1 end ] "-" ] ]
           file rename $sFile $sRename
       }
    }
}

Testen lässt sich auch schmerzfrei, indem man einfach nicht gleich das "richtige" Verzeichnis überbügelt, sondern eine Kopie davon
Oder alternativ ersetzt man temporär
file rename $sFile $sRename
durch
puts "file rename '$sFile' '$sRename'"
 
Ich bin jetzt absolut kein Kenner was tcl angeht. Den Einsatz von split verstehe ich. Aber warum setzt Du dann so umständlich via Schleife den Rest wieder zusammen? Wäre es da nicht praktischer einfach lrange zu nehmen?
"lrange" hatte ich heute Morgen schlicht nicht auf dem Radar… ist halt der universelle Ansatz, der auch mit anderen von mir genutzten Sprachen zielführend ist ;) Und dass es Sinn macht insgesamt noch ein wenig mehr Prüfroutinen einzubauen steht ohne Frage, will man das Ganze längerfristig nutzen (und nicht nur für diesen einen Fall). War halt ein schneller Ansatz zur ersten Tasse Tee.
 
Das geht mit misc/mmv sehr elegant. Aber sei gewarnt, dass das bei falscher Eingabe/was übersehen auch Dateien löschen kann. Daher prüfe es doppelt nach oder kopier das Verzeichnis vorher temporär nochmal woanders hin.

Dein Beispiel sollte konkret so lauten:
mmv "*Alice Cooper - *" "#1#2"
Erst mal vielen herzlichen Dank für die rege Beteiligung hier! In der Tat scheint hier mmv die einfachste Lösung zu sein. In der Manpage ist sogar ein konkretes Beispiel für Musikdateien genannt:

Code:
Rename music files from <track no.> - <interpreter> - <song title>.ogg
       to <interpreter> - <track no.> - <song title>.ogg in the current
       directory:

          mmv '* - * - *.ogg' '#2 - #1 - #3.ogg'

Demnach konnte ich also ganz einfach
Code:
mmv '* - * - *.mp3' '#2 - #3'
machen.

Hier das Resultat:

Code:
$ cd work/Raise\ Your\ Fist\ And\ Yell/
$ ls
Alice Cooper - 01 - Freedom.mp3
Alice Cooper - 02 - Lock Me Up.mp3
Alice Cooper - 03 - Give The Radio Back.mp3
Alice Cooper - 04 - Step On You.mp3
Alice Cooper - 05 - Not That Kind Of Love.mp3
Alice Cooper - 06 - Prince Of Darkness.mp3
Alice Cooper - 07 - Time To Kill.mp3
Alice Cooper - 08 - Chop, Chop, Chop.mp3
Alice Cooper - 09 - Gail.mp3
Alice Cooper - 10 - Roses On White Lace.mp3
$ mmv '* - * - *.mp3' '#2 - #3'
$ ls
01 - Freedom            06 - Prince Of Darkness
02 - Lock Me Up            07 - Time To Kill
03 - Give The Radio Back    08 - Chop, Chop, Chop
04 - Step On You        09 - Gail
05 - Not That Kind Of Love    10 - Roses On White Lace
$

Jetzt habe ich hier aber noch eine kleine Verständnisfrage: Was macht hier genau das #-Symbol? Offensichtlich ist es erst mal so, dass die Ziffern 1, 2 und 3 für die Reihenfolge der Dateistruktur aus <track no.> - <interpreter> - <song title> stehen, die man dann nach Wunsch vertauschen kann, bzw. in meinem Fall auch weglassen kann (#1). Welche Funktion kommt hier also dem #-Symbol zu? Und gibt es dafür einen bestimmten Begriff in der allgemeinen Programmierung oder ist das eine eigene Sache von mmv?
 
Was macht hier genau das #-Symbol?
Es greift das jeweilige vorgehende / n-te wildcard (*).
In deinem Beispiel hast du dir aber die Endung .mp3 weggegrillt.
Hier besser beschrieben, als ich das könnte: https://wiki.ubuntuusers.de/mmv/

Und gibt es dafür einen bestimmten Begriff in der allgemeinen Programmierung oder ist das eine eigene Sache von mmv?
Das fährt allgemein unter pattern match oder regular expression (regex). An sich oft gleich, aber manchmal dann je nach Anwendung andere Schalter, dass diese es als solches auch akzeptiert.
 
In der Tat scheint hier mmv die einfachste Lösung zu sein.
Find ich nicht. Ich finde, Deine anvisierte Lösung war am einfachsten. Man brauch nix installieren. Man braucht nicht mit regulären Ausdrücken rumzuhantieren, wo man im Zweifel nicht weiß was da alles "gematcht" wird und was nicht.

Reguläre Ausdrücke machen vorallem dann Sinn, wenn man nicht exakt "matchen" will.
Typische Szenario, wenn Alice Cooper in unterschiedlichen Dateinamen unterschiedlich geschrieben worden wäre. Mal klein (alice cooper), mal groß (ALICE COOPER) oder bunt durcheinander (AliCe CoOper).
Für exakte Suche und dann noch am Dateinamensanfang fand ich Dein Ansatz am besten/unkompliziertesten.

Wenn man reguläre Ausdrücke braucht, dann scheint tatsächlich aber mmv ein simples und gutes Tool zu sein. Insofern sind wir alle froh, das es hier mit genannt wurde.

Jetzt habe ich hier aber noch eine kleine Verständnisfrage: Was macht hier genau das #-Symbol?
Es wurde ja schon gesagt. Ich möchte es an der Stelle noch mal ergänzen.
Das sind Verweise auf die jeweilig gefunden Wildcard-Bruchstücke (das Wildcardzeichen * ist ja sozusagen ein Platzhalter für ein beliebigen Inhalt).

Jetzt mal Beispielhaft für Dein Muster und einen Dateinamen:
Code:
    '* - * - *.mp3' auf 'Alice Cooper - 01 - Freedom.mp3'
     |   |   |
     |   |   |
     |   |   +-------------------+
     |   |                       |
     |   +------------+          |
     |                |          |
     |                |          |
     #1               #2         #3
 Alice Cooper         01       Freedom

Um dann den Aufruf richtig anzuwerden, ist folgerichtig der Aufruf von Dir zu ergänzen zu:
mmv '* - * - *.mp3' '#2 - #3.mp3'
 
Zuletzt bearbeitet:
Jetzt habe ich hier aber noch eine kleine Verständnisfrage: Was macht hier genau das #-Symbol?
wenn du das so fragst, nutzt meine Antwort wahrscheinlich nicht viel, aber das ist so ähnlich, wie bei sed und/oder noch deutlicher bei awk auch. Man bestimmt aus einem String bestimmte Felder und schreibt die in neuer Reihenfolge oder lässt auch welche weg.
mmv nutzt halt eine sehr einfache Syntax dafür.
 
In deinem Beispiel hast du dir aber die Endung .mp3 weggegrillt.
Danke für den Hinweis, war mir nicht aufgefallen. Konnte ich aber manuell wieder anfügen.
OK, vielen Dank nochmal. Also ich finde schon gefallen an mmv, jedoch sind einige Ordner so chaotisch und mit uneinheitlicher Struktur, dass man besser komplett manuell verfährt. Hier habe ich einen, der gar nicht mal so schlimm ist, so dass ich mit 2 mmv-Befehlen schon mal in die richtige Richtung gehen konnte:

Code:
$ ls
01-bad_religion-52_seconds.mp3        09-bad_religion-grains_of_wrath.mp3
02-bad_religion-heroes_and_martyrs.mp3    10-bad_religion-murder.mp3
03-bad_religion-germs_of_perfection.mp3    11-bad_religion-scrutiny.mp3
04-bad_religion-new_dark_ages.mp3    12-bad_religion-prodigal_son.mp3
05-bad_religion-requiem_for_dissent.mp3    13-bad_religion-the_grand_delusion.mp3
06-bad_religion-before_you_die.mp3    14-bad_religion-lost_pilgrim.mp3
07-bad_religion-honest_goodbye.mp3    15-bad_religion-submission_complete.mp3
08-bad_religion-dearly_beloved.mp3    16-bad_religion-fields_of_mars.mp3
$ mmv '*-*.mp3' '#1 - #2.mp3'
$ ls
01 - bad_religion-52_seconds.mp3
02 - bad_religion-heroes_and_martyrs.mp3
03 - bad_religion-germs_of_perfection.mp3
04 - bad_religion-new_dark_ages.mp3
05 - bad_religion-requiem_for_dissent.mp3
06 - bad_religion-before_you_die.mp3
07 - bad_religion-honest_goodbye.mp3
08 - bad_religion-dearly_beloved.mp3
09 - bad_religion-grains_of_wrath.mp3
10 - bad_religion-murder.mp3
11 - bad_religion-scrutiny.mp3
12 - bad_religion-prodigal_son.mp3
13 - bad_religion-the_grand_delusion.mp3
14 - bad_religion-lost_pilgrim.mp3
15 - bad_religion-submission_complete.mp3
16 - bad_religion-fields_of_mars.mp3
$ mmv '* - *-*.mp3' '#1 - #3.mp3'
$ ls
01 - 52_seconds.mp3        09 - grains_of_wrath.mp3
02 - heroes_and_martyrs.mp3    10 - murder.mp3
03 - germs_of_perfection.mp3    11 - scrutiny.mp3
04 - new_dark_ages.mp3        12 - prodigal_son.mp3
05 - requiem_for_dissent.mp3    13 - the_grand_delusion.mp3
06 - before_you_die.mp3        14 - lost_pilgrim.mp3
07 - honest_goodbye.mp3        15 - submission_complete.mp3
08 - dearly_beloved.mp3        16 - fields_of_mars.mp3
$

Hier habe ich in einem ersten Schritt erreichen können, dass zwischen der Tracknummer und dem Rest der Bindestrich mit jeweils zwei Leerzeichen davor und dahinter geschrieben wird.
In einem zweiten Schritt habe ich dann "bad_religion-" entfernen und die Titel alleine stehen lassen können.

Jetzt wäre es natürlich schön, wenn man bei den Titeln alle Unterstriche entfernen und alle Wörter im Titel groß schreiben könnte, d.h. nur jeweils der erste Buchstabe als uppercase. Doch hier sieht man schon, dass die Titel aus mehreren Wörtern bestehen, es dementsprechend mehrere Unterstriche gibt. Denkbar sind natürlich noch längere Titel.
Kann man das mit mmv überhaupt noch hinbekommen?
 
Ob das mit mmv geht, weiß ich nicht. Gerade das mit den Groß- und Kleinbuchstaben, da haben wir ja das Problem, das es unterschiedlich viele Wörter sein können. Da einfach nur mit # zu referenzieren (ohne über Matches iterieren zu können), ist da ungünstig.

Beginnen würde ich mit Deinem Ansatz, um über alle Dateien im aktuellen Verzeichnis zu iterieren.

wenn man bei den Titeln alle Unterstriche entfernen
Da würde ich ein Befehl anwenden, den auch schon @pit234a vorgeschlagen hat. Nämlich sed.
Damit kann man wunderbar Suchen&Ersetzen machen: sed -e 's/_/ /g'

Nur den ersten Buchstaben groß zu machen, da weiß ich nicht, ob das auch mit sed geht. Aber @pit234a hat noch einen anderen Befehl mit ins Spiel gebracht. Nämlich awk. Das kennt nämlich die Funktion toupper und substr. Wir können dann noch tolower dazu nehmen, um sicher zu stellen das der Rest eines Wortes klein geschrieben ist:
for (i=1; i<=NF; ++i) { $i=toupper(substr($i,1,1)) tolower(substr($i,2)); } print

Wenn wir dann alles zusammen packen, haben wir folgendes sh-ShellScript:
Bash:
#!/bin/sh

for file in *.mp3
do
   newname=$( echo "$file" | sed -e 's/_/ /g')
   newname=$( echo "$newname" | awk '{ for (i=1; i<=NF; ++i) { $i=toupper(substr($i,1,1)) tolower(substr($i,2)); } print }')
   echo "'$file' -> '$newname'"
   # mv -i "$file" "$newname"
done
Sollte dann sowas wie
'02 - heroes_and_martyrs.mp3' -> '02 - Heroes And Martyrs.mp3'
ausgeben.

Wenn Du dann nach erster Überprüfung sicher gestellt hast, das alles so ist, wie Du es brauchst, de-kommentierst (also entfernst das Doppelkreuz) Du die vorletzte Zeile mit dem mv, um dann tatsächlich die Umbenennung "scharfzuschalten".
 
Zuletzt bearbeitet:
Ich wollte mich nochmal für alle Beiträge hier bedanken! Da stecken sehr viele Informationen drin, mit denen ich mich mal näher beschäftigen muss, alleine sed ist für mich schon wieder eine Wissenschaft für sich, aber es ist natürlich unerlässlich, wenn man sich mit Shellscripting beschäftigen will.
In dem ganzen Zusammenhang wollte ich noch fragen, ob es auch ein CLI-Programm gibt, um einfach eventuell vorhandene Tags von mp3's innerhalb eines Ordners zu entfernen? Bisher mache ich das mit easytag und da kann man dann die Ordner erstmal zum Anzeigen markieren und falls Tags vorhanden sind, die dann durch irgendeine Funktion entfernen.
Einfacher wäre natürlich ein kleines CLI-Programm, welches nichts anderes tut, als Tags zu entfernen, falls vorhanden.
 
Zurück
Oben