seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11176
Wohnort: München
|
Perl und UTF-8 macht immer Spaß... Erst mal liefert echo immer einen Zeilenumbruch mit (es sei denn man ruft es mit dem Argument -n auf) und dann muss man Perl noch dazu bringen UTF-8 encodierte Zeichen anzunehmen statt mit einzelnen Bytes zu arbeiten.
Ich denke Perl sieht erst mal drei Bytes (ein "ö" ist eine 246 und in UTF-8 damit zwei Bytes breit, da größer als 127) und das Byte für den Zeilenumbruch '\n' hat den Wert 0xa = 10. Nimmt man den Zeilenumbruch raus, siehst man, dass nur noch zwei Bytes ankommen: $ echo -n "$a" | perl -pe 's/[^a-z0-9]/-/g;'
--$
Und wenn man Perl dann noch sagt, dass es mit UTF-8 encodierten Strings arbeiten soll (hier wird das erklärt: http://www.perl.com/pub/2012/04/perlunicook-decode-standard-filehandles-as-utf-8.html ), sieht es nur noch ein Byte:
$ echo -n "$a" | perl -CS -pe 's/[^a-z0-9]/-/g;'
-$
Und mit angehängtem Zeilenumbruch: $ echo -n "$a" | perl -CS -pe 's/[^:alnum:]/-/g;s/^(.*)$/$1\n/g'
- Wenn man im Regex auch UTF-8 Zeichen nutzen will, braucht man noch das Modul utf8: $ echo -n "$a" | perl -CS -Mutf8 -pe 's/[^ö]/-/g;s/^(.*)$/$1\n/g'
ö
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Da hast Du gleich 2 Besonderheiten von Perl am Bein ... 😀
speichert Perl die gelesene Zeile intern grundsätzlich mit dem \n am Ende, im Gegensatz zu sed. Und da dieses "\n" auch ein Zeichen ist, das "nicht a-z und nicht 0-9 ist", wird es in ein "-" umgewandelt. arbeitet Perl erstmal stumpf mit Bytes, es sei denn, Du sagst ihm ausdrücklich was anderes. Schau Dir die Bytes mal im Hexdump an: (denk daran: Sonderzeichen sind in utf-8 immer Mehrbyte-Zeichen !) track@lucid:~$ echo "ö" | hd
00000000 c3 b6 0a |...| Jedes dieser 3 Bytes ist "nicht a-z und nicht 0-9 ist", wird also umgewandelt, auch das "\n" am Ende, so dass der Zeilenumbruch hinterher fehlt.
LG, track Edit: Ups, den Beitrag von Seahawk hatte ich gar nicht gesehen ... er hat es ja noch viel besser erklärt !
|
uuuser1
(Themenstarter)
Anmeldungsdatum: 28. April 2011
Beiträge: 257
|
Danke euch beiden für die Erklärung. Mittlerweile bin ich bei folgender Zeile angelangt: | find . -type f -exec rename -n -v 's/(.*)/\L\1/;s/ä/a/g;s/ö/o/g;s/ü/u/g;s/ß/ss/g;s/[^a-z0-9.\/-]/-/g;s/-+/-/g;s/\.+/./g;' '{}' \;
|
Natürlich findet find derzeit auch versteckte Ordner und würde somit (wenn man es von ~ ausführt) auch die Konfigurationsdateien überschreiben, was nicht gewünscht ist. Von daher brauche ich Hilfe, dass find keine versteckten Dateien aufruft (egal von wo aus man die Zeile aufruft)! (Das Manual habe ich mal überflogen, aber nichts direkt gefunden... auch Recherche im Netz hat mir nicht geholfen. Aber das liegt wohl eher an mir...) Des weiteren wäre es schön, wenn ein Minus am Anfang entfernt wird und auch Minusse, die direkt vor einem Punkt stehen, zB: -testdatei1-.mp3 soll zu testdatei1.mp3 werden. Zumindest das -. sollte doch durch: wegfallen oder? PS: Ein Hauptproblem ist auch, dass er anscheinend nicht damit zurecht kommt, dass find eine Datei findet, dann dadurch auch den Ordnername verändert (wenn dieser durch die RegEx läuft) und dann die darauffolgenden Dateien nicht mehr geändert werden können, weil der Verzeichnisname geändert wurde... wie kann man dieses Problem umgehen?
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
uuuser1 schrieb: Von daher brauche ich Hilfe, dass find keine versteckten Dateien aufruft (egal von wo aus man die Zeile aufruft)! (Das Manual habe ich mal überflogen, aber nichts direkt gefunden... auch Recherche im Netz hat mir nicht geholfen. Aber das liegt wohl eher an mir...)
überspringt Konfigurationsdateien.
Des weiteren wäre es schön, wenn ein Minus am Anfang entfernt wird und auch Minusse, die direkt vor einem Punkt stehen, zB: -testdatei1-.mp3 soll zu testdatei1.mp3 werden. Zumindest das -. sollte doch durch:
Im Suchausdruck muss der Punkt markiert werden, sonst steht er für ein beliebiges Zeichen.
PS: Ein Hauptproblem ist auch, dass er anscheinend nicht damit zurecht kommt, dass find eine Datei findet, dann dadurch auch den Ordnername verändert (wenn dieser durch die RegEx läuft) und dann die darauffolgenden Dateien nicht mehr geändert werden können, weil der Verzeichnisname geändert wurde... wie kann man dieses Problem umgehen?
Mit find -depth sucht man erst in der Tiefe, ändert da, und dann macht es nichts aus.
| find . -depth -not -name ".*" -type f -exec rename -n -v 's/(.*)/\L\1/;s/ä/a/g;s/ö/o/g;s/ü/u/g;s/ß/ss/g;s/[^a-z0-9.\/-]/-/g;s/-+/-/g;s/\.+/./g;' '{}' \;
|
Ohne Gewähr - erst testen!
|
uuuser1
(Themenstarter)
Anmeldungsdatum: 28. April 2011
Beiträge: 257
|
user unknown schrieb:
überspringt Konfigurationsdateien.
Leider wird immer noch .XYZ gefunden. UPDATE: Mit
{{{#!code bash
find * -name '*.*'
}}}
geht es!
UPDATE
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11176
Wohnort: München
|
track schrieb: (denk daran: Sonderzeichen sind in utf-8 immer Mehrbyte-Zeichen !)
Genauer gesagt lassen sich alle ASCII-Zeichen (da sind ja auch einige Sonderzeichen dabei) in UTF-8 mit einem Byte darstellen, alles was einen Wert > 127 hat (nicht mehr mit 7 Bit darstellbar), benötigt mehrere Bytes.
|
uuuser1
(Themenstarter)
Anmeldungsdatum: 28. April 2011
Beiträge: 257
|
| find . -depth \( ! -regex '.*/\..*' \)
|
gefunden hier: https://askubuntu.com/questions/266179/how-to-exclude-ignore-hidden-files-and-directories-in-a-wildcard-embedded-find
schließt versteckte Ordner vollständig aus und wenn eine versteckte Datei in einem nicht versteckten Ordner ist, wird diese trotzdem auch ignoriert. Genauso wie ich es will! Das Problem mit Can't rename BLABLA BLABLA: Datei oder Verzeichnis nicht gefunden besteht weiterhin... TROTZ: -depth
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
Ja, dass auch versteckte Ordner ignoriert werden sollen habe ich überlesen. uuuser1 schrieb: Das Problem mit Can't rename BLABLA BLABLA: Datei oder Verzeichnis nicht gefunden besteht weiterhin... TROTZ: -depth
Wie sieht denn das Kommando jetzt insgesamt aus, wie der Dateiname einer Datei bei der es konkret scheitert. Interessant sicher auch zu lesen: find
|
uuuser1
(Themenstarter)
Anmeldungsdatum: 28. April 2011
Beiträge: 257
|
$ ls -R -a -h
.:
. file1.txt .file3-hidden.txt sub1 .sub3-hidden
.. file2_with_illegal_char.txt .file4_hidden_with_illegal_char.txt sub2_with_illegal_char .sub4_hidden_with_illegal_char
./sub1:
. .. file1.txt file2_with_illegal_char.txt .file3-hidden.txt .file4_hidden_with_illegal_char.txt
./sub2_with_illegal_char:
. .. file1.txt file2_with_illegal_char.txt .file3-hidden.txt .file4_hidden_with_illegal_char.txt
./.sub3-hidden:
. .. file1.txt file2_with_illegal_char.txt .file3-hidden.txt .file4_hidden_with_illegal_char.txt
./.sub4_hidden_with_illegal_char:
. .. file1.txt file2_with_illegal_char.txt .file3-hidden.txt .file4_hidden_with_illegal_char.txt
$ find . -depth \( ! -regex '.*/\..*' \) -exec rename -v 's/(.*)/\L\1/;s/ä/a/g;s/ö/o/g;s/ü/u/g;s/ß/ss/g;s/[^a-z0-9.\/-]/-/g;s/-+/-/g;s/\.+/./g;' '{}' \;
\1 better written as $1 at (eval 4) line 1.
\1 better written as $1 at (eval 4) line 1.
./file2_with_illegal_char.txt renamed as ./file2-with-illegal-char.txt
\1 better written as $1 at (eval 4) line 1.
\1 better written as $1 at (eval 4) line 1.
./sub1/file2_with_illegal_char.txt renamed as ./sub1/file2-with-illegal-char.txt
\1 better written as $1 at (eval 4) line 1.
\1 better written as $1 at (eval 4) line 1.
Can't rename ./sub2_with_illegal_char/file1.txt ./sub2-with-illegal-char/file1.txt: Datei oder Verzeichnis nicht gefunden
\1 better written as $1 at (eval 4) line 1.
Can't rename ./sub2_with_illegal_char/file2_with_illegal_char.txt ./sub2-with-illegal-char/file2-with-illegal-char.txt: Datei oder Verzeichnis nicht gefunden
\1 better written as $1 at (eval 4) line 1.
./sub2_with_illegal_char renamed as ./sub2-with-illegal-char
\1 better written as $1 at (eval 4) line 1.
$ ls -R -a -h
.:
. file1.txt .file3-hidden.txt sub1 .sub3-hidden
.. file2-with-illegal-char.txt .file4_hidden_with_illegal_char.txt sub2-with-illegal-char .sub4_hidden_with_illegal_char
./sub1:
. .. file1.txt file2-with-illegal-char.txt .file3-hidden.txt .file4_hidden_with_illegal_char.txt
./sub2-with-illegal-char:
. .. file1.txt file2_with_illegal_char.txt .file3-hidden.txt .file4_hidden_with_illegal_char.txt
./.sub3-hidden:
. .. file1.txt file2_with_illegal_char.txt .file3-hidden.txt .file4_hidden_with_illegal_char.txt
./.sub4_hidden_with_illegal_char:
. .. file1.txt file2_with_illegal_char.txt .file3-hidden.txt .file4_hidden_with_illegal_char.txt Vor allem file2_with_illegal_char.txt im Ausgangsordner sub2_with_illegal_char betrachten.
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Tja, da hat sich rename vermutlich selber selber ausgetrickst ... 😀 Ich gehe davon aus, dass (zumindest hinter den Kulissen) zuerst ./sub2_with_illegal_char renamed as ./sub2-with-illegal-char
wurde, so dass das Verzeichnis ./sub2_with_illegal_char danach tatsächlich nicht mehr gefunden wurde, weil es ja schon umbenannt worden war. Warum die Reihenfolge anders angezeigt wird, weiß ich allerdings auch nicht. Du kannst ja mal statt dem rename .... {} einfach ein echo {} anhängen, vielleicht sieht man dann etwas. LG, track
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
Ich würde mal "-execdir" statt "-exec" probieren.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
track schrieb: Ich gehe davon aus, dass (zumindest hinter den Kulissen) zuerst ./sub2_with_illegal_char renamed as ./sub2-with-illegal-char
wurde, so dass das Verzeichnis ./sub2_with_illegal_char danach tatsächlich nicht mehr gefunden wurde, weil es ja schon umbenannt worden war.
Nein, gerade nicht. Er versucht die Datei samt Verzeichnis in einem Rutsch umzubenennen. Erst wird in dem Verzeichnis, dann das Verzeichnis selbst umbenannt, wie -depth sagt.
|
uuuser1
(Themenstarter)
Anmeldungsdatum: 28. April 2011
Beiträge: 257
|
Danke! find . -depth \( ! -regex '.*/\..*' \) -execdir rename -v 's/(.*)/\L\1/;s/ä/a/g;s/ö/o/g;s/ü/u/g;s/ß/ss/g;s/[^a-z0-9.\/-]/-/g;s/-+/-/g;s/\.+/./g;s/-\././g;s/\.-/./g;s/-\././g;s/\.-/./g;s/^-//g;' '{}' \; Macht genau das, was ich will! Eine Erweiterung wünsche ich mir noch, die eigentlich trivial ist, aber wahrscheinlich nicht klappen wird. Zumindest weiß ich keine sinnvolle Lösung. Ich möchte alle Dateinamen auf eine Länge von maximal 80 Zeichen beschränken. Inklusive der Dateiendung. Und hier wird es schwierig. Ich habe mir zuallererst gedacht, nach 80 Zeichen schneidet man alles ab. Da geht dann natürlich die Dateiendung flöten. Also müsste man, beginnend am Dateiende, zurück zu dem ersten auffindbaren Punkt. Ab hier wird es knifflig. Wie kann ich denn von diesem Punkt aus bestimmen, wie viele Zeichen ich links von dem Punkt abschneiden muss, damit ich insg. auf 80 Zeichen komme? Ich wage es nicht, die Erweiterung davon auszusprechen. Was tut man denn, wenn die Datei mit tar.bz endet? Das tar dürfte dann ja nicht abgeschnitten werden.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | echo "Ich habe einen Satz. Alles über 20 ab" | sed -r 's/([^.]{0,20})[^.]+(\..*)/\1\2/'
Ich habe einen Sat. Alles über 20 ab
echo "Ich habe einen Satz. Alles .über .20 ab" | sed -r 's/([^.]{0,20})[^.]+(\..*)/\1\2/'
Ich habe einen Sat. Alles .über .20 ab
echo "Ich habe. einen Satz. Alles .über .20 ab" | sed -r 's/([^.]{0,20})[^.]+(\..*)/\1\2/'
Ich hab. einen Satz. Alles .über .20 ab
echo ".Ich habe. einen Satz. Alles .über .20 ab" | sed -r 's/([^.]{0,20})[^.]+(\..*)/\1\2/'
.Ich hab. einen Satz. Alles .über .20 ab
echo ".Ich habe einen Satz Alles über 20 ab" | sed -r 's/([^.]{0,20})[^.]+(\..*)/\1\2/'
.Ich habe einen Satz Alles über 20 ab
echo ".Ich habe einen Satz Alles über .20 ab" | sed -r 's/([^.]{0,20})[^.]+(\..*)/\1\2/'
.Ich habe einen Satz .20 ab
echo "Ich habe einen Satz Alles über 20 ab" | sed -r 's/([^.]{0,20})[^.]+(\..*)/\1\2/'
Ich habe einen Satz Alles über 20 ab
|
Der Sedbefehl ist immer gleich. Nimmm 0-20 Nichtpunkte (als Variable 1), gefolgt von weiteren Nichtpunkten (für den Müll), einem Punkt gefolgt von beliebigem als Var. 2 und gebe nur 1 und 2 aus. Statt [^.]+ muss es wohl [^.]* heißen, falls nach dem Punkt nichts wegwerfwertes kommt, aber ich teste jetzt nicht nochmal von vorne. Wenn es hinten länger wird, wird vorne nicht mehr abgeschnitten - vielleicht willst Du das, aber wenn es nur wenige Dateien betrifft ist es vielleicht leichter diese von Hand rauszufischen.
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
uuuser1 schrieb: Ich möchte alle Dateinamen auf eine Länge von maximal 80 Zeichen beschränken. .... Wie kann ich denn von diesem Punkt aus bestimmen, wie viele Zeichen ich links von dem Punkt abschneiden muss, damit ich insg. auf 80 Zeichen komme? Ich wage es nicht, die Erweiterung davon auszusprechen. Was tut man denn, wenn die Datei mit tar.bz endet? Das tar dürfte dann ja nicht abgeschnitten werden.
Grundsätzlich ist auch mit Regulären Ausdrücken eine Längenbegrenzung möglich: track@lucid:~$ echo 'xxxxxxxxx1xxxxxxxxx2xxxxxxxxx3xxxxxxxxx4xxxxxxxxx5xxxxxxxxx6xxxxxxxxx7xxxxxxxxx8xxxxxxxxx9.xyz.abc' | sed -r 's/(.{80}).*(\..*)/\1\2/'
xxxxxxxxx1xxxxxxxxx2xxxxxxxxx3xxxxxxxxx4xxxxxxxxx5xxxxxxxxx6xxxxxxxxx7xxxxxxxxx8.abc Allerdings ist insbesondere Deine Bedingung mit den doppelten Endungen schwierig einzugrenzen. Denn
wie soll das arme Programm wissen, wie viele Punkte und Endungen vorkommen ? (keine ? - 1 ? - 2 ? - mehr ?) - und wie soll das Programm unterscheiden, was zu einer Endung gehört und was nur ein Punkt im Namen ist ? (Beispiel: datei.tar.gz / datei_vom_12.04.2015.txt )
Da müsstest Du die Bedingungen noch einmal genauer formulieren. (das mit der Längenbestimmung ist dann mit Perl kein Problem mehr) LG, track
|