# HG changeset patch # User markus schnalke # Date 1430809440 -7200 # Node ID 1dc4a9dca829f7418c418a924abc8e0878fcd8d3 # Parent 21ca59543b07536e47e2e300ccdcb496af36290f Zwischenstand diff -r 21ca59543b07 -r 1dc4a9dca829 cut.txt --- a/cut.txt Sun May 03 21:05:00 2015 +0200 +++ b/cut.txt Tue May 05 09:04:00 2015 +0200 @@ -7,23 +7,22 @@ Cut ist ein klassisches Programm im Unix-Werkzeugkasten. -In keinem ordentlichen Tutorial zur Shellprogrammierung darf -es fehlen. Es ist ein schoenes Anschauungs- und Beispielobjekt -fuer's Shellscripting. Hier will ich ein wenig hinter die -Fassade schauen. +In keinem ordentlichen Tutorial zur Shellprogrammierung fehlt +es. Es ist ein schoenes Anschauungsobjekt fuer's Shellscripting. +Hier soll ein wenig hinter die Fassade von cut geschaut werden. Funktionsweise -Die Funktionsbasis von cut waren urspruenglich zwei Modi, die -spaeter um einen dritten erweitert wurden. Cut schneidet -entweder bestimmte Zeichen aus den Zeilen der Eingabe oder -bestimmte durch Trennzeichen definierte Felder. +Urspruenglich hatte cut zwei Modi, die spaeter um einen dritten +erweitert wurden. Cut schneidet entweder bestimmte Zeichen aus +den Zeilen der Eingabe oder bestimmte, durch Trennzeichen +definierte, Felder. -Der Zeichenmodus ist geeignet um Ausschnitte aus -Festbreitenformaten zu extrahieren. So kann man damit -beispielsweise bestimmte Zugriffsrechte aus der Ausgabe von -`ls -l' ausschneiden. Hier die Rechte des Besitzers: +Der Zeichenmodus ist geeignet um Festbreitenformaten zu +zerteilen. So kann man damit beispielsweise bestimmte +Zugriffsrechte aus der Ausgabe von `ls -l' ausschneiden. Hier +die Rechte des Besitzers: $ ls -l foo | cut -c 2-4 rw- @@ -38,18 +37,19 @@ $ echo "$long" | cut -c -20 -Dieser Befehl gibt die ersten maximal 20 Zeichen (jeder -Zeile) von `$long' aus. +Dieser Befehl gibt die ersten maximal 20 Zeichen von +`$long' aus. (Alternativ kann man hierfuer auch `printf +"%.20s\n" "$long"' verwenden.) Geht es aber nicht um die Darstellung von Zeichen, sondern um -ihre Speicherung, dann ist `-c' nicht unbedingt die passende -Option. Frueher, als US-ASCII als Zeichensatz und -kodierung +ihre Speicherung, dann ist `-c' nicht unbedingt geeignet. +Frueher, als US-ASCII als Zeichensatz und -kodierung noch omnipraesent war, wurde jedes Zeichen mit genau einem Byte gespeichert. Somit selektierte `cut -c' gleichermassen sowohl Ausgabezeichen als auch Bytes. Mit dem Aufkommen von Multibyte-Kodierungen (wie UTF-8) musste man sich jedoch von dieser Annahme loesen. In diesem Zug bekam cut mit -POSIX.2-1992 die Option `-b'. Diese selektiert Bytes. Will man +POSIX.2-1992 einen Bytemodus mit der Option `-b'. Will man also nur die ersten maximal 500 Bytes vor dem Newline-Zeichen stehen haben (und den Rest stillschweigend ignorieren), dann macht man das mit: @@ -57,24 +57,23 @@ $ cut -b -500 Den Rest kann man sich mit `cut -b 501-' einfangen. Diese -Funktion ist insbesondere fuer POSIX wichtig, da so sicher -gestellt werden kann, dass Textdateien keine beliebig -langen Zeilen haben. +Funktion ist insbesondere fuer POSIX wichtig, da man so +Textdateien mit begrenzter Zeilenlaenge erzeugen kann. [ http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cut.html#tag_20_28_17 Neben dem Zeichen- bzw. Byte-Modus bietet cut noch den -interessanteren Feld-Modus, den man mit `-f' einleitet. Mit ihm +Feld-Modus, den man mit `-f' einleitet. Mit ihm koennen Felder ausgewaehlt werden. Das Trennzeichen (per Default der Tab) kann mit `-d' geaendert werden. -Der typische Anwendungsfall fuer den Feld-Modus. Ist die -Extraktion von Information aus der passwd-Datei. So z.B. der -Username, die User-ID und das Homeverzeichnis: +Der typische Anwendungsfall fuer cut im Feld-Modus ist die +Auswahl von Information aus der passwd-Datei. So z.B. der +Benutername, seine ID und das Homeverzeichnis: $ cut -d: -f1,3,6 /etc/passwd (Die Argumente fuer die Optionen koennen bei cut uebrigens -direkt angehaengt oder mit Whitespace abgetrennt folgen.) +mit Whitespace abgetrennt oder direkt angehaengt folgen.) Dieser Feld-Modus ist fuer einfache tabellarische Dateien, @@ -85,30 +84,27 @@ nicht sowohl an Leerzeichen als auch an Tabs getrennt werden. Auch unterteilt cut an jedem Trennzeichen. Zwei aneinander stehende Trennzeichen fuehren zu einem leeren Feld. Dieses -Verhalten widerspricht den Erwartungen fuer eine Datei mit -Whitespace-getrennten Feldern. (Manche Implementierungen von -cut, z.B. die von FreeBSD, haben deshalb Erweiterungen, die -das gewuenschte Verhalten fuer Whitespace-getrennte Felder -bieten.) Ansonsten, d.h. wenn man portabel bleiben will, -hilft awk. +Verhalten widerspricht den Erwartungen, die man an die +Verarbeitung einer Datei mit Whitespace-getrennten Feldern +hat. Manche Implementierungen von cut, z.B. die von FreeBSD, +haben Erweiterungen, die das gewuenschte Verhalten fuer +Whitespace-getrennte Felder bieten. Ansonsten, d.h. wenn +man portabel bleiben will, hilft awk. Awk bietet noch eine weitere Funktion, die cut missen -laesst: Das Tauschen der Felder-Reihenfolge. Bei cut ist die -Reihenfolge der Feldauswahl irrelevant; ein Feld kann selbst -mehrfach angegeben werden. Der Aufruf von `cut -c 5-8,1,4-6' -gibt z.B. die Zeichen Nummer 1, 4, 5, 6, 7 und 8 aus. Die -Auswahl aehnelt damit der Mengenlehre in der Mathematik: -Jedes angegebene Feld soll in der Ergebnismenge sein. Die -Felder der Ergebnismenge werden dabei immer in der gleichen -Reihenfolge ausgegeben wie sie in der Eingabe waren. - - - -cut(1) in Version 8 Unix -``In data base parlance, it projects a relation.'' - -WP: -http://de.wikipedia.org/wiki/Projektion_(Informatik)#Projektion +laesst: Das Tauschen der Feld-Reihenfolge in der Ausgabe. Bei +cut ist die Reihenfolge der Feldauswahlangabe irrelevant; ein +Feld kann selbst mehrfach angegeben werden. So gibt der Aufruf +von `cut -c 5-8,1,4-6' die Zeichen Nummer 1, 4, 5, 6, 7 und 8 +in genau dieser Reihenfolge aus. Die Auswahl entspricht damit +der Mengenlehre in der Mathematik: Jedes angegebene Feld wird +Teil der Ergebnismenge sein. Die Felder der Ergebnismenge sind +dabei immer gleich geordnet wie sie es in der Eingabe waren. +Oder, um die Worte der Manpage in Version 8 Unix +wiederzugeben: ``In data base parlance, it projects a relation.'' +Cut fuehrt also die Datenbankoperation Projektion auf +Textdateien aus. Die Wikipedia erklaert das in +verstaendlicherer Sprache: Die Projektion entspricht der Projektionsabbildung aus der Mengenlehre und kann auch Attributbeschränkung genannt @@ -117,6 +113,8 @@ Selektion auf Spaltenebene zu verstehen, das heißt, die Projektion blendet Spalten aus. +[ http://de.wikipedia.org/wiki/Projektion_(Informatik)#Projektion + @@ -128,30 +126,36 @@ Zeitstempel 1980-04-11. [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=SysIII/usr/src/cmd Das ist die aelteste Manifestation des Programms, die ich -aufstoebern konnte. +aufstoebern konnte. Allerdings spricht die sccsid im +Quellcode von Version 1.5. Es muss also noch eine +Vorgeschichte geben. Zu dieser habe ich leider keinen Zugang +gefunden. Aber werfen wir doch einen Blick auf die BSD-Linie: Dort ist mein -fruehester Fund ein cut.c mit dem Datum 1986-11-07 im Code der -Spezialversion 4.3BSD-UWisc, +fruehester Fund ein cut.c mit dem Dateimodifikationsdatum +1986-11-07 +[ http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-UWisc/src/usr.bin/cut +als Teil der Spezialversion 4.3BSD-UWisc, [ http://gunkies.org/wiki/4.3_BSD_NFS_Wisconsin_Unix die im Januar 1987 veroeffentlicht wurde. -[ http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-UWisc/src/usr.bin/cut -Die Datei unterscheidet sich nur minimal von der aus System III. -Im bekannteren 4.3BSD-Tahoe (1988) taucht cut aber nicht auf. -Im darauf folgenden 4.3BSD-Reno (1990) gibt es wiederum ein -cut ... ein von Adam S. Moskowitz und Marciano Pitargue neu -implementiertes cut, das 1989 in BSD aufgenommen wurde. +Die Implementierung unterscheidet sich nur minimal von der +in System III. +Im bekannteren 4.3BSD-Tahoe (1988) taucht cut nicht auf. +Das darauf folgende 4.3BSD-Reno (1990) liefert aber wieder +ein cut mit aus. Dieses cut ist ein von Adam S. Moskowitz und +Marciano Pitargue neu implementiertes cut, das 1989 in BSD +aufgenommen wurde. [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-Reno/src/usr.bin/cut Seine Manpage [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-Reno/src/usr.bin/cut/cut.1 erwaehnt bereits die erwartete Konformitaet mit POSIX.2. Nun sollte man wissen, dass POSIX.2 erst im September -1992 veroeffentlicht wurde, gut zwei Jahren *nachdem* die -Manpage und das Programm geschrieben wurden. Dieses cut -wurde also anhand von Entwuerfen des Standards +1992 veroeffentlicht wurde, also gut zwei Jahren *nachdem* die +Manpage und das Programm geschrieben wurden. Das Programm +wurde also anhand von Arbeitsversionen des Standards implementiert. Zweieinhalb Jahre Arbeit war immerhin schon in den Standardisierungsprozess geflossen; bis zur -Fertigstellung sollte es noch weitere zwei Jahre dauern. +Fertigstellung sollte es aber noch weitere zwei Jahre dauern. Trotz all dieser Jahreszahlen aus den 80er Jahren gehoert cut aus Sicht des urspruenglichen Unix zu den juengeren Tools. @@ -162,8 +166,8 @@ Unix-Systeme darstellt. Die weit komplexeren Programme sed und awk waren dort schon vertreten. Man muss sich also fragen, warum cut ueberhaupt noch entwickelt wurde, wo es -schon zwei Programme gab, die die Aufgabe von cut bereits -abdeckten. Ein Argument fuer cut ist seine Kompaktheit und +schon zwei Programme gab, die die Aufgabe von cut abdeckten. +Ein Argument fuer cut war sicher seine Kompaktheit und die damit verbundene Geschwindigkeit gegenueber dem damals traegen awk. Diese schlanke Gestalt ist es auch, die der Unix Philosopie entspricht: Mache eine Aufgabe und die richtig! @@ -171,13 +175,200 @@ uebernommen, standardisiert und ist heutzutage ueberall anzutreffen. -Die urspruengliche Variante (ohne -b) taucht schon 1985 in +Die urspruengliche Variante (ohne -b) tauchte schon 1985 in der System V Interface Definition, einer wichtigen formalen Beschreibung von UNIX System V, und in allen relevanten Standards seither auf. Mit POSIX.2 im Jahre 1992 wurde cut zum ersten Mal in der heutigen Form (mit -b) standardisiert. + +Multibyte-Behandlung + +Nun sind der Bytemodus und die damit verbundene +Multibyte-Verarbeitung des POSIX-Zeichenmodus bereits seit +1992 standardisiert, wie steht es aber mit deren Umsetzung? +Welche Versionen implementieren denn den POSIX korrekt? +Die Situation ist mehrschichtig. Es gibt traditionelle +Implementierungen, die nur -c und -f kennen. Dann gibt es +Implementierungen die zwar -b kennen, es aber nur als Alias +fuer -c handhaben. Diese Implementierungen funktionieren mit +Single-Byte-Encodings (z.B. US-ASCII, Latin1) korrekt, bei +Multi-Byte-Encodings (z.B. UTF-8) verhaelt sich ihr -c aber +wie -b (und -n wird ignoriert). Schliesslich gibt es noch +Implementierungen, die -b und -c tatsaechlich POSIX-konform +implementieren. + +Traditionelle Zwei-Modi-Implementierungen sind z.B. die von +System III, System V und die aller BSDs bis in die 90er. + +Pseude-Multibyte-Implementierungen bieten GNU und die +modernen NetBSDs und OpenBSDs. Wie sehr dort der Schein von +POSIX-konformitaet gewahrt wird, ist unterschiedlich. Nicht +immer findet man klare Aussagen wie diese: + + /* Since we don't support multi-byte characters, the -c and -b + options are equivalent, and the -n option is meaningless. */ + +[ XXX + +Tatsaechlich standardkonforme Implementierungen, die +Multibytes korrekt handhaben, bekommt man bei einem modernen +FreeBSD und bei den Heirloom Tools. Bei FreeBSD hat Tim Robbins +(tjr) im Sommer 2004 den Zeichenmodus POSIX-konform reimplementiert. +[ https://svnweb.freebsd.org/base?view=revision&revision=131194 +Warum die beiden anderen grossen BSDs diese Aenderung nicht +uebernommen haben, bleibt offen. Es scheint aber an der im +obigen Kommentar formulierten Grundausrichtung zu liegen. + +Wie findet man als Nutzer heraus, ob beim cut(1) des eigenen +Systems Multibytes korrekt unterstuetzt werden? Zuerst ist +entscheidend, ob das System selbst mit einem Multibyte-Encoding +arbeitet, denn tut es das nicht, dann entsprechen sich Zeichen +und Bytes und die Frage eruebrigt sich. Man kann dazu nachschauen, +welches Locale eingestellt ist, aber einfacher ist es, ein +typisches Mehrbytezeichen, wie z.B. einen Umlaut, auszugeben +und zu schauen ob dieses in einem oder in mehreren Bytes +kodiert ist: + + $ echo ä | od -c + 0000000 303 244 \n + 0000003 + +In diesem Fall sind es zwei Bytes: oktal 303 und 244 . (Den +Zeilenumbruch fuegt echo(1) hinzu.) + +Mit dem Programm iconv(1) kann man Test explizit in bestimmte +Kodierungen konvertieren. Hier Beispiele, wie das Ergebnis +bei Latin1 und wie es bei UTF-8 aussieht. + + $ echo ä | iconv -t latin1 | od -c + 0000000 344 \n + 0000002 + + $ echo ä | iconv -t utf8 | od -c + 0000000 303 244 \n + 0000003 + +Die Ausgabe auf dem eigenen System (ohne die iconv-Konvertierung) +wird recht sicher einer dieser beiden Ausgaben entsprechen. + +Nun zum Test der cut-Implementierung. Hat man ein UTF-8-System, +dann sollte sich eine POSIX-konforme Implementierung so verhalten: + + $ echo aä | ./cut -c -2 | od -c + 0000000 a 303 244 \n + 0000004 + + $ echo aä | ./cut -b -2 | od -c + 0000000 a 303 \n + 0000003 + + $ echo aä | ./cut -b -2 -n | od -c + 0000000 a \n + 0000002 + +Bei einer Implementierung, die -b und -c gleich behandelt, +ist die Ausgabe in allen drei Faellen wie die mittlere: Es +werden die ersten beiden Bytes ausgegeben. + + + +Implementierungen + +Nun zum Blick auf den Code. Hier soll eine Auswahl an +Implementierungen etwas genauer betrachtet werden. Fuer einen +ersten Eindruck ist der Umfang des Quellcodes hilfreich. +Typischerweise steigt dieser ueber die Jahre an. Diese +Beobachtung kann hier in der Tendenz, aber nicht in jedem Fall, +bestaetigt werden. + +Die Unterstuetzung des Byte-Modus (-b) erfordert zwangslaeufig +mehr Code, deshalb ist zu erwarten, dass diejenigen +Implementierungen, die ihn haben, umfangreicher sind. + +Codevergleich + +SLOC Zeilen Bytes Gehoert zu Dateidatum Kategorie +----------------------------------------------------------------- +116 123 2966 System III 1980-04-11 (trad) +118 125 3038 4.3BSD-UWisc 1986-11-07 (trad) +200 256 5715 4.3BSD-Reno 1990-06-25 (trad) +200 270 6545 NetBSD 1993-03-21 (trad) +218 290 6892 OpenBSD 2008-06-27 (pseudo) +224 296 6920 FreeBSD 1994-05-27 (trad) +232 306 7500 NetBSD 2014-02-03 (pseudo) +340 405 7423 Heirloom 2012-05-20 (POSIX) +382 586 14175 GNU coreutils 1992-11-08 (pseudo) +391 479 10961 FreeBSD 2012-11-24 (POSIX) +588 830 23167 GNU coreutils 2015-05-01 (pseudo) + + +$ awk -F' +' '{printf("%d\t%d (%.2f)\t%d (%.2f)\t%s\t%s\t%s\n", + $1, $2, $2/$1, $3, $3/$1, $4, $5, $6);}' . - - Rewrite cut_fields and cut_bytes -- Jim Meyering. */ - -1992-11-08 -Jim Meyering - - - - - Autoreninfo