docs/cut

view cut.txt @ 8:1dc4a9dca829

Zwischenstand
author markus schnalke <meillo@marmaro.de>
date Tue, 05 May 2015 09:04:00 +0200
parents 21ca59543b07
children e67bd0d48bd6
line source
1 Das Werkzeugkaestle, #1
3 cut - cut out selected fields of each line of a file
4 ----------------------------------------------------
5 markus schnalke <meillo@marmaro.de>
6 2015-05
9 Cut ist ein klassisches Programm im Unix-Werkzeugkasten.
10 In keinem ordentlichen Tutorial zur Shellprogrammierung fehlt
11 es. Es ist ein schoenes Anschauungsobjekt fuer's Shellscripting.
12 Hier soll ein wenig hinter die Fassade von cut geschaut werden.
15 Funktionsweise
17 Urspruenglich hatte cut zwei Modi, die spaeter um einen dritten
18 erweitert wurden. Cut schneidet entweder bestimmte Zeichen aus
19 den Zeilen der Eingabe oder bestimmte, durch Trennzeichen
20 definierte, Felder.
22 Der Zeichenmodus ist geeignet um Festbreitenformaten zu
23 zerteilen. So kann man damit beispielsweise bestimmte
24 Zugriffsrechte aus der Ausgabe von `ls -l' ausschneiden. Hier
25 die Rechte des Besitzers:
27 $ ls -l foo | cut -c 2-4
28 rw-
30 Oder die Schreibrechte des Besitzers, der Gruppe und der
31 Welt:
33 $ ls -l | cut -c 3,6,9
34 ww-
36 Mit cut lassen sich aber auch Strings kuerzen.
38 $ echo "$long" | cut -c -20
40 Dieser Befehl gibt die ersten maximal 20 Zeichen von
41 `$long' aus. (Alternativ kann man hierfuer auch `printf
42 "%.20s\n" "$long"' verwenden.)
44 Geht es aber nicht um die Darstellung von Zeichen, sondern um
45 ihre Speicherung, dann ist `-c' nicht unbedingt geeignet.
46 Frueher, als US-ASCII als Zeichensatz und -kodierung
47 noch omnipraesent war, wurde jedes Zeichen mit genau einem
48 Byte gespeichert. Somit selektierte `cut -c' gleichermassen
49 sowohl Ausgabezeichen als auch Bytes. Mit dem Aufkommen von
50 Multibyte-Kodierungen (wie UTF-8) musste man sich jedoch von
51 dieser Annahme loesen. In diesem Zug bekam cut mit
52 POSIX.2-1992 einen Bytemodus mit der Option `-b'. Will man
53 also nur die ersten maximal 500 Bytes vor dem
54 Newline-Zeichen stehen haben (und den Rest stillschweigend
55 ignorieren), dann macht man das mit:
57 $ cut -b -500
59 Den Rest kann man sich mit `cut -b 501-' einfangen. Diese
60 Funktion ist insbesondere fuer POSIX wichtig, da man so
61 Textdateien mit begrenzter Zeilenlaenge erzeugen kann.
62 [ http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cut.html#tag_20_28_17
64 Neben dem Zeichen- bzw. Byte-Modus bietet cut noch den
65 Feld-Modus, den man mit `-f' einleitet. Mit ihm
66 koennen Felder ausgewaehlt werden. Das Trennzeichen (per
67 Default der Tab) kann mit `-d' geaendert werden.
69 Der typische Anwendungsfall fuer cut im Feld-Modus ist die
70 Auswahl von Information aus der passwd-Datei. So z.B. der
71 Benutername, seine ID und das Homeverzeichnis:
73 $ cut -d: -f1,3,6 /etc/passwd
75 (Die Argumente fuer die Optionen koennen bei cut uebrigens
76 mit Whitespace abgetrennt oder direkt angehaengt folgen.)
79 Dieser Feld-Modus ist fuer einfache tabellarische Dateien,
80 wie eben die passwd, gut geeignet. Er kommt aber schnell an
81 seine Grenzen. Gerade der uebliche Fall, dass an Whitespace
82 in Felder geteilt werden soll, wird damit nicht abgedeckt.
83 Der Delimiter kann nur genau ein Zeichen sein. Es kann also
84 nicht sowohl an Leerzeichen als auch an Tabs getrennt werden.
85 Auch unterteilt cut an jedem Trennzeichen. Zwei aneinander
86 stehende Trennzeichen fuehren zu einem leeren Feld. Dieses
87 Verhalten widerspricht den Erwartungen, die man an die
88 Verarbeitung einer Datei mit Whitespace-getrennten Feldern
89 hat. Manche Implementierungen von cut, z.B. die von FreeBSD,
90 haben Erweiterungen, die das gewuenschte Verhalten fuer
91 Whitespace-getrennte Felder bieten. Ansonsten, d.h. wenn
92 man portabel bleiben will, hilft awk.
94 Awk bietet noch eine weitere Funktion, die cut missen
95 laesst: Das Tauschen der Feld-Reihenfolge in der Ausgabe. Bei
96 cut ist die Reihenfolge der Feldauswahlangabe irrelevant; ein
97 Feld kann selbst mehrfach angegeben werden. So gibt der Aufruf
98 von `cut -c 5-8,1,4-6' die Zeichen Nummer 1, 4, 5, 6, 7 und 8
99 in genau dieser Reihenfolge aus. Die Auswahl entspricht damit
100 der Mengenlehre in der Mathematik: Jedes angegebene Feld wird
101 Teil der Ergebnismenge sein. Die Felder der Ergebnismenge sind
102 dabei immer gleich geordnet wie sie es in der Eingabe waren.
103 Oder, um die Worte der Manpage in Version 8 Unix
104 wiederzugeben: ``In data base parlance, it projects a relation.''
105 Cut fuehrt also die Datenbankoperation Projektion auf
106 Textdateien aus. Die Wikipedia erklaert das in
107 verstaendlicherer Sprache:
109 Die Projektion entspricht der Projektionsabbildung aus der
110 Mengenlehre und kann auch Attributbeschränkung genannt
111 werden. Sie extrahiert einzelne Attribute aus der
112 ursprünglichen Attributmenge und ist somit als eine Art
113 Selektion auf Spaltenebene zu verstehen, das heißt, die
114 Projektion blendet Spalten aus.
116 [ http://de.wikipedia.org/wiki/Projektion_(Informatik)#Projektion
121 Geschichtliches
123 Cut erblickte 1982 mit dem Release von UNIX System III das
124 Licht der oeffentlichen Welt. Wenn man die Quellen von System
125 III durchforstet, findet man die Quellcodedatei cut.c mit dem
126 Zeitstempel 1980-04-11.
127 [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=SysIII/usr/src/cmd
128 Das ist die aelteste Manifestation des Programms, die ich
129 aufstoebern konnte. Allerdings spricht die sccsid im
130 Quellcode von Version 1.5. Es muss also noch eine
131 Vorgeschichte geben. Zu dieser habe ich leider keinen Zugang
132 gefunden.
134 Aber werfen wir doch einen Blick auf die BSD-Linie: Dort ist mein
135 fruehester Fund ein cut.c mit dem Dateimodifikationsdatum
136 1986-11-07
137 [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-UWisc/src/usr.bin/cut
138 als Teil der Spezialversion 4.3BSD-UWisc,
139 [ http://gunkies.org/wiki/4.3_BSD_NFS_Wisconsin_Unix
140 die im Januar 1987 veroeffentlicht wurde.
141 Die Implementierung unterscheidet sich nur minimal von der
142 in System III.
143 Im bekannteren 4.3BSD-Tahoe (1988) taucht cut nicht auf.
144 Das darauf folgende 4.3BSD-Reno (1990) liefert aber wieder
145 ein cut mit aus. Dieses cut ist ein von Adam S. Moskowitz und
146 Marciano Pitargue neu implementiertes cut, das 1989 in BSD
147 aufgenommen wurde.
148 [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-Reno/src/usr.bin/cut
149 Seine Manpage
150 [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-Reno/src/usr.bin/cut/cut.1
151 erwaehnt bereits die erwartete Konformitaet mit POSIX.2.
152 Nun sollte man wissen, dass POSIX.2 erst im September
153 1992 veroeffentlicht wurde, also gut zwei Jahren *nachdem* die
154 Manpage und das Programm geschrieben wurden. Das Programm
155 wurde also anhand von Arbeitsversionen des Standards
156 implementiert. Zweieinhalb Jahre Arbeit war immerhin schon in
157 den Standardisierungsprozess geflossen; bis zur
158 Fertigstellung sollte es aber noch weitere zwei Jahre dauern.
160 Trotz all dieser Jahreszahlen aus den 80er Jahren gehoert cut
161 aus Sicht des urspruenglichen Unix zu den juengeren Tools.
162 Wenn cut auch ein Jahrzehnt aelter als Linux, der Kernel, ist,
163 so war Unix doch schon ueber zehn Jahre alt, als cut das
164 erste Mal auftauchte. Insbesondere gehoerte cut noch nicht
165 zu Version 7 Unix, das die Ausgangsbasis aller modernen
166 Unix-Systeme darstellt. Die weit komplexeren Programme sed
167 und awk waren dort schon vertreten. Man muss sich also
168 fragen, warum cut ueberhaupt noch entwickelt wurde, wo es
169 schon zwei Programme gab, die die Aufgabe von cut abdeckten.
170 Ein Argument fuer cut war sicher seine Kompaktheit und
171 die damit verbundene Geschwindigkeit gegenueber dem damals
172 traegen awk. Diese schlanke Gestalt ist es auch, die der Unix
173 Philosopie entspricht: Mache eine Aufgabe und die richtig!
174 So bewaehrte sich cut. Es wurde in andere Unix Varianten
175 uebernommen, standardisiert und ist heutzutage ueberall
176 anzutreffen.
178 Die urspruengliche Variante (ohne -b) tauchte schon 1985 in
179 der System V Interface Definition, einer wichtigen formalen
180 Beschreibung von UNIX System V, und in allen relevanten
181 Standards seither auf. Mit POSIX.2 im Jahre 1992 wurde cut
182 zum ersten Mal in der heutigen Form (mit -b) standardisiert.
186 Multibyte-Behandlung
188 Nun sind der Bytemodus und die damit verbundene
189 Multibyte-Verarbeitung des POSIX-Zeichenmodus bereits seit
190 1992 standardisiert, wie steht es aber mit deren Umsetzung?
191 Welche Versionen implementieren denn den POSIX korrekt?
192 Die Situation ist mehrschichtig. Es gibt traditionelle
193 Implementierungen, die nur -c und -f kennen. Dann gibt es
194 Implementierungen die zwar -b kennen, es aber nur als Alias
195 fuer -c handhaben. Diese Implementierungen funktionieren mit
196 Single-Byte-Encodings (z.B. US-ASCII, Latin1) korrekt, bei
197 Multi-Byte-Encodings (z.B. UTF-8) verhaelt sich ihr -c aber
198 wie -b (und -n wird ignoriert). Schliesslich gibt es noch
199 Implementierungen, die -b und -c tatsaechlich POSIX-konform
200 implementieren.
202 Traditionelle Zwei-Modi-Implementierungen sind z.B. die von
203 System III, System V und die aller BSDs bis in die 90er.
205 Pseude-Multibyte-Implementierungen bieten GNU und die
206 modernen NetBSDs und OpenBSDs. Wie sehr dort der Schein von
207 POSIX-konformitaet gewahrt wird, ist unterschiedlich. Nicht
208 immer findet man klare Aussagen wie diese:
210 /* Since we don't support multi-byte characters, the -c and -b
211 options are equivalent, and the -n option is meaningless. */
213 [ XXX
215 Tatsaechlich standardkonforme Implementierungen, die
216 Multibytes korrekt handhaben, bekommt man bei einem modernen
217 FreeBSD und bei den Heirloom Tools. Bei FreeBSD hat Tim Robbins
218 (tjr) im Sommer 2004 den Zeichenmodus POSIX-konform reimplementiert.
219 [ https://svnweb.freebsd.org/base?view=revision&revision=131194
220 Warum die beiden anderen grossen BSDs diese Aenderung nicht
221 uebernommen haben, bleibt offen. Es scheint aber an der im
222 obigen Kommentar formulierten Grundausrichtung zu liegen.
224 Wie findet man als Nutzer heraus, ob beim cut(1) des eigenen
225 Systems Multibytes korrekt unterstuetzt werden? Zuerst ist
226 entscheidend, ob das System selbst mit einem Multibyte-Encoding
227 arbeitet, denn tut es das nicht, dann entsprechen sich Zeichen
228 und Bytes und die Frage eruebrigt sich. Man kann dazu nachschauen,
229 welches Locale eingestellt ist, aber einfacher ist es, ein
230 typisches Mehrbytezeichen, wie z.B. einen Umlaut, auszugeben
231 und zu schauen ob dieses in einem oder in mehreren Bytes
232 kodiert ist:
234 $ echo ä | od -c
235 0000000 303 244 \n
236 0000003
238 In diesem Fall sind es zwei Bytes: oktal 303 und 244 . (Den
239 Zeilenumbruch fuegt echo(1) hinzu.)
241 Mit dem Programm iconv(1) kann man Test explizit in bestimmte
242 Kodierungen konvertieren. Hier Beispiele, wie das Ergebnis
243 bei Latin1 und wie es bei UTF-8 aussieht.
245 $ echo ä | iconv -t latin1 | od -c
246 0000000 344 \n
247 0000002
249 $ echo ä | iconv -t utf8 | od -c
250 0000000 303 244 \n
251 0000003
253 Die Ausgabe auf dem eigenen System (ohne die iconv-Konvertierung)
254 wird recht sicher einer dieser beiden Ausgaben entsprechen.
256 Nun zum Test der cut-Implementierung. Hat man ein UTF-8-System,
257 dann sollte sich eine POSIX-konforme Implementierung so verhalten:
259 $ echo aä | ./cut -c -2 | od -c
260 0000000 a 303 244 \n
261 0000004
263 $ echo aä | ./cut -b -2 | od -c
264 0000000 a 303 \n
265 0000003
267 $ echo aä | ./cut -b -2 -n | od -c
268 0000000 a \n
269 0000002
271 Bei einer Implementierung, die -b und -c gleich behandelt,
272 ist die Ausgabe in allen drei Faellen wie die mittlere: Es
273 werden die ersten beiden Bytes ausgegeben.
277 Implementierungen
279 Nun zum Blick auf den Code. Hier soll eine Auswahl an
280 Implementierungen etwas genauer betrachtet werden. Fuer einen
281 ersten Eindruck ist der Umfang des Quellcodes hilfreich.
282 Typischerweise steigt dieser ueber die Jahre an. Diese
283 Beobachtung kann hier in der Tendenz, aber nicht in jedem Fall,
284 bestaetigt werden.
286 Die Unterstuetzung des Byte-Modus (-b) erfordert zwangslaeufig
287 mehr Code, deshalb ist zu erwarten, dass diejenigen
288 Implementierungen, die ihn haben, umfangreicher sind.
290 Codevergleich
292 SLOC Zeilen Bytes Gehoert zu Dateidatum Kategorie
293 -----------------------------------------------------------------
294 116 123 2966 System III 1980-04-11 (trad)
295 118 125 3038 4.3BSD-UWisc 1986-11-07 (trad)
296 200 256 5715 4.3BSD-Reno 1990-06-25 (trad)
297 200 270 6545 NetBSD 1993-03-21 (trad)
298 218 290 6892 OpenBSD 2008-06-27 (pseudo)
299 224 296 6920 FreeBSD 1994-05-27 (trad)
300 232 306 7500 NetBSD 2014-02-03 (pseudo)
301 340 405 7423 Heirloom 2012-05-20 (POSIX)
302 382 586 14175 GNU coreutils 1992-11-08 (pseudo)
303 391 479 10961 FreeBSD 2012-11-24 (POSIX)
304 588 830 23167 GNU coreutils 2015-05-01 (pseudo)
307 $ awk -F' +' '{printf("%d\t%d (%.2f)\t%d (%.2f)\t%s\t%s\t%s\n",
308 $1, $2, $2/$1, $3, $3/$1, $4, $5, $6);}' <sloc
309 116 123 (1.06) 2966 (25.57) System III 1980-04-11 (trad)
310 118 125 (1.06) 3038 (25.75) 4.3BSD-UWisc 1986-11-07 (trad)
311 200 256 (1.28) 5715 (28.57) 4.3BSD-Reno 1990-06-25 (trad)
312 200 270 (1.35) 6545 (32.73) NetBSD 1993-03-21 (trad)
313 218 290 (1.33) 6892 (31.61) OpenBSD 2008-06-27 (pseudo)
314 224 296 (1.32) 6920 (30.89) FreeBSD 1994-05-27 (trad)
315 232 306 (1.32) 7500 (32.33) NetBSD 2014-02-03 (pseudo)
316 340 405 (1.19) 7423 (21.83) Heirloom 2012-05-20 (POSIX)
317 382 586 (1.53) 14175 (37.11) GNU coreutils 1992-11-08 (pseudo)
318 391 479 (1.23) 10961 (28.03) FreeBSD 2012-11-24 (POSIX)
319 588 830 (1.41) 23167 (39.40) GNU coreutils 2015-05-01 (pseudo)
322 Einige Auffaelligkeiten:
324 Das Kandidatenfeld teilt sich grob in vier Gruppen: Die zwei urspruenglichen
325 Implementierungen, die sich nur minimal unterscheiden, mit gut 100 SLOCs.
326 Dann die fuenf BSD-Versionen mit knapp ueber 200 SLOCs. Anschliessend die
327 zwei POSIX-konformen Programme und die alte GNU-Version mit 350-400
328 SLOCs. Und zum Abschluss die moderne GNU-Variante mit fast 600 SLOCs.
330 Die Abweichung von logischen Codezeilen (nach der Definition von
331 SLOCcount) und der Anzahl von Zeilenumbruechen in der Datei erstreckt
332 sich ueber einen Faktor von 1.06 bei den aeltesten Vertretern bis zu
333 Faktor 1.5 bei GNU.
335 Betrachtet man die Abweichungen zwischen den logischen Codezeilen und der
336 Dateigroesse, so pendelt das Teilnehmerfeld zwischen 25 und 30 Bytes je
337 Anweisung. Die Heirloom-Implementierung weicht nach unten ab, die
338 GNU-Implementierungen nach oben.
342 Das cut in System III von 1980 ist, wie man anhand der SCCS-ID erkennen
343 kann bereits in Version 1.5. Die Vorversionen konnte ich aber leider nicht
344 ermitteln.
346 Schaut man sich die SCCS-IDs in den BSD-Quellen an, dann findet man dort
347 Versionsnummern, die die Entwicklung dokumentieren:
349 4.3bsd-uwisc "@(#)cut.c 1.3";
350 4.3bsd-reno "@(#)cut.c 5.3 (Berkeley) 6/24/90";
351 netbsd "@(#)cut.c 5.4 (Berkeley) 10/30/90";
352 freebsd "@(#)cut.c 8.1 (Berkeley) 6/6/93";
354 Die neueren BSD-Versionen enthalten zwar weiterhin eine SCCS-ID, diese
355 ist aber bei Version "8.3 (Berkeley) 5/4/95" stehen geblieben. Danach
356 wurde scheinbar von SCCS auf CSV oder SVN gewechselt.
358 Bei GNU befindet sich folgender Copyright-Vermerk im Code:
360 Copyright (C) 1997-2015 Free Software Foundation, Inc.
361 Copyright (C) 1984 David M. Ihnat
363 Wie aus weiteren Kommentaren zu entnehmen ist, wurde der Code von zuerst
364 von David MacKenzie und spaeter von Jim Meyering ueberarbeitet. Letzterer
365 hat den Code 1992 auch ins Versionkontrollsystem eingestellt. Weshalb
366 die Jahre zwischen 1992 und 1997 nicht im Copyright-Vermerk auftauchen,
367 ist unklar.
372 Beschreibungen
374 Interessant ist ein Vergleich der Kurzbeschreibungen von cut,
375 wie sie sich in der Titelzeile von Manpages oder manchmal auch
376 am Anfang der Quellcodedatei finden.
378 Die folgende Liste ist grob nach Zeit geordnet und nach
379 Abstammung gruppiert:
382 System III cut out selected fields of each line of a file
383 System III (src) cut and paste columns of a table (projection of a relation)
384 System V cut out selected fields of each line of a file
385 HP-UX cut out (extract) selected fields of each line of a file
387 4.3BSD-UWisc (src) cut and paste columns of a table (projection of a relation)
388 4.3BSD-Reno select portions of each line of a file
389 NetBSD select portions of each line of a file
390 OpenBSD 4.6 select portions of each line of a file
391 FreeBSD 1.0 select portions of each line of a file
392 FreeBSD 7.0 cut out selected portions of each line of a file
393 SunOS 4.1.3 remove selected fields from each line of a file
394 SunOS 5.5.1 cut out selected fields of each line of a file
396 Heirloom Tools cut out selected fields of each line of a file
398 POSIX cut out selected fields of each line of a file
400 GNU coreutils remove sections from each line of files
402 Minix select out columns of a file
404 Version 8 Unix rearrange columns of data
405 ``Unix Reader'' rearrange columns of text
408 Die zwei mit ``(src)'' markierten Beschreibungen sind aus
409 dem Quellcode entnommen, und verdeutlichen den Codetransfer.
410 POSIX ist ein Set von Standards, keine Implementierung. Der
411 ``Unix Reader'' ist ein rueckblickendes Textdokument von
412 Doug McIlroy, das das Auftreten von Tools in der Geschichte
413 des Research Unix zum Thema hat. Alle uebrigen Beschreibungen
414 entstammen den Manpages.
416 Zumeist ist mit der Zeit die POSIX-Beschreibung uebernommen
417 worden, wie beispielsweise bei FreeBSD zu sehen.
418 [ https://svnweb.freebsd.org/base?view=revision&revision=167101
420 Interessant ist, dass die GNU coreutils seit Anbeginn vom
421 Entfernen von Teilen der Eingabe sprechen, wohingegen die
422 Kommandozeilenangabe klar ein Auswaehlen darstellt. Die
423 Worte ``cut out'' sind vielleicht auch nicht klar genug.
424 HP-UX hat sie deshalb praezisiert.
426 Auch beim Begriff, was denn nun selektiert wird, ist man sich
427 uneins. Die einen reden von Feldern (POSIX), andere von
428 Abschnitten bzw. Teilen (BSD) und wieder andere von Spalten
429 (Research Unix). Ironischerweise leistet sich gerade Version
430 8 Unix, das eigentlich um eine sehr treffende Weltsicht
431 bemueht ist, mit ``rearrange columns of data'' die
432 unzutreffendste der Beschreibungen.
437 Autoreninfo
439 Markus Schnalke interessiert sich fuer die Hintergruende
440 von Unix und seinen Werkzeugen. Fuer die Erarbeitung dieses
441 Textes wurde er regelrecht zum Historiker.
444 Lizenz
445 CC0 (und kann damit auch unter CC BY-SA 4.0 Unported
446 veroeffentlicht werden)