ForumBash Rename Skript
Andi Latte – Samstag, 12. Januar 2008 12:34 Uhr

Also erstmal Hallo!

Aus gegebenen Anlass habe ich versucht ein Skript (mein erstes), welches rekursiv Dateien und Ordner umbenennt zu schreiben.

Begonnen hat es mit einem Skript welches ich in einem Forum gefunden habe, dieses habe ich versucht rekursiv zu machen, und um meine regular expressions zu erweitern.

Ca. 20 regular expressions werden angewendet.

Soweit so gut

Das Skript funktioniert nun zwar, aber leider nicht so richtig:

Für jede Ebene die ein Leerzeichen enthält muss ich das Skript einmal ausführen.

Und die Performance ist alles andere als toll, es funktioniert zwar aber sehr langsam. (ca. 1 min/GB im Testdurchlauf)

Ich hoffe ich kann mein Skript hier irgendwie hochladen, und irgendjemand kann mir Verbesserungsvorschläge geben.

Wie schon erwähnt es ist mein erstes Skript überhaupt, also bitte nicht wundern falls etwas formhalber oder so nicht ganz richtig ist.

Ich hoffe es kann sich nun jemand für dieses Skript begeistern und mir eventuell ein bisschen helfen.

Danke

Harald

PS: Ich habs nun irgendwie nicht geschafft meine 2 Dateien hochzuladen.

Soll ichs lieber als Klartext hier reinschreiben?

3 Antworten
Andi Latte – Samstag, 12. Januar 2008 15:03 Uhr

Leider hat mir auf meine Frage keine geantwortet nun habe ich das Skript einfachmal mit Zeilenumbrüchen hier reingestellt, falls ich irgendetwas dabei falsch gemacht habe, weist mich bitte darauf hin

#!/bin/bash
############################################################
##################
#
# Normieren von Datei und Verzeichnissnamen
#
# v0.1 : erste Version
# v0.2 : Bugfix Verzeichnisse mit Leerzeichen
# v0.3 : Mehr Kommentare
# Config Sektion
# Fehlerhafte Warung, wenn Datei _nicht_ umbenannt wurde entfernt
# v0.4 : rekursiv
############################################################
##################
############################################################
##################
# Config
############################################################
##################
VERBOSE=0
DEBUG=0
DRY=0
# Welchen mode sollen die Dateien haben
FILE_MODE=”644″ # rw-r–r–
DIR_MODE=”755″ # rwxr-xr-x

fPATH=”/PATH/TO/fpath.sed”
############################################################
##################
# Kommandozeilenparameter
############################################################
##################
case “$1” in
-v|–verbose)
VERBOSE=1
;;
-d|–debug)
DEBUG=1
VERBOSE=1
;;
-t|–dry-run|–simulate|–test)
DRY=1
VERBOSE=1
;;
-?|-h|–help)
echo “usage: sane_name.sh [-v|-d|-?|-h|-t|–help|–verbose|–debug|–dry-run|–simulate] PATH ## ONE OPTION -v,-d,–dry-run must be set ##”
echo “-v –verbose : be verbose”
echo “-d –debug : debug mode”
echo “-t –test –dry-run : dont actually rename anything”
echo “-? -h –help : this message”
exit 0
;;
esac

d=$(find “$2” -depth | “$fPATH”)
dateien=$(find “$2” -depth -type f) # alle Dateinamen in eine Variable
while [ “$dateien” != “” ] # Solange wir welche übrig haben
do
datei=$(echo “$dateien” | head -n1) # nehmen wir die erste
# tu was immer du mit $datei tun willst, z.B.:
a=`echo “$datei”`
c=`echo “$datei” | “$fPATH” ` ## den Pfad der Datei anzeigen (ohne der datei selbst)

[ “$VERBOSE” -eq 1 ] && echo “[$a]”

# test if file is ok to process:
# is it a regular file ?
if [ ! -f “$a” ] && [ ! -d “$a” ] ;
then
echo “skipping $a cause it is not a regular file.”
exit ;
continue ;
fi

# normalize mode for files
if [ -f “$a” ] ;
then
[ “$VERBOSE” -eq 1 ] && echo “normalize mode to $FILE_MODE”
[ “$DRY” -eq 0 ] && chmod $FILE_MODE “$a”
fi

# normalize mode for dirs
if [ -d “$a” ] ;
then
[ “$VERBOSE” -eq 1 ] && echo “normalize mode to $DIR_MODE”
[ “$DRY” -eq 0 ] && chmod $DIR_MODE “$a”
fi
dateialt=$(echo “$datei” )

xPath=`echo “$c” | sed -e ‘s/\ /\\ /g’`

a=`echo “$datei” | sed ‘s/.*\///’` # a=Datei ohne Pfad
b=`echo $a | \
tr “‘” “_” |\
tr ‘!@#+ ‘ ‘_____’ |\
tr ‘[]{}’ ‘()()()’ |\
sed ‘s/[\`\´\”\]//g’ |\
sed ‘s/\&/and/g’`

[ “$DEBUG” -eq 1 ] && echo “mapping chars: [$b]”

# Dateiendungen anpassen

b=`echo $b | sed -e ‘s/\.MP3$\|.mP3$\|.Mp3$/\.mp3/’`
b=`echo $b | sed -e ‘s/\.jpeg$\|.jpe$/\.jpg/’`

[ “$DEBUG” -eq 1 ] && echo “normalize extensions: [$b]”

# Unerwünschte zeichen ersetzen oder entfernen
b=`echo $b | sed -e ‘s/[àáâãäå]/a/g’`
b=`echo $b | sed -e ‘s/[àáâãäå]/a/g’`
b=`echo $b | sed -e ‘s/[ÀÁÂÃÄÅ]/A/g’`
b=`echo $b | sed -e ‘s/[éëêè]/e/g’`
b=`echo $b | sed -e ‘s/[ÈÉÊË€]/E/g’`
b=`echo $b | sed -e ‘s/[ìíîï]/i/g’`
b=`echo $b | sed -e ‘s/[ÌÍÎÏ¡]/I/g’`
b=`echo $b | sed -e ‘s/[òóôõöø]/o/g’`
b=`echo $b | sed -e ‘s/[ÒÓÔÕÖ]/O/g’`
b=`echo $b | sed -e ‘s/[ùúûü]/u/g’`
b=`echo $b | sed -e ‘s/[ÙÚÛÜ]/U/g’`
b=`echo $b | sed -e ‘s/[ÿý?]/y/g’`
b=`echo $b | sed -e ‘s/[ÝŸ]/Y/g’`
b=`echo $b | sed -e ‘s/[æ]/ae/g’`
b=`echo $b | sed -e ‘s/[Æ]/AE/g’`
b=`echo $b | sed -e ‘s/[碩]/c/g’`
b=`echo $b | sed -e ‘s/[Ç]/C/g’`
b=`echo $b | sed -e ‘s/[ßš]/s/g’`
b=`echo $b | sed -e ‘s/[$§Š]/S/g’`
b=`echo $b | sed -e ‘s[=¦#:~*¿·¸°¯\%”;+^˜‰…‡†???•]/_/g’`
b=`echo $b | sed -e ‘s/[²]/2/g’`
b=`echo $b | sed -e ‘s/[³]/3/g’`

# Durch Ersetzung entstandene doppelte ‘_’ entfernen
# b=`echo $b | tr -s ‘_’ ‘_’`

# Alle Zeichen die Nicht A-Z a-z 0-9 . SPACE _ – ( ) sind entfernen
b=`echo $b | sed -e ‘s/[^\(\)\.\/\_a-zA-Z0-9-]//g’`
C=$(echo $c | sed ‘s/\ /\\ /g’)
x=`echo “$C””$b”`

# DO IT: Datei umbenennen
if [ ! -e $b ] ;
then
[ “$VERBOSE” -eq 1 ] && echo “new name: $x”
[ “$DRY” -eq 0 ] && mv -i “$dateialt” $x
else
if [ $datei = $x ] ;
then
echo “Skipping, $a unchanged”
else
echo “Warning: Skipping, $a new name $b allready exists”
fi
fi

dateien=$(echo “$dateien” | sed 1d)
done

fpath.sed:

#! /bin/sed -f

# usage: find path -print | fdirname
#
# fdirname acts like dirname, but read files from stdin

# print the directory component of a path

# special case: `/’ is given
/^\/$/c\
/

# strip trailing `/’s if any
s/\/*$//
# strip trailing filename
s/[^/]*$//

# if get no chars after these, then we have current dir (things like
# `bin/ src/’ were given
/./!c\
.

# delete the trailing `/’
# (“/usr/bin/ls” –> “/usr/bin/”, this makes “/usr/bin”)
#s/\/$//

Andi Latte – Samstag, 12. Januar 2008 15:11 Uhr

Ähm Sorry, für den 3. Beitrag von mir (hätte ich auch alles in einen packen können):

Zur Erklärung:

Ich splitte mit den Pfad (wie Ihr eventuell schon bemerkt habt) auf, um mit Leerzeichen im Pfad umgehen zu können.

Daher: Es wird nur der letzte Teil des Pfades (also alles nach dem letzten “/”) mit den Sed Regeln verändert, und anschliessend der original Pfad wieder davor gesetzt, und das für jede Ebene.

Lars S. – Sonntag, 13. Januar 2008 13:39 Uhr

Um mit Leerzeichen im Pfad und in den Dateinamen umgehen zu können
gibt es doch den “Infield Separator”.

#!/bin/bash
IFS=”

[..Rest des Scripts..]

Andi Latte – Sonntag, 13. Januar 2008 15:56 Uhr

danke für den tip!

Ich verstehe ihn nur nicht ganz.

Könntest du mir das mal bitte ganz kurz erklären?

Lars S. – Sonntag, 13. Januar 2008 22:45 Uhr

Wie du ja schon festgestellt hast hat die Bash “Probleme” mit Leerzeichen
in Datei- und Verzeichnisnamen.
Durch die Anweisung
IFS=”

wird der sog. ‘Infield Separator’ auf den Zeichenwert “Newline” gesetzt.
Das heißt, daß in Scriptverarbeitung das Zeilenende durch einen
Zeilenvorschub gekennzeichnet wird und nicht (wie Standard) auch durch
Leerzeichen oder Tab.
Somit stellen Leerzeichen, Tabs etc. in Dateinamen kein Problem bei der
Scriptverarbeitung mehr dar.
Siehe auch ‘man bash’ => IFS

Andi Latte – Montag, 14. Januar 2008 10:18 Uhr

Vielen Vielen Dank.

Werde ich gleichmal ausprobieren.

Und das Ergebnis dann hier posten.

Vielleicht interessiert dieses Skript ja auch andere Leute

Lars S. – Montag, 14. Januar 2008 10:35 Uhr

Ich würde mir dein fertiges Produkt gerne mal ansehen.
Und hier zur Verdeutlichung mal ein kleines Beispielscript um zu zeigen was
IFS
bewirkt:

#!/bin/bash
IFS=”

mkfifo audiodump.wav
for f in `ls *.ogg`
do
lame -q2 -V 5 –vbr-new ./audiodump.wav ./${f%.ogg}.mp3 &
mplayer $f -vc dummy -vo null -ao pcm:file=./audiodump.wav >/dev/null
done
rm ./audiodump.wav

Dieses Script konvertiert für mich ein ganzes Verzeichnis Ogg-Audio nach
MP3 damit ich die auf meinem MP3-Player (ohne Ogg-Unterstützung)
abspielen kann.
Ohne IFS werden keine Dateien bearbeitet die ein Leerzeichen enthalten.
Durch die seltsam aussehende IFS-Anweisung arbeitet das Script auch
Deiteinamen mit Leerzeichen ohne Probleme ab.
Ich hoffe die Wirkungsweise ist nun klar.

Lars S. – Montag, 14. Januar 2008 10:35 Uhr

Ich würde mir dein fertiges Produkt gerne mal ansehen.
Und hier zur Verdeutlichung mal ein kleines Beispielscript um zu zeigen was
IFS
bewirkt:

#!/bin/bash
IFS=”

mkfifo audiodump.wav
for f in `ls *.ogg`
do
lame -q2 -V 5 –vbr-new ./audiodump.wav ./${f%.ogg}.mp3 &
mplayer $f -vc dummy -vo null -ao pcm:file=./audiodump.wav >/dev/null
done
rm ./audiodump.wav

Dieses Script konvertiert für mich ein ganzes Verzeichnis Ogg-Audio nach
MP3 damit ich die auf meinem MP3-Player (ohne Ogg-Unterstützung)
abspielen kann.
Ohne IFS werden keine Dateien bearbeitet die ein Leerzeichen enthalten.
Durch die seltsam aussehende IFS-Anweisung arbeitet das Script auch
Deiteinamen mit Leerzeichen ohne Probleme ab.
Ich hoffe die Wirkungsweise ist nun klar.

Andi Latte – Montag, 14. Januar 2008 13:01 Uhr

Ja super!

Also ich sitze im Moment leider vor keinem Linux drum kann ichs nun auch nicht testen. Werde ich aber später natürlich nachholen.

Eigentlich müsste das nun schon ein wenig Performance bringe, da das Skript nun einige Schritte weniger benötigt.

Nun hätte ich übrigens noch eine Erweiterung als Wunsch:

Alles “_-_” die vorher schon im Dateinamen waren, sollen bleiben .. der rest gehört mit “-” ersetzt.

Wie schreibe ich das am Besten?

PS: Wie oben schon erwähnt dieses ist mein 1. Skript, darum all diese Fragen, habe da noch nicht soviel Erfahrungen damit.

Andi Latte – Dienstag, 15. Januar 2008 13:22 Uhr

Ich habe gestern Nacht das Skript mal wieder komplett umgeschrieben!

Und es ist nun ca. 30mal so schnell als vorhin

Das einzige was mir nun noch Fehlt, ist eben die Abfrage ob ein “_-_” schon vor dem Skript im Dateinamen war, dann gehört es behalten, ansonstn mit einem “-” ersetzt?!

HIER MEIN NEUES SKRIPT:

###########################################################################

#!/bin/bash
# ersetzt böhse Zeichen im dateinamen rekursiv
##############################################

if [ -z $1 ];then echo Verwendung: clean-rename “/PFAD” ; exit 0;fi

find “$1” -depth -name “*” | while read file ; do
directory=$(dirname “$file”)
oldfilename=$(basename “$file”)
newfilename=$(echo “$oldfilename” | tr “‘” “_” | tr ‘!@#+ ‘ ‘_____’ | tr ‘[]{}’ ‘()()()’ | sed -e ‘s/[\`\´\”\]//g’ | sed -e ‘s/\&/and/g’ | sed -e ‘s/[àáâãäå]/a/g;s/[àáâãäå]/a/g;s/[ÀÁÂÃÄÅ]/A/g;s/[éëêè]/e/g;s/[ÈÉÊË€]/E/g;s/[ìíîï]/i/g;s/[ÌÍÎÏ¡]/I/g;s/[òóôõöø]/o/g;s/[ÒÓÔÕÖ]/O/g;s/[ùúûü]/u/g;s/[ÙÚÛÜ]/U/g;s/[ÿý?]/y/g;s/[ÝŸ]/Y/g;s/[æ]/ae/g;s/[Æ]/AE/g;s/[碩]/c/g;s/[Ç]/C/g;s/[ßš]/s/g;s/[$§Š]/S/g;s/[=¦#:~*¿·¸°¯\%”;+^˜‰…‡†???•]/_/g;s/[²]/2/g;s/[³]/3/g’ | sed -e ‘s/[^\(\)\.\/\_a-zA-Z0-9-]//g’ )
if [ “$oldfilename” != “$newfilename” ]; then
mv -i “$directory/$oldfilename” “$directory/$newfilename”
echo “”$directory/$oldfilename” —> “$directory/$newfilename””
#echo “$directory”
#echo “$oldfilename”
#echo “$newfilename”
#echo
fi
done
exit 0

###########################################################################

Andi Latte – Mittwoch, 16. Januar 2008 02:38 Uhr

Heute habe ich die Lösung für mein Problem (_-_) gefunden:

if [[ “$oldfilename” != *_-_* ]]; then
newfilename=$(echo “$newfilename” | sed ‘s/_-_/-/g’)
fi

rainer glauben – Montag, 14. Januar 2008 17:26 Uhr

Hi

also erstmal viel Erfolg mit deinem Script .. ich möchte dich auch nicht vom Scripting abhalten, möchte nur nochmals darauf hinweisen, dass es zum umbenennen auf der Kommandozeile das gute alte, aber verdammt pfiffige “mmv” gibt.
Mann kann in den Namen ziemlich alles durch ziemlich alles ersetzen und kommt auch in Unterverzeichnisse.

[1] http://www.linuxtopia.org/online_books/gnu_linux_tools_guide/mass-rename.html
[2] http://linux.die.net/man/1/mmv

Andi Latte – Montag, 14. Januar 2008 17:40 Uhr

Danke!

natürlich habe ich mir rename (das perl skript) und mmv angesehen bevor ich mit dem skript begonnen habe, aber irgendwie hab ich es nicht geschafft alle regular expressions in einem Befehl zusammen zufassen.

Wenn mir jemand dabei helfen kann, hab ich auch nichts dagegen.

Einerseits geht es zwar auch darum ein bisschen was zu lernen,

primär geht es mir aber darum die Aufgabe möglichst effizient und performant zu lösen!

Andi Latte – Mittwoch, 16. Januar 2008 02:51 Uhr

Das neue voll funktionstüchtige Skript:

########################################################################################
#!/bin/bash
# ersetzt böhse Zeichen im dateinamen rekursiv
##############################################

if [ -z $1 ];then echo Verwendung: clean-rename “/PFAD” ; exit 0;fi

find “$1” -depth -name “*” | while read file ; do
directory=$(dirname “$file”)
oldfilename=$(basename “$file”)
newfilename=$(echo “$oldfilename” | tr “‘” “_” | tr ‘!@#+ ‘ ‘_____’ | tr ‘[]{}’ ‘()()()’ | sed -e ‘s/[\`\´\”\]//g’ | sed -e ‘s/\&/and/g’ | sed -e
‘s/[àáâãäå]/a/g;s/[àáâãäå]/a/g;s/[ÀÁÂÃÄÅ]/A/g;s/[éëêè]/e/g;s/[ÈÉÊË€]/E/g;s/[ìíîï]/i/g;s/[ÌÍÎÏ¡]/I/g;s/[òóôõöø]/o/g;s/[ÒÓÔÕÖ]/O/g;s/[ùúûü]/u/g;s/[ÙÚÛÜ]/U/g;s/[ÿý?]/y/g;s/[Ýÿ]/Y/g;s/[æ]/ae/g;s/[Æ]/AE/g;s/[碩]/c/g;s/[Ç]/C/g;s/[ßš]/s/g;s/[$§Š]/S/g;s/[=¦#:~*¿·¸°¯\%”;+^?‰ †???•]/_/g;s/[²]/2/g;s/[³]/3/g’
| sed -e ‘s/[^\(\)\.\/\_a-zA-Z0-9-]//g’ )

## Jedes “_-_” welches schon vorher im Dateinamen war bleibt erhalten ##

if [[ “$oldfilename” != *_-_* ]]; then
newfilename=$(echo “$newfilename” | sed ‘s/_-_/-/g’)
fi

if [ “$oldfilename” != “$newfilename” ]; then
mv -i “$directory/$oldfilename” “$directory/$newfilename”
echo “”$directory/$oldfilename” —> “$directory/$newfilename””
fi

done
exit 0
########################################################################################

Nochmals Vielen Dank an Alle die mir geholfen haben!