ubuntuusers.de

Verzeichnis rekursiv Sonderzeichen entfernen und Dateinamen kürzen

Status: Ungelöst | Ubuntu-Version: Kubuntu 22.04 (Jammy Jellyfish)
Antworten |

dafosy

Anmeldungsdatum:
26. Februar 2012

Beiträge: 164

Ich weiß,

ein leidiges und oft gespieltes Thema.

Ich möchte einen Ordner mit allen Unterordners und Dateinamen durchskripten und alles von Sonderzeichen, Leerzeichen befreien. Und dann soll alles auch noch auf max. 128 Zeichen begrenzt werden oder 64, da muss ich mir mal anschauen, wie das aussieht.

Folgendes ist jetzt mein Ansatz:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
#24.01.2024

verz=/media/DATA950-B/98_20240124_Testumgebung_Umbenennen


for i in $(seq 1 10) 
do 

        find $verz/. -maxdepth $i -exec rename -n 's/ü/ue/g; s/Ü/Ue/g; s/ä/ae/g; s/Ä/Ae/g; s/ö/oe/g; s/Ö/Oe/g; s/ß/ss/g; s/\ /_/g; s/#/_/g' {} \;

        # rename -n ... nur Test
        # rename -v ... vebose - zeige alle

done

cd $verz
for altname in *; 
do 
        neuname=$(echo $altname | cut -c -128); echo $neuname 
done

#scharf: mv $altname $neuname 
#unscharf: echo $neuname

Bei der Cut-Nummer habe ich noch Probleme. Diese läuft nämlich erstmal nur im oberen Verzeichnis und nicht rekursiv absteigend. Kann ich das nicht auch mit in die obige erste for-Schleife einbauen? Fände das galanter.

Grüße /dafosy

wxpte

Anmeldungsdatum:
20. Januar 2007

Beiträge: 1388

Ich frage mich gerade, was du mit der for-Schleife überhaupt bewirken willst. Mit dem Befehl

1
find $verz/. -maxdepth i0 -exec rename -n 's/ü/ue/g; s/Ü/Ue/g; s/ä/ae/g; s/Ä/Ae/g; s/ö/oe/g; s/Ö/Oe/g; s/ß/ss/g; s/\ /_/g; s/#/_/g' {} \;

müsste eigentlich alles abgedeckt sein. Wozu du zusätzlich -maxdepth 1 bis -maxdepth 9 benötigst, ist mir ein Rätsel.

Aufgefallen ist mir auch, dass du in Zeile 18 ein Semikolon gesetzt, das do aber dennoch in die nächste Zeile geschrieben hast. Das ist redundant und führt schlimmstenfalls zu einem Fehler.

Bei deinem Vorhaben würde ich aber zunächst das Ergebnis von find in eine Datei schreiben lassen:

1
find $verz/. -maxdepth i0 > Umbenennungen.lst

und danach sowohl die Ersetzung der Sonderzeichen als auch das Trimmen auf 128 Zeichen über eine while read-Schleife abarbeiten. Getestet habe ich das aber noch nicht.

Weiterer Hinweis: Du müsstest dir auch sicher sein, dass es nicht mehrere Dateien gibt, die bis einschließlich des 128. Zeichens identisch sind (sonst wird die zuerst umbenannte Datei mit der später umbenannten überschrieben).

Nachtrag: Noch zwei Dinge:

Erstens:

neuname=$(echo $altname | cut -c -128); echo $neuname

Damit wird aber nur der gekürzte Name auf der Standardausgabe angezeigt, umbenannt wird hier noch nichts.

Zweitens:
Da find auch den kompletten Pfad mit anzeigt, ist das mit dem Kürzen nicht ganz so einfach. Hier müsstest du zunächst mit basename den reinen Dateinamen isolieren. Mit dirname isolierst du dann den Pfad. Beides hinterlegst du in einer Variablen. Beim Umbenennen müsstest du dann beides wieder zusammensetzen, dann bekommst du

Pfad/zur/Datei/128-Zeichen-Neuname

.

CarstenHa

Avatar von CarstenHa

Anmeldungsdatum:
1. Mai 2020

Beiträge: 137

Das Kürzen der Namen könnte man in etwa so lösen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/bash

check() {
 dname="$(dirname "$datei")"
 oldname="$(basename "$datei")"
 fname="${oldname%.*}"
 fsuffix="$(sed -n 's/.*\(\..*\)/\1/p' <<<"$oldname")"
 # Hier die Anzahl der Zeichen eintragen:
 newname="$(printf "%.128s\n" "${fname}")"

 if [ "$oldname" == "${newname}${fsuffix}" ]; then
  echo "Name ${datei} ist passend."
 elif [ -e "$datei" -a ! -e "${dname}/${newname}${fsuffix}" -o \
        -d "$datei" -a ! -d "${dname}/${newname}${fsuffix}" ]; then
  mv -v "${datei}" "${dname}/${newname}${fsuffix}"
  #echo "${datei} ${dname}/${newname}${fsuffix}"
 else
  echo "${datei} konnte nicht in ${dname}/${newname}${fsuffix} umbenannt werden. Existiert bereits."
 fi
}

echo "Ordner werden geprüft ..."
dirlist="$(find . -type d)"
while read -r datei; do
 check
done <<<"$dirlist"

echo "Dateien werden geprüft ..."
dateilist="$(find . -type f)"
while read -r datei; do
 check
done <<<"$dateilist"

Aber bitte unbedingt vorher bei Dir testen, indem du '#' vom mv/echo-Befehl in der Funktion check austauschst.

Gruß

Carsten

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17593

Wohnort: Berlin

Wenn Du ein Verzeichnis ä mit einer Datei ü hast, dann wird bei Dir erst ä umbenannt, und ä/ü kann nicht mehr umbenannt werden, weil sie jetzt a/ü heißt, vermute ich.

Dafür hat find den Schalter -depth, dann werden erst die Dateien IN den Verzeichnissen, und danach die Verzeichnisse umbenannt.

Ah - jetzt verstehe ich erst, dass Du dafür die For-Schleife mit dem -maxdepth hast. Sorry, aber interessiert wahrscheinlich trotzdem.

CarstenHa

Avatar von CarstenHa

Anmeldungsdatum:
1. Mai 2020

Beiträge: 137

Nachtrag:

1
dirlist="$(find . -type d)"

Besser ist (Damit erst die Unterverzeichnisse geändert werden):

1
dirlist="$(find . -type d | sort -r)"

Gruß

Carsten

dafosy

(Themenstarter)

Anmeldungsdatum:
26. Februar 2012

Beiträge: 164

Hallo ihr,

@wxpte die for-Schleife mit maxdepth habe ich für den Zweck, dass wenn der Ordnername geändert wird und erst danach der Inhalt, es zu Fehlern kommt, da der ursprüngliche Ordnername bei Skriptstart sich geändert hat.

und

echo $neuname

ist tatsächlich erstmal Test

dafosy

(Themenstarter)

Anmeldungsdatum:
26. Februar 2012

Beiträge: 164

Die cut-Nummer ist tatsächlich doch ein größeres Problem. Nun, alle meine Windows-Maschinen können jeweils die dortigen Nutzerverzeichnisse auf dem Linux-Server beschreiben und nutzen.

Für Backup-Zwecke soll also die Dateinamen-Zeichenlänge gekürzt werden. Eigentlich ist das aber nicht notwendig, da Windows ja eher die 256 Zeichen-Grenze vorgibt. Linux sollte mehr können, oder?

Ordner sind ja in aller Regel keine 64 Zeichen lang, bzw. könnte ich das ja als Vorgabe rausgeben. Problematisch ist es ja eher mit zugesendeten Dateien oder mp3s, die manche Software selbst vergeben.

Wäre es alternativ nicht auch denkbar, dass ich mir per 'ls' und 'sed' alles bis zum letzten "/" wegkürze und mir dann nur alle Dateinamen im log ausgeben lasse, die länger als meine Anzahl Zeichen sind?

/dafosy

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13121

CarstenHa schrieb:

1
dirlist="$(find . -type d)"

Generell nicht so eine gute Idee: wenn es Verzeichnisse mit Leerzeichen oder Zeilenumbrüchen gibt, funktioniert das nicht. Besser einen Ansatz mit find direkt nehmen, wie z.B. der hier, nur, dass man -depth nutzen sollte.

Besser ist (Damit erst die Unterverzeichnisse geändert werden):

1
dirlist="$(find . -type d | sort -r)"

Dafür kann man einfacher -depth nehmen.

CarstenHa

Avatar von CarstenHa

Anmeldungsdatum:
1. Mai 2020

Beiträge: 137

@rklm Ja, manchmal sehe ich den Wald vor lauter Bäumen nicht 😉

Gruß

Carsten

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6463

Wohnort: Hamburg

... 256 Zeichen-Grenze vorgibt. Linux sollte mehr können, oder?

Auf meinem PC:

@samurai:~$ getconf  PATH_MAX /
4096
@samurai:~$ getconf  NAME_MAX /
255

Dabei ist allerdings zu berücksichtigen, dass einige Dateisysteme kleinere Werte haben. Daher das "/" Zeichen für den Pfadnamen.

CarstenHa

Avatar von CarstenHa

Anmeldungsdatum:
1. Mai 2020

Beiträge: 137

Hallo,

Das müsste funktionieren:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash

cd /media/DATA950-B/98_20240124_Testumgebung_Umbenennen

find . -depth -maxdepth 10 -execdir rename 's/ü/ue/g; s/Ü/Ue/g; s/ä/ae/g; s/Ä/Ae/g; s/ö/oe/g; s/Ö/Oe/g; s/ß/ss/g; s/\ /_/g; s/#/_/g' {} \;

# Verzeichnisse werden umbenannt wenn größer als 128 Zeichen.
# Wenn Name schon vorhanden ist, wird nicht umbenannt.
find . -depth -maxdepth 10 -type d -execdir rename 's/^\.\/(.{,128}).*/$1/' {} \;

# Dateinamen werden umbenannt wenn größer als 128 Zeichen. Dateiendungen bleiben erhalten. Auch z.B. .tar.gz
# Wenn Name schon vorhanden ist, wird nicht umbenannt.
find . -depth -maxdepth 10 -type f -execdir rename 's/^\.\/([^\.]{,128})[^\.]*(\..*)/$1$2/' {} \;

Für einen dry-run solltest du aber auf alle Fälle alle rename-Befehle zunächst mit der Option -n ausstatten. Nicht, das da ungewollte Änderungen gemacht werden.

Gruß

Carsten

dafosy

(Themenstarter)

Anmeldungsdatum:
26. Februar 2012

Beiträge: 164

@all:

starke Herleitung und interessantes Ergebnis. Ich teste gleich mal.

dafosy

(Themenstarter)

Anmeldungsdatum:
26. Februar 2012

Beiträge: 164

Welcher Schalter bei 'rename' verhindert denn das nicht umbenennen, wenn schon vorhanden?

# Wenn Name schon vorhanden ist, wird nicht umbenannt.

dafosy

(Themenstarter)

Anmeldungsdatum:
26. Februar 2012

Beiträge: 164

@CarstenHA

Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/^\./(.{ <-- HERE ,128}).*/ at (user-supplied code).

CarstenHa

Avatar von CarstenHa

Anmeldungsdatum:
1. Mai 2020

Beiträge: 137

dafosy schrieb:

@CarstenHA

Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/^\./(.{ <-- HERE ,128}).*/ at (user-supplied code).

Dann versuch mal in Zeile 9 und 13 das {,128} durch \{,128} zu ersetzen.

Antworten |