[gelöst] tr, [[ =~ ]], ${#} und die Umlaute

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
Lohengrin
Beiträge: 3227
Registriert: 29.08.2004 00:01:05
Wohnort: Montsalvat

[gelöst] tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von Lohengrin » 10.10.2018 23:20:26

Ich bin auf ein Problem mit Bytes, Characters und LC_COLLATE gestoßen, das ich noch nicht durchschaue.

LC_COLLATE und irgendwas mit LANG ist wie folgt.

Code: Alles auswählen

$ set|grep ^LC
$ 
$ set|grep LANG
GDM_LANG=de_DE.UTF-8
LANG=de_DE.UTF-8 
Es fing damit an, dass [[ $foo =~ ^[0-9a-f]{2}$ ]] nicht nur auf genau 2 Hexziffern passt, sondern auch ä7 mitnimmt.
Einerseits habe ich

Code: Alles auswählen

$ [[ ä7 =~ ^[0-9a-f]{2}$ ]] ; echo $?
0
$ [[ B4 =~ ^[0-9a-f]{2}$ ]] ; echo $?
1
Demnach liegt ä im Bereich [a-f], aber B nicht.
Andererseits habe ich

Code: Alles auswählen

$ touch {a..f}
$ ls 
a  b  c  d  e  f
$ touch {A..F}
$ ls 
a  A  b  B  c  C  d  D  e  E  f  F
$ touch ä Ä
$ ls 
a  A  ä  Ä  b  B  c  C  d  D  e  E  f  F
$ ls [a-f]
a  A  ä  Ä  b  B  c  C  d  D  e  E  f
Demnach sitzt bei ls [a-f] sowohl ä als auch B dazwischen, aber bei {a..f} nicht.

Und dann ist da noch

Code: Alles auswählen

$ foo=ä ; echo ${#foo} ; echo -n $foo|hd
1
00000000  c3 a4                                             |..|
00000002
Demnach zählt ${#} nicht Bytes sondern Characters. Das steht auch tatsächlich so in man bash.

Aber tr zählt in Bytes, obwohl in man tr etwas von Characters steht.

Code: Alles auswählen

$ echo ä|tr 'ä' 'xy' ; echo x | tr 'x' 'ä'|hd
xy
00000000  c3 0a                                             |..|
00000002
Jetzt meine Fragen dazu.
Welches Alphabet gilt bei [[ =~ ]] ?
Wird tr so bleiben, oder werden die auch auf Characters umstellen?
Wie zählt man die Länge einer Variable in Bytes statt in Characters?

Code: Alles auswählen

$ foo=ä ; echo $(echo -n $foo|wc -c)
2
ist doch recht umständlich.
<edit>
Gibt es überhaupt eine sinnvolle Anwendung, wo man die Länge einer Variable in Zeichen braucht? Mir fällt keine ein.
</edit>

Früher hat LC_COLLATE=C funktioniert. Das klappt aber nicht mehr.

Code: Alles auswählen

$ LC_COLLATE=C
$ ls
a  A  ä  Ä  b  B  c  C  d  D  e  E  f  F
Was kann man heute tun?
Zuletzt geändert von Lohengrin am 12.10.2018 17:33:17, insgesamt 2-mal geändert.
Harry, hol schon mal das Rasiermesser!

wanne
Moderator
Beiträge: 7548
Registriert: 24.05.2010 12:39:42

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von wanne » 11.10.2018 07:27:56

Zuerstmal: GNU tr manipuliert – entgegen seiner Manpage Bytes – nicht Zeichen. Das kann für einige low-level funktionen ganz hilfreich sein. (Z.B. um kaputte Zeilenumbrüche zu korrigieren oder in binarys rumzpatchen)
Ich nehme an, dass man es aus dem Grund, dass es für dererlerlei Funktionen genutzt wird (und das so manches Script, dass tr für Bytes nutzt gebrochen worden wäre) nicht für mehr als ASCII Fitt gemacht hat. Denke das tr so bleiben wird.
Wenn du Zeichen manipulieren willst nimm lieber eine der "Scriptsprachen": (sed/awk/perl)

Für fast alles andere (Es gibt noch immer ein paar tools die nicht mit mehr als ASCII umgehen können. Aber so langsam ist das wirklich vorbei.):
LC_*=C heißt nun mal ASCII only. Wenn du dann mit ä oder ähnlichem kommst, kommt Müll raus. (über dererlei inkonsistente Ergebnisse wollen wir uns nicht Unterhlaten. Shit in – Shit out.) Korrekt wäre C.UTF-8. Gibt aber einige wenige grafische Tools, die damit nicht zurecht kommen. Dann kannst du en_US.UTF-8 nehmen.

Dann scheinst du nicht so richtig zu verstehen, was [0-9a-f] bedeutet:
- bedutet von ... bis ..
[ .. ] ein beliebiges der genannten.
Das macht in dem Fall ein beliebiges zwischen 0 oder 9 oder zwichen a und f.
Das entspricht mit Nichten Hex-Ziffern. Die wären [:xdigit:].
Wenn du das willst Matche auf doch auf [[:xdigit:]]
Die Zählweise im englischen ist etwas kompliziert:
Die einfachste zuerst:

Code: Alles auswählen

en_US.UTF-8: 0 1 2 ... 9 a b c ... z A B C Z ...
Aber schon bei C.UTF-8 wird es komplizierter:

Code: Alles auswählen

C.UTF-8: 0 1 2 .... 9 : ; < = > ? @ A B C D ... Z [ / ] ^ _ ` a b c ... z
Du merkst da kommen recht unerwartet Sonderzeichen zwischen groß und kleinbuchstaben.
de_DE.UTF-8 ist zwar etwas uninuitiv

Code: Alles auswählen

de_DE.UTF-8: 0 1 2 ... 9 a ä b c ... z A Ä B C Z ...
Hat für deutsche aber extreme Vorteile: äöü sind dann innerhalb von a-z. (und ä halt zwischen a und b und damit zwischen a und f.)
Für alle gilt aber: Groß- und Kleinbuchstaben sind getrennt. Willst du beide haben nimm [a-zA-Z] Dann kann nichts schief gehen.
rot: Moderator wanne spricht, default: User wanne spricht.

Benutzeravatar
Lohengrin
Beiträge: 3227
Registriert: 29.08.2004 00:01:05
Wohnort: Montsalvat

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von Lohengrin » 11.10.2018 21:39:12

wanne hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 07:27:56
Zuerstmal: GNU tr manipuliert – entgegen seiner Manpage Bytes – nicht Zeichen.
...
Denke das tr so bleiben wird.
Das ist beruhigend.
Dann sollte man aber die Manpage von tr ändern. Noch vor kurzer Zeit - und dabei merke ich, wie alt ich bin - war Character im Gegensatz zu Byte nur eine Frage von unsigned und signed.
wanne hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 07:27:56
Wenn du Zeichen manipulieren willst nimm lieber eine der "Scriptsprachen": (sed/awk/perl)
Ich will nur feststellen, ob es in einer Datei Bytes gibt, die da nicht sein sollen. Ich mache mit tr -d alles Gute weg, und stelle fest, ob noch etwas raus kommt.
Bytes oder Zeichen in einem Strom zu manipulieren, ist das nächste Ding. Ich will zB jedes < durch &lt; ersetzen, aber ich will das gar nicht zeilenweise bearbeiten. sed liest bis zum nächsten \x0a, und dann kann ich in dieser Zeile mit Regex herummachen. Nur bei meinen Datenströmen kommt mglw gar kein \x0a vor, so dass sed daran verrecken könnte.
Wenn man bei sed die Zeilenlänge auf 1 (Bytes oder Characters) setzen könnte, würde sed -e 's/</&lt;/' funktionieren. Wie sagt man sed, dass es statt bis zum nächsten \x0a die nächsten n Bytes oder Characters lesen soll?
wanne hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 07:27:56
LC_*=C heißt nun mal ASCII only.
Schön wärs. Bei mir wird LC_COLLATE=C anscheinend teilweise ignoriert. An Gnome liegt es nicht. Auf der Konsole ist es genauso.

Code: Alles auswählen

$ touch {a..f} {A..F} ä Ä
$ ls [a-f] # B liegt drin
a  A  ä  Ä  b  B  c  C  d  D  e  E  f
$ [[ B =~ [a-f] ]] ; echo $? # B liegt nicht drin
1
$ LC_COLLATE=C
$ ls [a-f]
a  b  c  d  e  f
$ ls # LC_COLLATE wird ignoriert
a  A  ä  Ä  b  B  c  C  d  D  e  E  f  F
$ LC_COLLATE=C ls # so geht es
A  B  C  D  E  F  a  b  c  d  e  f  Ä  ä
$ echo -e ':Hans Meier\nHans  Meier\nDirk Schuster\nHans Meier'|sort # LC_COLLATE wird ignoriert
Dirk Schuster
:Hans Meier
Hans Meier
Hans  Meier
$ echo -e ':Hans Meier\nHans  Meier\nDirk Schuster\nHans Meier'|LC_COLLATE=C sort # so geht es
:Hans Meier
Dirk Schuster
Hans  Meier
Hans Meier
Was ist da los?
wanne hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 07:27:56
Dann scheinst du nicht so richtig zu verstehen, was [0-9a-f] bedeutet:
- bedutet von ... bis ..
[ .. ] ein beliebiges der genannten.
Das habe ich mir auch so gedacht. Nur gehört bei [a-f] mal B dazu (bei ls [a-f]) und mal nicht (bei [[ B =~[a-f] ]]). Ich will verstehen wann und warum das so ist.
wanne hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 07:27:56
Das macht in dem Fall ein beliebiges zwischen 0 oder 9 oder zwichen a und f.
Das entspricht mit Nichten Hex-Ziffern. Die wären [:xdigit:].
Wenn du das willst Matche auf doch auf [[:xdigit:]]
Ich will aber nur Hexziffern mit kleinen abcdef haben.
Außerdem will ich überprüfen, ob in einer Zeile gültiges base64 steht.

Code: Alles auswählen

b64_regex='^[A-Za-z0-9+/]{4}*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'
[[ $line =~ $b64_regex ]]
Wenn ich da noch mit ä rechnen muss, wird das unübersichtlich.

Code: Alles auswählen

b64_class='[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/]'
b64_regex="^${b64_class}{4}*(${b64_class}{2}==|${b64_class}{3}=)?\$"
[[ $line =~ $b64_regex ]]
wanne hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 07:27:56
Aber schon bei C.UTF-8 wird es komplizierter:

Code: Alles auswählen

C.UTF-8: 0 1 2 .... 9 : ; < = > ? @ A B C D ... Z [ / ] ^ _ ` a b c ... z
Du merkst da kommen recht unerwartet Sonderzeichen zwischen groß und kleinbuchstaben.
So habe ich das erwartet (vermutlich ist dein / ein \, denn / ist \x2f).
Wenn man schon mehrere Bytes zu einem Zeichen zusammenfasst, dann sollte man gültige utf8-Kombinationen gemäß dem Unicode anordnen, aber was macht man bei ungültigen utf8-Kombinationen? Soll man, wenn etwas mit [\xf0-\xf7] anfängt, die nächsten drei Bytes mitnehmen, auch wenn etwas von [\x00-\x7f] dabei ist, oder soll man abbrechen, sobald klar ist, dass es keine gültige utf8-Kombination wird? Und wo sortiert man das ungültige Zeichen dann ein?
wanne hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 07:27:56
de_DE.UTF-8 ist zwar etwas uninuitiv

Code: Alles auswählen

de_DE.UTF-8: 0 1 2 ... 9 a ä b c ... z A Ä B C Z ...
Vermutlich ist das bei [[ $foo =~ [a-f] ]] so, aber leider nicht bei ls [a-f].
wanne hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 07:27:56
Hat für deutsche aber extreme Vorteile: äöü sind dann innerhalb von a-z. (und ä halt zwischen a und b und damit zwischen a und f.)
Vor ein paar Jahrzehnten waren noch grob drei Versionen üblich. Die einen haben ä wie a und ß wie s behandelt, die anderen haben ä wie ae und ß wie ss behandelt (ist noch heute im gedruckten Telefonbuch so, habe es gerade nachgeschaut), und noch andere haben das in der Reihenfolge äößü hinter das z sortiert. Mir hat die dritte Version am besten gefallen. Dass jemand ä zwischen a und b sortiert, war selten.
Am liebsten ist mir die Reihenfolge Muller Mutter Mu\xcc\x88ller Myller M\xc3\xbcller. Ein u mit Punkten als \xc3\xbc ist so wie ein O mit Schwanz als Q. Ein u mit Punkten ist u\xcc\x88, so wie ein f mit Punkten f\xcc\x88 ist. Nun gibt es leider zwei verschiedene u mit Punkten, und die sitzen ganz genau da wo sie meiner Meinung nach und außerdem auch hin gehööööörn ( ... pampam, pampa ... Gruß an Wum von Loriot!).
Harry, hol schon mal das Rasiermesser!

wanne
Moderator
Beiträge: 7548
Registriert: 24.05.2010 12:39:42

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von wanne » 12.10.2018 11:58:31

Lohengrin hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 21:39:12
sed liest bis zum nächsten \x0a […] Nur bei meinen Datenströmen kommt mglw gar kein \x0a vor, so dass sed daran verrecken könnte.
Nein. sed ließt immer bis zum Zeilenende. Und das muss mit nichten immer ein \x0a sein. Sondern kann auch das Ende einer Datei oder je nach Einstellung eine andere Bytefolge sein.
sed -e 's/</&lt;/'
Was hältst du denn davon?

Code: Alles auswählen

sed 's/</\&lt;/g'
Ersetzt jedes vorkommen von < durch &lt; und das eben unabhängig davon, wie viele Byte < hat.
wanne hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 07:27:56
LC_*=C heißt nun mal ASCII only.
Schön wärs. Bei mir wird LC_COLLATE=C anscheinend teilweise ignoriert. An Gnome liegt es nicht. Auf der Konsole ist es genauso.
DU sagst damit, dass du ASCII only machst. Wenn du dann was anderes rein schmeißt, kommt halt (möglicherweise) Mist raus. ganz früher haben sich manche Programme dann auch ganz gerne mal kommentarlos beendet oder noch beliebter einfach die vorderen Bits weggowrofen. Aus einem ü im Passowrt macht dir cryptocat kommentarlos C<.
Du solltest niemals was anderes als ASCII verwenden, wenn deine LANG auf C steht.

Code: Alles auswählen

ls [a-f] # B liegt drin
Das [ ] als Dateinamen-Replacement in der Bash ist case insensitive. Vermutlich um mit Windows kompatibler zu sein. (Windows öffnet in Wirklichkeit DATEI wenn man auf Datei oder datei klickt.) B liegt nicht in a-f. b liegt in a-f und für b bekommst du auch die Datei B. Das ist relativ verwirrend, weil ich sonst kein Linux-Programm kenne, dass case-insensitive ist und auch die bash das an anderen Stellen (wie bei [[ ) nicht ist. Vor allem ist das Programm test (Liegt unter /usr/bin/[ siehe auch /usr/bin/[ --version und das passende bash-builtin auch case sensitive)
Ich weiß warum ich so ungerne die bash zum scripten nehme. Mit der dash (liegt bei Debian unter /bin/sh) (oder der zsh) wäre dir das nicht passiert ;-)


Code: Alles auswählen

Außerdem will ich überprüfen, ob in einer Zeile gültiges base64 steht.
Was hällts du davon:

Code: Alles auswählen

echo "$line" | base64 -d 2>&1 > /dev/null
Dein "Parser" hat noch ein paar ganz andere Probleme. z.B. erkennt er 'aaa\na' nicht als gültiges base64.
Wenn ich da noch mit ä rechnen muss, wird das unübersichtlich.
So unübersichtlich finde ich es gar nicht. Aber im allgemeinen ist halt die bash eher nichts für richtiges Programmieren und vor allem nichts um Bits und Bytes hin und her zu schieben. Die shell tools sind halt auf Zeichenketten nicht auf Bytes Wenn du Bytes manipulieren willst, nimm C. – Oder lass dir die Aufgabe von vorgefertigten Programmen erledigen, die in C geschrieben sind. Aber selber bauen in der bash wird nur schmerzhaft.
aber was macht man bei ungültigen utf8-Kombinationen?
Shit in Shit out. Und zum x. mal mehr gibt es da nicht zu sagen. Wie das alt bekannte a[-1] in C funktioniert oft gut, war aber nie so gedacht und irgend wann haut es dich halt mal auf die Fresse. So ist das mit deinem LANG=C und umlauten so wird das passieren wenn du 0x00ff00 in LANG=C.UTF-8 hast.
rot: Moderator wanne spricht, default: User wanne spricht.

Benutzeravatar
MartinV
Beiträge: 790
Registriert: 31.07.2015 19:38:52
Wohnort: Hyperion
Kontaktdaten:

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von MartinV » 12.10.2018 12:08:18

Code: Alles auswählen

$ LC_COLLATE=C
Es muß heißen:

Code: Alles auswählen

$ export LC_COLLATE=C
Die Vernunft kann einem schon leidtun. Sie verliert eigentlich immer.

Benutzeravatar
Lohengrin
Beiträge: 3227
Registriert: 29.08.2004 00:01:05
Wohnort: Montsalvat

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von Lohengrin » 12.10.2018 13:10:40

MartinV hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 12:08:18

Code: Alles auswählen

$ LC_COLLATE=C
Es muß heißen:

Code: Alles auswählen

$ export LC_COLLATE=C
:facepalm:
Danke!
Harry, hol schon mal das Rasiermesser!

Benutzeravatar
Lohengrin
Beiträge: 3227
Registriert: 29.08.2004 00:01:05
Wohnort: Montsalvat

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von Lohengrin » 12.10.2018 14:57:09

wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 11:58:31
Lohengrin hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 21:39:12
sed liest bis zum nächsten \x0a […] Nur bei meinen Datenströmen kommt mglw gar kein \x0a vor, so dass sed daran verrecken könnte.
Nein. sed ließt immer bis zum Zeilenende. Und das muss mit nichten immer ein \x0a sein. Sondern kann auch das Ende einer Datei oder je nach Einstellung eine andere Bytefolge sein.
Dass es immer bis zum \x0a liest, ist mein Problem. Ich würde gerne eine feste Zeilenlänge vorgeben. Etwas anderes als \x0a als Begrenzer der Zeilen wäre auch nett. Bei read geht das mit -d, aber so etwas habe ich bei sed nicht gefunden. Gibt es in bash so etwas wie IFS, aber nicht für Felder, sondern für Zeilen?
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 11:58:31
sed -e 's/</&lt;/'
Was hältst du denn davon?

Code: Alles auswählen

sed 's/</\&lt;/g'
Ersetzt jedes vorkommen von < durch &lt; und das eben unabhängig davon, wie viele Byte < hat.
Wenn kein Zeilenende kommt, und die Datei riesig ist, wird das monströs. In meinem Fall würde das wohl noch gehen, aber schön ist das nicht.

Was meinst du mit unabhängig wie viele Byte < hat? Ich hoffe es gibt nur eine Kodierung für <.
Wenn ich auf UTF-16 bin, sind das wohl zwei Bytes, aber mein Überprüfung, ob nur gute Zeichen in der Datei sind, würde da mit tr nicht mehr funtionieren.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 11:58:31
wanne hat geschrieben: ↑ zum Beitrag ↑
11.10.2018 07:27:56
LC_*=C heißt nun mal ASCII only.
Schön wärs. Bei mir wird LC_COLLATE=C anscheinend teilweise ignoriert. An Gnome liegt es nicht. Auf der Konsole ist es genauso.
DU sagst damit, dass du ASCII only machst. Wenn du dann was anderes rein schmeißt, kommt halt (möglicherweise) Mist raus.
LC_COLLATE wird gar nicht ignoriert. Ich hatte das ohne export angegeben, weshalb es ls und sort nicht hatten. Der Punkt ist geklärt.
Wenn ich LANG nicht auf C habe aber LC_COLLATE schon, wird es gruselig, wenn ich mir da Regex vorstelle. [äbc] könnten dann vier Bytes oder drei Zeichen sein. Welches Match ist da wohl gemeint?
Dann doch besser LC_COLLATE=C.UTF-8 setzen.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 11:58:31

Code: Alles auswählen

ls [a-f] # B liegt drin
Das [ ] als Dateinamen-Replacement in der Bash ist case insensitive.
Das [ ] bei ls [a-f] und das [ ] bei [[ B =~ [a-f] ]] scheinen zwei Paar Schuhe zu sein.
Das bei ls [a-f] ist wohl das, was bei man bash unter Pathname Expansion, Pattern Matching steht, und das bei [[ B =~ [a-f] ]] ist das, was bei man regex steht.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 11:58:31
Vor allem ist das Programm test (Liegt unter /usr/bin/[ siehe auch /usr/bin/[ --version und das passende bash-builtin auch case sensitive
Das [ ] vom builtin test oder von Programm test ist wohl ein drittes Paar Schuhe.
Ich bin verwirrt.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 11:58:31
Ich weiß warum ich so ungerne die bash zum scripten nehme. Mit der dash (liegt bei Debian unter /bin/sh) (oder der zsh) wäre dir das nicht passiert ;-)
Ich werden den Kram wohl in C schreiben.
Es fing damit an, dass ich aus verschiedenen Dateien automatisiert eine HTML-Seite bauen wollte, die ich mir dann mit einem Webbrowser ansehen kann. Das geht sehr gut mit Bash.
Nun will ich aber die Ursprung-Dateien dafür selber zusammenbauen, nämlich aufgrund von Infos die in Dateien liegen, worin nur Zeilen mit 40 Hexziffern langen Stücken, :-getrennt liegen, und in einem Fall auch eine Zeile, die aus base64 besteht.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 11:58:31

Code: Alles auswählen

Außerdem will ich überprüfen, ob in einer Zeile gültiges base64 steht.
Was hällts du davon:

Code: Alles auswählen

echo "$line" | base64 -d 2>&1 > /dev/null
Dein "Parser" hat noch ein paar ganz andere Probleme. z.B. erkennt er 'aaa\na' nicht als gültiges base64.
Das ist schon in Ordndung. Es geht um genau eine Zeile, in der korrektes base64 liegen soll. base64 -d würde bei \x0a nicht meckern, aber ich will ja, dass dann gemeckert wird.
Harry, hol schon mal das Rasiermesser!

Benutzeravatar
Lohengrin
Beiträge: 3227
Registriert: 29.08.2004 00:01:05
Wohnort: Montsalvat

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von Lohengrin » 12.10.2018 15:42:48

Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Das [ ] bei ls [a-f] und das [ ] bei [[ B =~ [a-f] ]] scheinen zwei Paar Schuhe zu sein.
Das bei ls [a-f] ist wohl das, was bei man bash unter Pathname Expansion, Pattern Matching steht, und das bei [[ B =~ [a-f] ]] ist das, was bei man regex steht.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 11:58:31
Vor allem ist das Programm test (Liegt unter /usr/bin/[ siehe auch /usr/bin/[ --version und das passende bash-builtin auch case sensitive
Das [ ] vom builtin test oder von Programm test ist wohl ein drittes Paar Schuhe.
Ich bin verwirrt.
Ich habe mich gerade entwirrt.

Das [ ] bei ls [a-f] oder bei echo [a-f] ist das in Pathname Expansion, Pattern Matching bei man bash Beschriebene.
Das ist ein Parameter, der dem Programm ls oder echo mitgegeben wird, und da macht Bash genau das, was in der Manpage steht.
Es gilt die Reihenfolge aAäAbBcCdDeEf. Insbesondere bleibt das [a-f] so wie es ist, wenn nichts gefunden wird.

Das [ ] bei [ a-f ] ist das Builtin test. Es wurde nur ein Argument gegeben, nämlich a-b, und das ist nicht null, demnach ist der Rückgabewert 0. Das steht auch so unter SHELL BUILTIN COMMANDS, test bei man bash.

Das [ ] bei [[ ä =~ [a-f] ]] ist regex, so wie es bei SHELL GRAMMAR, Compound Commands, [[ expression ]] steht.
Harry, hol schon mal das Rasiermesser!

wanne
Moderator
Beiträge: 7548
Registriert: 24.05.2010 12:39:42

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von wanne » 12.10.2018 16:02:57

Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Dass es immer bis zum \x0a liest, ist mein Problem. Ich würde gerne eine feste Zeilenlänge vorgeben. Etwas anderes als \x0a als Begrenzer der Zeilen wäre auch nett. Bei read geht das mit -d, aber so etwas habe ich bei sed nicht gefunden.
Gibt es in bash so etwas wie IFS, aber nicht für Felder, sondern für Zeilen?
Direkt kannst du ihm das glaube ich nicht sagen. Du könntest aber ein anderes locale bauen, das den anders definiert.
Wenn kein Zeilenende kommt, und die Datei riesig ist, wird das monströs. In meinem Fall würde das wohl noch gehen, aber schön ist das nicht.
Für Performance und vor allem kleinen Speicherbedarf war die bash nie gemacht. Eignet sich aber am Ende meist doch recht gut. Zumindest durch grep habe ich schon ohne Probleme ~100GiB Partitionsimages gejagt. So schnell ist kaum was auf den Datenmengen. (Auch wenn es jetzt extra Projekte gibt, die auf großen Daten ausdrücklich schneller sein wollen als grep.)
Am Ende hast du da aber eben eine korrekte Lösung. Im krassen Gegensatz zu irgend welchen Lösungen die von irgend welchen Byte-Längen ausgehen und dann über die nächste Encodingänderung stolpern
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 11:58:31
Wenn ich auf UTF-16 bin, sind das wohl zwei Bytes, aber mein Überprüfung, ob nur gute Zeichen in der Datei sind, würde da mit tr nicht mehr funtionieren.
Ja. Deswegen halte ich tr auch für ein extrem ungeschicktes tool für sowas. Und gerade bei XML ist UTF-16 jetzt nicht so absurd. Das kommt schon hin und wieder vor.
Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Das [ ] bei ls [a-f] und das [ ] bei [[ B =~ [a-f] ]] scheinen zwei Paar Schuhe zu sein.
Das bei ls [a-f] ist wohl das, was bei man bash unter Pathname Expansion, Pattern Matching steht, und das bei [[ B =~ [a-f] ]] ist das, was bei man regex steht.
[…]
Das [ ] vom builtin test oder von Programm test ist wohl ein drittes Paar Schuhe.
Ich bin verwirrt.
Das ist auch verwirrend. Defakto sind es sogar (mindestens) vier [ als bash Ersatz für GNU-test. [ als alias für /usr/bin/test, [ ] als regex in einem Vergleich und [] als argument an einem beliebigen Befehl, das durch Dateinamen ersetzt wird. Ein Graus.
Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Ich werden den Kram wohl in C schreiben.
Es fing damit an, dass ich aus verschiedenen Dateien automatisiert eine HTML-Seite bauen wollte, die ich mir dann mit einem Webbrowser ansehen kann. Das geht sehr gut mit Bash.
Nun will ich aber die Ursprung-Dateien dafür selber zusammenbauen, nämlich aufgrund von Infos die in Dateien liegen, worin nur Zeilen mit 40 Hexziffern langen Stücken, :-getrennt liegen, und in einem Fall auch eine Zeile, die aus base64 besteht.
Dann würde ich sogar auf keinen Fall zu C greifen. Bzw. nur mit passende HTML-libs.
Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Es geht um genau eine Zeile, in der korrektes base64 liegen soll. base64 -d würde bei \x0a nicht meckern, aber ich will ja, dass dann gemeckert wird.
Auch hier sehe ich das wieder anders. Nicht base64 -d verhält sich falsch. sondern dein nachfolgender Code, der nicht mit base64 umgehen kann sondern nur mit welchem, dass keine Zeilenumbrüche hat.

Ganz grundsätzliches anliegen: Wenn du irgend wie Daten manipulierst, dann auf der Ebene, auf der das Sinn macht und nicht irgend wie 5 schichten drunter.
Willst du XML bearbeiten nimm ein Tool für XML (wie xmlstarlet) und keines für Strings (wie sed). Und vor allem behandel sie auch so. Willst du Strings bearbeiten, behandel sie als Zeichenketten und nicht als welche von Bytes usw.
Das kann man schon machen aber so Repräsentationen sind in den seltensten Fällen eindeutig. (Wie du schon zurecht beim ä bemerkt hast (das btw. wenn überhaupt u\xcc\x88 und nicht u\xcc\x88 encodiert wird. Aber die kanonische Form ist \xc3\xbc.) aber es gibt halt nochmal 500 andere Varianten. Das bekommst du halt nicht alles abgefangen.) Spätestens bei der nächsten Änderung der Encodierung auf das Layer drunter haut es dich halt auf die Fresse.
Die Abstraktion ist schon sinnvoll und macht dir das Leben deutlich einfacher. Nicht das Problem ist nicht, dass sich irgend wo das Encoding ändert das Problem ist, dass du an stellen wo du hex meinst [0-9a-f] schreibst. Und dann vergisst A-F zu Händeln. Oder dass du wo du Zeichen zählen willst Bytes zählst, und dich dann über das 2Byte ü wunderst usw.
rot: Moderator wanne spricht, default: User wanne spricht.

wanne
Moderator
Beiträge: 7548
Registriert: 24.05.2010 12:39:42

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von wanne » 12.10.2018 16:06:31

Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Dass es immer bis zum \x0a liest, ist mein Problem. Ich würde gerne eine feste Zeilenlänge vorgeben. Etwas anderes als \x0a als Begrenzer der Zeilen wäre auch nett. Bei read geht das mit -d, aber so etwas habe ich bei sed nicht gefunden.
Gibt es in bash so etwas wie IFS, aber nicht für Felder, sondern für Zeilen?
Direkt kannst du ihm das glaube ich nicht sagen. Du könntest aber ein anderes locale bauen, das den anders definiert.
Wenn kein Zeilenende kommt, und die Datei riesig ist, wird das monströs. In meinem Fall würde das wohl noch gehen, aber schön ist das nicht.
Für Performance und vor allem kleinen Speicherbedarf war die bash nie gemacht. Eignet sich aber am Ende meist doch recht gut. Zumindest durch grep habe ich schon ohne Probleme ~100GiB Partitionsimages gejagt. So schnell ist kaum was auf den Datenmengen. (Auch wenn es jetzt extra Projekte gibt, die auf großen Daten ausdrücklich schneller sein wollen als grep.)
Am Ende hast du da aber eben eine korrekte Lösung. Im krassen Gegensatz zu irgend welchen Lösungen die von irgend welchen Byte-Längen ausgehen und dann über die nächste Encodingänderung stolpern.
Formate die sich auf aligments Verlassen will man nicht machen. Das kann ein ganz übler performance-Hack für irgend welche low-level Datenstrukturen sein (wie z.B. in perl 5 intern) das versteckt man dann aber schön brav hinter nem Kompiler oder einer API. Als Programmierer einer Hochsprache macht man sowas nicht.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 11:58:31
Wenn ich auf UTF-16 bin, sind das wohl zwei Bytes, aber mein Überprüfung, ob nur gute Zeichen in der Datei sind, würde da mit tr nicht mehr funtionieren.
Ja. Deswegen halte ich tr auch für ein extrem ungeschicktes tool für sowas. Und gerade bei XML ist UTF-16 jetzt nicht so absurd. Das kommt schon hin und wieder vor.
Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Das [ ] bei ls [a-f] und das [ ] bei [[ B =~ [a-f] ]] scheinen zwei Paar Schuhe zu sein.
Das bei ls [a-f] ist wohl das, was bei man bash unter Pathname Expansion, Pattern Matching steht, und das bei [[ B =~ [a-f] ]] ist das, was bei man regex steht.
[…]
Das [ ] vom builtin test oder von Programm test ist wohl ein drittes Paar Schuhe.
Ich bin verwirrt.
Das ist auch verwirrend. Defakto sind es sogar (mindestens) vier [ als bash Ersatz für GNU-test. [ als alias für /usr/bin/test, [ ] als regex in einem Vergleich und [] als argument an einem beliebigen Befehl, das durch Dateinamen ersetzt wird. Ein Graus.
Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Ich werden den Kram wohl in C schreiben.
Es fing damit an, dass ich aus verschiedenen Dateien automatisiert eine HTML-Seite bauen wollte, die ich mir dann mit einem Webbrowser ansehen kann. Das geht sehr gut mit Bash.
Nun will ich aber die Ursprung-Dateien dafür selber zusammenbauen, nämlich aufgrund von Infos die in Dateien liegen, worin nur Zeilen mit 40 Hexziffern langen Stücken, :-getrennt liegen, und in einem Fall auch eine Zeile, die aus base64 besteht.
Dann würde ich sogar auf keinen Fall zu C greifen. Bzw. nur mit passende HTML-libs.
Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Es geht um genau eine Zeile, in der korrektes base64 liegen soll. base64 -d würde bei \x0a nicht meckern, aber ich will ja, dass dann gemeckert wird.
Auch hier sehe ich das wieder anders. Nicht base64 -d verhält sich falsch. sondern dein nachfolgender Code, der nicht mit base64 umgehen kann sondern nur mit welchem, dass keine Zeilenumbrüche hat.

Ganz grundsätzliches anliegen: Wenn du irgend wie Daten manipulierst, dann auf der Ebene, auf der das Sinn macht und nicht irgend wie 5 schichten drunter.
Willst du XML bearbeiten nimm ein Tool für XML (wie xmlstarlet) und keines für Strings (wie sed). Und vor allem behandel sie auch so. Willst du Strings bearbeiten, behandel sie als Zeichenketten und nicht als welche von Bytes usw.
Das kann man schon machen aber so Repräsentationen sind in den seltensten Fällen eindeutig. (Wie du schon zurecht beim ä bemerkt hast (das btw. wenn überhaupt u\xcc\x88 und nicht u\xcc\x88 encodiert wird. Aber die kanonische Form ist \xc3\xbc.) aber es gibt halt nochmal 500 andere Varianten. Das bekommst du halt nicht alles abgefangen.) Spätestens bei der nächsten Änderung der Encodierung auf das Layer drunter haut es dich halt auf die Fresse.
Die Abstraktion ist schon sinnvoll und macht dir das Leben deutlich einfacher. Nicht das Problem ist nicht, dass sich irgend wo das Encoding ändert das Problem ist, dass du an stellen wo du hex meinst [0-9a-f] schreibst. Und dann vergisst A-F zu Händeln. Oder dass du wo du Zeichen zählen willst Bytes zählst, und dich dann über das 2Byte ü wunderst usw.
rot: Moderator wanne spricht, default: User wanne spricht.

wanne
Moderator
Beiträge: 7548
Registriert: 24.05.2010 12:39:42

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von wanne » 12.10.2018 16:31:00

Hier nochmal zur Performance von sed:
Ausgeführt auf eine Datei mit 1GiB die alle in einer Zeile liegen.

Code: Alles auswählen

sed 's/</\&lt;/g'
Dauer: 1.79s 
Ersetzungen: 4196627
Mach ~ 599MByte/s
Zum Vergleich: Samsung SSD 860 EVO 1TB macht lesend 550MB/​s. und schreibend 520MB/​s
Sprich: Das lesen von der Platte ist deutlich langsamer. Und das dürfte so ziemlich worst case sein. 1GiB Zeilen haben nicht so viele Dateien.
rot: Moderator wanne spricht, default: User wanne spricht.

Benutzeravatar
Lohengrin
Beiträge: 3227
Registriert: 29.08.2004 00:01:05
Wohnort: Montsalvat

Re: tr, [[ =~ ]], ${#} und die Umlaute

Beitrag von Lohengrin » 12.10.2018 17:31:26

wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 16:02:57
Defakto sind es sogar (mindestens) vier [ als bash Ersatz für GNU-test. [ als alias für /usr/bin/test, [ ] als regex in einem Vergleich und [] als argument an einem beliebigen Befehl, das durch Dateinamen ersetzt wird. Ein Graus.
Ich dachte bisher, dass es die Builtins [ , das auch den Namen test hat, und [[ gäbe, und dass es noch ein Programm Programm /usr/bin/\[ gäbe, das es auch unter dem Namen /usr/bin/test gäbe.
Nun stelle ich fest, dass /usr/bin\[ und /usr/bin/test zwei verschiedene Programme sind.
Ich habe in meinen alias weder [ noch test.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 16:02:57
Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Ich werden den Kram wohl in C schreiben.
Es fing damit an, dass ich aus verschiedenen Dateien automatisiert eine HTML-Seite bauen wollte, die ich mir dann mit einem Webbrowser ansehen kann. Das geht sehr gut mit Bash.
Nun will ich aber die Ursprung-Dateien dafür selber zusammenbauen, nämlich aufgrund von Infos die in Dateien liegen, worin nur Zeilen mit 40 Hexziffern langen Stücken, :-getrennt liegen, und in einem Fall auch eine Zeile, die aus base64 besteht.
Dann würde ich sogar auf keinen Fall zu C greifen. Bzw. nur mit passende HTML-libs.
Das HTML-Ding zusammenbauen ist nur ein Aneinanderhängen von Teilen, die ich vorbereitet da liegen habe.
Es geht darum, zu verwalten, was wie wann zusammengesetzt wird. Ich will mit Dateien, worin diese 40-hex vorkommen, festlegen, welche Teile für das Zusammensetzen benutzt werden.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 16:02:57
Lohengrin hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 14:57:09
Es geht um genau eine Zeile, in der korrektes base64 liegen soll. base64 -d würde bei \x0a nicht meckern, aber ich will ja, dass dann gemeckert wird.
Auch hier sehe ich das wieder anders. Nicht base64 -d verhält sich falsch. sondern dein nachfolgender Code, der nicht mit base64 umgehen kann sondern nur mit welchem, dass keine Zeilenumbrüche hat.
Das soll auch gar nicht mit jedem base64 umgehen können.
Ich habe zB Dateien, in denen genau zwei Zeilen sind. Die erste soll aus :-getrennten Stücken zu jeweils genau 40 Hexziffern mit kleinen abcdef bestehen. Und die zweite Zeile soll aus base64 bestehen. Wenn etwas anderes drin steht, soll die Datei verworfen werden.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 16:02:57
Ganz grundsätzliches anliegen: Wenn du irgend wie Daten manipulierst, dann auf der Ebene, auf der das Sinn macht und nicht irgend wie 5 schichten drunter.
Es sind ja nicht irgendwelche Daten. Es sind Daten, die in dem von mir gewünschten Format vorliegen müssen.
Da will ich zB dass der Benutzer den möglichen Namen für etwas als Parameter übergibt, und der mögliche Name besteht bei mir aus 40 Hexziffern mit kleinen abcdef. Wenn ich Pech habe, gibt der Benutzer einen Namen ein, wo ein ä vorkommt. Es hat mich überrascht, dass ich das nicht einfach mit [[ $line =~ ^[0-9a-f]{40}$ ]] prüfen kann.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 16:02:57
(Wie du schon zurecht beim ä bemerkt hast (das btw. wenn überhaupt u\xcc\x88 und nicht u\xcc\x88 encodiert wird. Aber die kanonische Form ist \xc3\xbc.)
M\xc3\xbcller und Mu\xcc\x88ller sind zwei verschiedene Namen. Ich will die nicht in einen Topf werfen. Das kann später geschehen, nämlich da, wo auch Stephan und Stefan zusammengefasst werden kann.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 16:02:57
Spätestens bei der nächsten Änderung der Encodierung auf das Layer drunter haut es dich halt auf die Fresse.
Ich habe Daten von Identitäten, die ihre Daten unterschrieben haben. Wenn da das Encoding wechselt, sind die Daten ungültig.
Die Identitäten haben bei mir eine Folge aus genau 40 Hexziffern mit kleinen abcdef als Namen. Und alles, was ich von den Daten der Identitäten auswerte, besteht nur aus Hexziffern mit kleinen abcdef oder base64 ohne \x0a.
Was das entpackte base64 für ein Encoding hat, ist nicht mein Problem.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 16:02:57
Nicht das Problem ist nicht, dass sich irgend wo das Encoding ändert das Problem ist, dass du an stellen wo du hex meinst [0-9a-f] schreibst.
Ich meine aber nicht hex. Ich meine [0123456789abcdef]. Bei mir heißen viele Dateien wie ihr Digest, und ich benutze ein Dateisystem, das zwischen A und a unterscheidet.
wanne hat geschrieben: ↑ zum Beitrag ↑
12.10.2018 16:02:57
Oder dass du wo du Zeichen zählen willst Bytes zählst, und dich dann über das 2Byte ü wunderst usw.
Ich habe mich nicht darüber gewundert, dass ü zwei Byte lang ist. Ich habe mich darüber gewundert, dass bei ${#foo} Zeichen gezählt werden und nicht Bytes. Für mich enthält eine Variable alles Mögliche außer \x00, so wie in C der Inhalt von char* .
Ich bin gar nicht auf die Idee gekommen, dass da jemand Zeichen statt Bytes zählen könnte, hätte, wenn ich nach deutschen Selbstlauten suche, die Regex als [aeiou(ä)(ö)(ü)] geschrieben.
Harry, hol schon mal das Rasiermesser!

Antworten