docs/cut

view cut.txt @ 10:7e1214b556b9

Zwischenstand
author markus schnalke <meillo@marmaro.de>
date Mon, 11 May 2015 07:09:00 +0200
parents e67bd0d48bd6
children 04a8a33fc48a
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, denn es ist ein schoenes, praktisches und anschauliches
12 Helferlein. Hier soll ein wenig hinter seine Fassade geschaut
13 werden.
16 Funktionsweise
18 Urspruenglich hatte cut zwei Modi, die spaeter um einen dritten
19 erweitert wurden. Cut schneidet entweder gewuenschte Zeichen aus
20 den Zeilen der Eingabe oder gewuenschte, durch Trennzeichen
21 definierte, Felder.
23 Der Zeichenmodus ist optimal geeignet um Festbreitenformate zu
24 zerteilen. So kann man damit beispielsweise bestimmte
25 Zugriffsrechte aus der Ausgabe von `ls -l' ausschneiden, in
26 diesem Beispiel die Rechte des Besitzers:
28 $ ls -l foo | cut -c 2-4
29 rw-
31 Oder die Schreibrechte des Besitzers, der Gruppe und der
32 Welt:
34 $ ls -l | cut -c 3,6,9
35 ww-
37 Mit cut lassen sich aber auch Strings kuerzen.
39 $ long=12345678901234567890
40 $ echo "$long" | cut -c -10
41 1234567890
43 Dieser Befehl gibt die ersten maximal 10 Zeichen von
44 `$long' aus. (Alternativ kann man hierfuer auch `printf
45 "%.10s\n" "$long"' verwenden.)
47 Geht es aber nicht um die Darstellung von Zeichen, sondern um
48 ihre Speicherung, dann ist `-c' nicht unbedingt geeignet.
49 Frueher, als US-ASCII als Zeichensatz und -kodierung
50 noch omnipraesent war, wurde jedes Zeichen mit genau einem
51 Byte gespeichert. Somit selektierte `cut -c' gleichermassen
52 sowohl Ausgabezeichen als auch Bytes. Mit dem Aufkommen von
53 Multibyte-Kodierungen (wie UTF-8) musste man sich jedoch von
54 dieser Annahme loesen. In diesem Zug bekam cut mit
55 POSIX.2-1992 einen Bytemodus (Option `-b'). Will man
56 also nur die ersten maximal 500 Bytes vor dem
57 Newline-Zeichen stehen haben (und den Rest stillschweigend
58 ignorieren), dann macht man das mit:
60 $ cut -b -500
62 Den Rest kann man sich mit `cut -b 501-' einfangen. Diese
63 Funktion ist insbesondere fuer POSIX wichtig, da man so
64 Textdateien mit begrenzter Zeilenlaenge erzeugen kann.
65 [ http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cut.html#tag_20_28_17
67 Auch wenn der Bytemodus neu eingefuehrt wurde, so sollte er
68 sich doch nur so verhalten wie der alte Zeichenmodus normalerweise
69 implementiert war. Beim Zeichenmodus aber wurde durch POSIX.2
70 eine andere Implementierungsweise gefordert. Das Problem war
71 also nicht, den neuen Bytemodus zu implementieren, sondern
72 den Zeichenmodus neu zu implementieren.
74 Neben dem Zeichen- und Byte-Modus bietet cut noch den
75 Feld-Modus, den man mit `-f' einleitet. Mit ihm
76 koennen Felder ausgewaehlt werden. Das Trennzeichen (per
77 Default der Tab) kann mit `-d' geaendert werden.
79 Der typische Anwendungsfall fuer cut im Feld-Modus ist die
80 Auswahl von Information aus der passwd-Datei. So z.B. der
81 Benutzername, seine ID und das Homeverzeichnis:
83 $ cut -d: -f1,3,6 /etc/passwd
84 root:0:/root
85 bin:1:/bin
86 daemon:2:/sbin
87 mail:8:/var/spool/mail
88 ...
90 (Die Argumente fuer die Optionen koennen bei cut uebrigens
91 mit Whitespace abgetrennt oder direkt angehaengt folgen.)
93 Dieser Feld-Modus ist fuer einfache tabellarische Dateien,
94 wie eben die passwd, gut geeignet. Er kommt aber schnell an
95 seine Grenzen. Gerade der haeufige Fall, dass an Whitespace
96 in Felder geteilt werden soll, wird damit nicht abgedeckt.
97 Der Delimiter kann nur genau ein Zeichen sein. Es kann also
98 nicht sowohl an Leerzeichen als auch an Tabs getrennt werden.
99 Auch unterteilt cut an jedem Trennzeichen. Zwei aneinander
100 stehende Trennzeichen fuehren zu einem leeren Feld. Dieses
101 Verhalten widerspricht den Erwartungen, die man an die
102 Verarbeitung einer Datei mit Whitespace-getrennten Feldern
103 hat. Manche Implementierungen von cut, z.B. die von FreeBSD,
104 haben aber Erweiterungen, die das gewuenschte Verhalten fuer
105 Whitespace-getrennte Felder bieten. Ansonsten, d.h. wenn
106 man portabel bleiben will, verwendet man awk in diesen
107 Faellen.
109 Awk bietet noch eine weitere Funktion, die cut missen
110 laesst: Das Tauschen der Feld-Reihenfolge in der Ausgabe. Bei
111 cut ist die Reihenfolge der Feldauswahlangabe irrelevant; ein
112 Feld kann selbst mehrfach angegeben werden. So gibt der Aufruf
113 von `cut -c 5-8,1,4-6' die Zeichen Nummer 1, 4, 5, 6, 7 und 8
114 in genau dieser Reihenfolge aus. Die Auswahl entspricht damit
115 der Mengenlehre in der Mathematik: Jedes angegebene Feld wird
116 Teil der Ergebnismenge. Die Felder der Ergebnismenge sind
117 dabei immer gleich geordnet wie in der Eingabe. Um die Worte
118 der Manpage XXX von Version 8 Unix wiederzugeben: ``In data base
119 parlance, it projects a relation.''
120 [ XXX
121 Cut fuehrt also die Datenbankoperation Projektion auf
122 Textdateien aus. Die Wikipedia erklaert das folgendermassen:
124 Die Projektion entspricht der Projektionsabbildung aus der
125 Mengenlehre und kann auch Attributbeschränkung genannt
126 werden. Sie extrahiert einzelne Attribute aus der
127 ursprünglichen Attributmenge und ist somit als eine Art
128 Selektion auf Spaltenebene zu verstehen, das heißt, die
129 Projektion blendet Spalten aus.
131 [ http://de.wikipedia.org/wiki/Projektion_(Informatik)#Projektion
134 Geschichtliches
136 Cut erblickte 1982 mit dem Release von UNIX System III das
137 Licht der oeffentlichen Welt. Wenn man die Quellen von System
138 III durchforstet, findet man die Quellcodedatei cut.c mit dem
139 Zeitstempel 1980-04-11.
140 [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=SysIII/usr/src/cmd
141 Das ist die aelteste Manifestation des Programms, die ich
142 aufstoebern konnte. Allerdings spricht die sccsid im
143 Quellcode von Version 1.5. Es muss also noch eine
144 Vorgeschichte geben. Zu dieser habe ich leider keinen Zugang
145 gefunden.
146 XXX mail an TUHS
148 Nun ein Blick auf die BSD-Linie: Dort ist mein
149 fruehester Fund ein cut.c mit dem Dateimodifikationsdatum
150 1986-11-07
151 [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-UWisc/src/usr.bin/cut
152 als Teil der Spezialversion 4.3BSD-UWisc,
153 [ http://gunkies.org/wiki/4.3_BSD_NFS_Wisconsin_Unix
154 die im Januar 1987 veroeffentlicht wurde.
155 Die Implementierung unterscheidet sich nur minimal von der
156 in System III.
157 Im bekannteren 4.3BSD-Tahoe (1988) taucht cut nicht auf.
158 Das darauf folgende 4.3BSD-Reno (1990) liefert aber wieder
159 ein cut mit aus. Dieses cut ist ein von Adam S. Moskowitz und
160 Marciano Pitargue neu implementiertes cut, das 1989 in BSD
161 aufgenommen wurde.
162 [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-Reno/src/usr.bin/cut
163 Seine Manpage
164 [ http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-Reno/src/usr.bin/cut/cut.1
165 erwaehnt bereits die erwartete Konformitaet mit POSIX.2.
166 XXX 2 oder 3 modi?
167 Nun sollte man wissen, dass POSIX.2 erst im September
168 1992 veroeffentlicht wurde, also gut zwei Jahren nachdem die
169 Manpage und das Programm geschrieben wurden. Das Programm
170 wurde folglich anhand von Arbeitsversionen des Standards
171 implementiert. Zweieinhalb Jahre Arbeit war immerhin schon in
172 den Standardisierungsprozess geflossen; bis zur
173 Fertigstellung sollte es aber noch weitere zwei Jahre dauern.
175 Das cut der GNU Coreutils enthaelt einen Copyrightvermerk von
176 David M. Ihnat aus dem Jahr 1984.
178 Trotz all dieser Jahreszahlen aus den 80er Jahren gehoert cut,
179 aus Sicht des urspruenglichen Unix, zu den juengeren Tools.
180 Wenn cut auch ein Jahrzehnt aelter als Linux, der Kernel, ist,
181 so war Unix doch schon ueber zehn Jahre alt, als cut das
182 erste Mal auftauchte. Insbesondere gehoerte cut auch noch nicht
183 zu Version 7 Unix, das die Ausgangsbasis aller modernen
184 Unix-Systeme darstellt. Die weit komplexeren Programme sed
185 und awk waren dort schon vertreten. Man muss sich also
186 fragen, warum cut ueberhaupt noch entwickelt wurde, wo es
187 schon zwei Programme gab, die die Funktion von cut abdecken
188 konnten. Ein Argument fuer cut war sicher seine Kompaktheit und
189 die damit verbundene Geschwindigkeit gegenueber dem damals
190 traegen awk. Diese schlanke Gestalt ist es auch, die der Unix
191 Philosopie entspricht: Mache eine Aufgabe und die richtig!
192 Cut ueberzeugte. Es wurde in andere Unix Varianten uebernommen,
193 standardisiert und ist heutzutage ueberall anzutreffen.
195 Die urspruengliche Variante (ohne -b) wurde schon 1985 in
196 der System V Interface Definition, einer wichtigen formalen
197 Beschreibung von UNIX System V, spezifiziert und tauchte
198 anschliessend in allen relevanten Standards auf. Mit POSIX.2
199 im Jahre 1992 wurde cut zum ersten Mal in der heutigen Form
200 (mit -b) standardisiert.
201 XXX sicher? s.o.
204 Multibyte-Unterstuetzung
206 Nun sind der Bytemodus und die damit verbundene
207 Multibyte-Verarbeitung des POSIX-Zeichenmodus bereits seit
208 1992 standardisiert, wie steht es aber mit deren Umsetzung?
209 Welche Versionen implementieren POSIX korrekt?
210 Die Situation ist dreiteilig: Es gibt traditionelle
211 Implementierungen, die nur -c und -f kennen. Dann gibt es
212 Implementierungen die -b zwar kennen, es aber lediglich als Alias
213 fuer -c handhaben. Diese Implementierungen funktionieren mit
214 Single-Byte-Encodings (z.B. US-ASCII, Latin1) korrekt, bei
215 Multi-Byte-Encodings (z.B. UTF-8) verhaelt sich ihr -c aber
216 wie -b (und -n wird ignoriert). Schliesslich gibt es noch
217 Implementierungen, die -b und -c tatsaechlich POSIX-konform
218 implementieren.
220 Traditionelle Zwei-Modi-Implementierungen sind z.B. die von
221 System III, System V und die aller BSDs bis in die 90er.
223 Pseudo-Multibyte-Implementierungen bieten GNU und die
224 modernen NetBSDs und OpenBSDs. Wie sehr dort ein Schein von
225 POSIX-Konformitaet gewahrt wird, ist unterschiedlich. Nicht
226 immer findet man klare Aussagen wie diese:
228 /* Since we don't support multi-byte characters, the -c and -b
229 options are equivalent, and the -n option is meaningless. */
231 [ XXX
233 Tatsaechlich standardkonforme Implementierungen, die
234 Multibytes korrekt handhaben, bekommt man bei einem modernen
235 FreeBSD und bei den Heirloom Tools. Bei FreeBSD hat Tim Robbins
236 im Sommer 2004 den Zeichenmodus POSIX-konform reimplementiert.
237 [ https://svnweb.freebsd.org/base?view=revision&revision=131194
238 Warum die beiden anderen grossen BSDs diese Aenderung nicht
239 uebernommen haben, bleibt offen. Es scheint aber an der im
240 obigen Kommentar formulierten Grundausrichtung zu liegen.
242 Wie findet man als Nutzer heraus, ob beim cut(1) des eigenen
243 Systems Multibytes korrekt unterstuetzt werden? Zuerst ist
244 entscheidend, ob das System selbst mit einem Multibyte-Encoding
245 arbeitet, denn tut es das nicht, dann entsprechen sich naemlich
246 Zeichen und Bytes und die Frage eruebrigt sich. Man kann das
247 herausfinden indem man sich das Locale anschaut, aber einfacher
248 ist es, ein typisches Mehrbytezeichen, wie z.B. einen Umlaut,
249 auszugeben und zu schauen ob dieses in einem oder in mehreren
250 Bytes kodiert ist:
252 $ echo ä | od -c
253 0000000 303 244 \n
254 0000003
256 In diesem Fall sind es zwei Bytes: oktal 303 und 244 . (Den
257 Zeilenumbruch fuegt echo(1) hinzu.)
259 Mit dem Programm iconv(1) kann man Text explizit in bestimmte
260 Kodierungen konvertieren. Hier Beispiele, wie die Ausgabe
261 bei Latin1 und wie sie bei UTF-8 aussieht.
263 $ echo ä | iconv -t latin1 | od -c
264 0000000 344 \n
265 0000002
267 $ echo ä | iconv -t utf8 | od -c
268 0000000 303 244 \n
269 0000003
271 Die Ausgabe auf dem eigenen System (ohne die iconv-Konvertierung)
272 wird recht sicher einer dieser beiden Ausgaben entsprechen.
274 Nun zum Test der cut-Implementierung. Hat man ein UTF-8-System,
275 dann sollte sich eine POSIX-konforme Implementierung so verhalten:
277 $ echo ä | ./cut -c 1 | od -c
278 0000000 303 244 \n
279 0000003
281 $ echo ä | ./cut -b 1 | od -c
282 0000000 303 \n
283 0000002
285 $ echo ä | ./cut -b 1 -n | od -c
286 0000000 \n
287 0000001
289 Bei einer Pseudo-POSIX-Implementierung ist die Ausgabe in
290 allen drei Faellen wie die mittlere: Es wird das erste Byte
291 ausgegeben.
294 Implementierungen
296 Nun ein Blick auf den Code. Betrachtet wird eine Auswahl an
297 Implementierungen.
299 Fuer einen ersten Eindruck ist der Umfang des Quellcodes
300 hilfreich. Typischerweise steigt dieser ueber die Jahre an. Diese
301 Beobachtung kann hier in der Tendenz, aber nicht in jedem Fall,
302 bestaetigt werden. Die Unterstuetzung des Byte-Modus (-b)
303 erfordert zwangslaeufig mehr Code, deshalb sind die
304 POSIX-konformen Implementierungen tendenziell umfangreicher.
307 SLOC Zeilen Bytes Gehoert zu Dateidatum Kategorie
308 -----------------------------------------------------------------
309 116 123 2966 System III 1980-04-11 (trad)
310 118 125 3038 4.3BSD-UWisc 1986-11-07 (trad)
311 200 256 5715 4.3BSD-Reno 1990-06-25 (trad)
312 200 270 6545 NetBSD 1993-03-21 (trad)
313 218 290 6892 OpenBSD 2008-06-27 (pseudo)
314 224 296 6920 FreeBSD 1994-05-27 (trad)
315 232 306 7500 NetBSD 2014-02-03 (pseudo)
316 340 405 7423 Heirloom 2012-05-20 (POSIX)
317 382 586 14175 GNU coreutils 1992-11-08 (pseudo)
318 391 479 10961 FreeBSD 2012-11-24 (POSIX)
319 588 830 23167 GNU coreutils 2015-05-01 (pseudo)
320 XXX verlinken
323 Das Kandidatenfeld teilt sich grob in vier Gruppen: (1) Die zwei
324 urspruenglichen Implementierungen, die sich nur minimal
325 unterscheiden, mit gut 100 SLOCs. (2) Die fuenf BSD-Versionen mit
326 gut 200 SLOCs. (3) Die zwei POSIX-konformen Programme und
327 die alte GNU-Version mit 340-390 SLOCs. Und (4) die moderne
328 GNU-Variante mit fast 600 SLOCs.
330 Die Abweichung zwischen logischen Codezeilen (SLOC, ermittelt mit
331 SLOCcount) und der Anzahl von Zeilenumbruechen in der Datei (`wc
332 -l') erstreckt sich ueber einen Faktor von 1.06 bei den aeltesten
333 Vertretern bis zu Faktor 1.5 bei GNU. Der groesste
334 Einflussfaktor darauf sind Leerzeilen, reine Kommentarzeilen und
335 die Groesse des Lizenzblocks am Dateianfang.
337 Betrachtet man die Abweichungen zwischen den logischen Codezeilen
338 und der Dateigroesse (`wc -c'), so pendelt das Teilnehmerfeld
339 zwischen 25 und 30 Bytes je Anweisung. Die Heirloom-Implementierung
340 weicht mit nur 21 nach unten ab, die GNU-Implementierungen mit
341 fast 40 nach oben. Dies liegt bei GNU hauptsaechlich an deren
342 Programmierstil, mit spezieller Einrueckung und langen Bezeichnern.
343 Ob man die Heirloom-Implementierung als besonders kryptisch
344 oder als besonders elegant bezeichnen will, das soll der
345 eigenen Einschaetzung des Lesers ueberlassen bleiben.
348 Schaut man sich die SCCS-IDs (die vom damaligen
349 Versionskontrollsystem eingefuegt wurden) in den BSD-Quellen an,
350 dann findet man dort Versionsnummern, die die Entwicklung
351 dokumentieren:
353 4.3bsd-uwisc "@(#)cut.c 1.3";
354 4.3bsd-reno "@(#)cut.c 5.3 (Berkeley) 6/24/90";
355 netbsd "@(#)cut.c 5.4 (Berkeley) 10/30/90";
356 freebsd "@(#)cut.c 8.1 (Berkeley) 6/6/93";
358 Die neueren BSD-Versionen enthalten zwar weiterhin eine SCCS-ID, diese
359 ist aber bei Version "8.3 (Berkeley) 5/4/95" stehen geblieben. Danach
360 wurde scheinbar von SCCS auf ein anderes
361 Versionskontrollsystem gewechselt.
362 XXX
364 Bei GNU befindet sich folgender Copyright-Vermerk im Code:
366 Copyright (C) 1997-2015 Free Software Foundation, Inc.
367 Copyright (C) 1984 David M. Ihnat
369 Der Code hat also ziemlich alte Urspruenge. Wie aus weiteren
370 Kommentaren zu entnehmen ist, wurde der Code zuerst von David
371 MacKenzie und spaeter von Jim Meyering ueberarbeitet. Letzterer
372 hat den Code 1992 auch ins Versionkontrollsystem eingestellt.
373 Weshalb die Jahre zwischen 1992 und 1997 nicht im Copyright-Vermerk
374 auftauchen, ist unklar.
377 Beschreibungen
379 Interessant ist auch ein Vergleich der Kurzbeschreibungen von
380 cut, wie sie sich in der Titelzeile von Manpages oder manchmal
381 auch am Anfang der Quellcodedatei finden. Die folgende Liste
382 ist grob zeitlich geordnet und nach Abstammung gruppiert:
385 System III cut out selected fields of each line of a file
386 System III (src) cut and paste columns of a table (projection of a relation)
387 System V cut out selected fields of each line of a file
388 HP-UX cut out (extract) selected fields of each line of a file
390 4.3BSD-UWisc (src) cut and paste columns of a table (projection of a relation)
391 4.3BSD-Reno select portions of each line of a file
392 NetBSD select portions of each line of a file
393 OpenBSD 4.6 select portions of each line of a file
394 FreeBSD 1.0 select portions of each line of a file
395 FreeBSD 10.0 cut out selected portions of each line of a file
396 SunOS 4.1.3 remove selected fields from each line of a file
397 SunOS 5.5.1 cut out selected fields of each line of a file
399 Heirloom Tools cut out selected fields of each line of a file
400 Heirloom Tools (src) cut out fields of lines of files
402 GNU coreutils remove sections from each line of files
404 Minix select out columns of a file
406 Version 8 Unix rearrange columns of data
407 ``Unix Reader'' rearrange columns of text
409 POSIX cut out selected fields of each line of a file
412 Die mit ``(src)'' markierten Beschreibungen sind aus dem
413 jeweiligen Quellcode entnommen.
414 Der POSIX-Eintrag enthaelt die Beschreibung des Standards.
415 Der ``Unix Reader'' ist ein rueckblickendes Textdokument von
416 Doug McIlroy, das das Auftreten von Tools in der Geschichte
417 des Research Unix zum Thema hat.
418 [ XXX
419 Eigentlich sollte seine
420 Beschreibung der in Version 8 Unix entsprechen. Die
421 Abweichung koennte sowohl ein Uebertragungsfehler als auch
422 eine nachtraegliche Korrektur sein.
423 Alle uebrigen Beschreibungen entstammen den Manpages.
425 Oft ist mit der Zeit die POSIX-Beschreibung uebernommen
426 worden, wie beispielsweise bei FreeBSD zu sehen.
427 [ https://svnweb.freebsd.org/base?view=revision&revision=167101
428 XXX fixme!
430 Interessant ist, dass die GNU coreutils seit Anbeginn vom
431 Entfernen von Teilen der Eingabe sprechen, wohingegen die
432 Kommandozeilenangabe klar ein Auswaehlen darstellt. Die
433 Worte ``cut out'' sind vielleicht auch nur etwas zu
434 missverstaendlich. HP-UX hat sie deshalb praezisiert.
436 Auch beim Begriff, was selektiert wird, ist man sich
437 uneins. Die einen reden von Feldern (POSIX), andere von
438 Abschnitten bzw. Teilen (BSD) und wieder andere von Spalten
439 (Research Unix). Ironischerweise leistet sich gerade Version
440 8 Unix, das eigentlich um eine sehr treffende Weltsicht
441 bemueht ist, mit ``rearrange columns of data'' die
442 unzutreffendste der Beschreibungen.
445 Autoreninfo
447 Markus Schnalke interessiert sich fuer die Hintergruende
448 von Unix und seinen Werkzeugen. Fuer die Erarbeitung dieses
449 Textes wurde er regelrecht zum Historiker.
452 Lizenz
454 CC0 (und kann damit auch unter CC BY-SA 4.0 Unported
455 veroeffentlicht werden)