Gentoo Archives: gentoo-commits

From: "John Christian Stoddart (chiguire)" <chiguire@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] gentoo commit in xml/htdocs/doc/es/articles: l-awk2.xml
Date: Thu, 21 Feb 2008 12:39:55
Message-Id: E1JSAiN-00060m-7r@stork.gentoo.org
1 chiguire 08/02/21 12:39:51
2
3 Added: l-awk2.xml
4 Log:
5 first spanish translation (jesus guerrero)
6
7 Revision Changes Path
8 1.1 xml/htdocs/doc/es/articles/l-awk2.xml
9
10 file : http://sources.gentoo.org/viewcvs.py/gentoo/xml/htdocs/doc/es/articles/l-awk2.xml?rev=1.1&view=markup
11 plain: http://sources.gentoo.org/viewcvs.py/gentoo/xml/htdocs/doc/es/articles/l-awk2.xml?rev=1.1&content-type=text/plain
12
13 Index: l-awk2.xml
14 ===================================================================
15 <?xml version='1.0' encoding="UTF-8"?>
16 <!-- $Header: /var/cvsroot/gentoo/xml/htdocs/doc/es/articles/l-awk2.xml,v 1.1 2008/02/21 12:39:50 chiguire Exp $ -->
17 <!DOCTYPE guide SYSTEM "/dtd/guide.dtd">
18
19 <guide link="/doc/en/articles/l-awk2.xml" lang="es" disclaimer="articles">
20 <title>Awk mediante ejemplos, Parte 2</title>
21
22 <author title="Author">
23 <mail link="drobbins@g.o">Daniel Robbins</mail>
24 </author>
25 <author title="Traductor">
26 <mail link="i92guboj@×××××.es">Jesús Guerrero</mail>
27 </author>
28
29 <abstract>
30 En esta secuela de la introducción a awk, Daniel Robbins continúa
31 explorando awk, un gran lenguaje de extraño nombre. Daniel nos
32 enseñará como manejar registros compuestos por múltiples líneas, usar
33 construcciones de bucle y crear y usar matrices en awk. Al final de
34 este artículo estarás versado en una amplia variedad de
35 funcionalidades, y estarás preparado para escribir tus propios guiones
36 en awk.
37 </abstract>
38
39 <!-- The original version of this article was published on IBM
40 developerWorks, and is property of Westtech Information
41 Services. This document is an updated version of the original
42 article, and contains various improvements made by the Gentoo
43 Linux Documentation team -->
44
45 <version>1.4</version>
46 <date>2005-10-31</date>
47
48 <chapter>
49 <title>Registros, bucles y matrices</title>
50 <section>
51 <title>Registros compuestos de múltibles líneas</title>
52 <body>
53
54 <p>
55 Awk es una herramienta excelente para leer y procesar datos
56 estructurados, tales como el contenido del fichero de sistema
57 <path>/etc/passwd</path>. <path>/etc/passwd</path> contiene la base
58 de datos de usuarios en UNIX, y es un fichero de texto con campos
59 delimitados por el signo de dos puntos. Además contiene información
60 importante como las cuentas de usuario existentes y los números de ID
61 de los usuarios, entre otras cosas. En <uri
62 link="/doc/es/articles/l-awk1.xml">mi artículo anterior</uri>, mostré
63 como awk puede procesar este fichero de forma rápida. Todo lo que se
64 necesitaba era establecer la variables de separador de campos FS al
65 caracter ":".
66 </p>
67
68 <p>
69 Al establecer la variable FS de forma correcta, awk puede ser
70 configurado para interpretar casi cualquier bloque de datos
71 estructurado, siempre que haya un registro por línea. Sin embargo, FS
72 por sí sola no nos solucionará el problema si necesitamos procesar
73 registros que ocupen varias líneas. En estas situaciones, también
74 necesitaremos modificar la variable RS, que es el separador de
75 registro. Esta variable le dice a awk cuando se acaba un registro y
76 cuando comienza el siguiente.
77 </p>
78
79 <p>
80 Como ejemplo, podemos considerar la tarea de procesar la lista de
81 direcciones de los participantes en el programa federal de protección
82 de testigos:
83 </p>
84
85 <pre caption="Registros de ejemplo para la lista del programa federal de
86 protección de testigos">
87 Jimmy the Weasel
88 100 Pleasant Drive
89 San Francisco, CA 12345
90
91 Big Tony
92 200 Incognito Ave.
93 Suburbia, WA 67890
94 </pre>
95
96 <p>
97 Lo ideal sería que awk reconociera cada grupo de tres líneas como un
98 registro, en lugar de reconocer cada línea como un registro
99 individual. Nuestro código sería mucho más simple si awk reconociera
100 la primera línea del registro como el primer campo ($1), la dirección
101 como el segundo campo ($2), y la ciudad, estado y código postal como
102 el tercer campo ($3). Y eso lo conseguimos con el código siguiente:
103 </p>
104
105 <pre caption="Extrayendo un campo de la dirección">
106 BEGIN {
107 FS="\n"
108 RS=""
109 }
110 </pre>
111
112 <p>
113 Al establecer FS a "\n" le decimos a awk que cada campo aparecerá en
114 su propia línea. Estableciendo RS a "" le decimos a awk que cada
115 registro está separado por una línea en blanco. Una vez que awk sabe
116 como la entrada está formateada, puede realizar todo el trabajo por sí
117 solo, y el resto del guión es simple. Veamos como quedaría un guión
118 completo para procesar esta lista de direcciones e imprimir cada
119 registro completo en una sola línea, en campos separados por comas.
120 </p>
121
122 <pre caption="Guión completo">
123 BEGIN {
124 FS="\n"
125 RS=""
126 }
127 { print $1 ", " $2 ", " $3 }
128 </pre>
129
130
131 <p>
132 Si el guión se salvó como <path>address.awk</path>, y la información
133 sobre las direcciones está en un fichero llamado
134 <path>address.txt</path>, puedes ejecutar este guión escribiendo
135 <c>awk -f address.awk address.txt</c>. Este código producirá esta
136 salida:
137 </p>
138
139 <pre caption="La salida del guión">
140 Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345
141 Big Tony, 200 Incognito Ave., Suburbia, WA 67890
142 </pre>
143 </body>
144 </section>
145
146 <section>
147 <title>OFS y ORS</title>
148 <body>
149
150 <p>
151 En la instrucción print se puede ver que awk une cadenas de caracteres
152 que son colocadas juntas en una línea. Hemos usado esta característica
153 para insertar una coma y un espacio (", ") entre los tres campos de
154 datos de cada línea. Ahora, si bien este método funciona, no es el más
155 elegante. En lugar de insertar la cadena fija (", ") entre los campos,
156 podremos indicarle a awk que lo haga por nosotros. Ésto lo haremos
157 mediante otra variable, que se llama OFS. Mira esta muestra de código.
158 </p>
159
160 <pre caption="Código de ejemplo">
161 print "Hola,","Jim!"
162 </pre>
163
164 <p>
165 Las comas en esta línea no son parte de la cadena literal. En su
166 lugar, ellas le dicen a awk que "Hello", "there", y "Jim!" son campos
167 separados, y que la variable OFS deberá imprimirse entre ambas
168 cadenas. Por defecto, awk produce esto:
169 </p>
170
171 <pre caption="Salida de awk">
172 Hola, Jim!
173 </pre>
174
175 <p>
176 Esto nos enseña, que, por defecto, OFS es igual a " ", un solo espacio
177 en blanco. Sin embargo, podemos redefinirlo de forma fácil para que
178 inserte nuestro separador de cambio personalizado. Aquí está una
179 versión actualizada del guión original <path>address.awk</path> que
180 usa ÖFS para añadir estos separadores intermedios (", ").
181 </p>
182
183 <pre caption="Redefinir OFS">
184 BEGIN {
185 FS="\n"
186 RS=""
187 OFS=", "
188 }
189 { print $1, $2, $3 }
190 </pre>
191
192 <p>
193 Awk también tiene una variable especial ORS, llamada "separador de
194 campos de salida". Ésta se establece por defecto a ("\n"), con ella
195 podemos controlar qué separador se va a imprimir al final de cada
196 print. Por defecto viene a una sola línea, si lo queremos con doble
197 espaciado, entonces podemos establecer ORS para que sea igual a
198 "\n\n". O, si queremos que los registros estén separados por un simple
199 espacio (sin salto de línea), entonces asignariamos un espacio en
200 blanco " " a ORS.
201 </p>
202 </body>
203 </section>
204
205 <section>
206 <title>De múltiples líneas a tabulado</title>
207 <body>
208
209 <p>
210 Imaginemos que lo que queremos es escribir un guión que convierta los
211 registros que ocupan varias líneas en otros que tengan una sola línea
212 cuyos campos vayan separados por tabuladores para importarlos en una
213 hoja de cálculo. Tras usar una versión ligeramente modificada de
214 <path>address.awk</path>, quedaría claro que nuestro programa solo
215 funciona para direcciones compuestas por tres líneas. Si el programa
216 encontrara el siguiente registro, la cuarta línea sería descartada
217 silencionsamente:
218 </p>
219
220 <pre caption="Ejemplo de registro">
221 Cousin Vinnie
222 Vinnie's Auto Shop
223 300 City Alley
224 Sosueme, OR 76543
225 </pre>
226
227 <p>
228 Para manejar situaciones como esta, sería bueno que nuestro código
229 pudiera ver el número de campos por registro y tenerlo en cuenta para
230 imprimirlos más tarde en orden. Ahora mismo, el código solo imprime
231 los tres primeros campos de la dirección. El siguiente código hace
232 justo lo que queremos:
233 </p>
234
235 <pre caption="Código mejorado">
236 BEGIN {
237 FS="\n"
238 RS=""
239 ORS=""
240 }
241
242 {
243 x=1
244 while ( x&lt;NF ) {
245 print $x "\t"
246 x++
247 }
248 print $NF "\n"
249 }
250 </pre>
251
252 <p>
253 Primero establecemos el separador de campo FS a "\n" y el separador de
254 registros RS a "" para que awk interprete las direcciones multilinea
255 de forma correcta, como antes. Entonces ponemos la variable separadora
256 del registro de salida ORS a "", lo cual causará que la instrucción
257 print no escriba un caracter de nueva línea tras cada registro. Esto
258 significa que si queremos comenzar una nueva línea, tendremos que
259 imprimirla explícitamente con print "\n".
260 </p>
261
262 <p>
263 En el bloque de código principal, creamos una variable x que contiene
264 el número de campo actual. Inicialmente se pone a 1. Entonces usamos
265 un bucle while (idéntico al while del lenguaje C) para iterar a través
266 de todos los campos excepto el último, imprimiendo el campo
267 correspondiente en cada vuelta, y además un caracter de
268 tabulación. Finalmente imprimimos el último campo y un caracter de
269 salto de línea. De nuevo, como ORS está puesto a "", print no
270 imprimirá los caracteres de nueva línea por nosotros. La salida del
271 programa se verá de esta forma, que es exáctamente lo que queríamos:
272 </p>
273
274 <pre caption="La salida deseada. No es bonita, pero al estar delimitada por
275 tabuladores es fácil de importar en una hoja de cálculo">
276 Jimmy the Weasel 100 Pleasant Drive San Francisco, CA 12345
277 Big Tony 200 Incognito Ave. Suburbia, WA 67890
278 Cousin Vinnie Vinnie's Auto Shop 300 City Alley Sosueme, OR 76543
279 </pre>
280 </body>
281 </section>
282
283 <section>
284 <title>Estructuras de bucle</title>
285 <body>
286
287 <p>
288 Ya hemos visto la construcción while de awk, que es idéntica a la de
289 C. Awk también tiene una construcción "do...while", que evalúa la
290 condición al final del bloque de código, en lugar de al principio como
291 se haría en un bloque while estándar. Es similar al "repeat...until"
292 que podemos encontrar en otros lenguajes. Aquí hay un ejemplo:
293 </p>
294
295 <pre caption="ejemplo de do...while">
296 {
297 count=1
298 do {
299 print "Me imprimo una vez al menos"
300 } while ( count != 1 )
301 }
302 </pre>
303
304 <p>
305 Como la condición es evaluada tras el bloque, un bucle "do...while",
306 al contrario que un bucle while normal, siempre se ejecuta al menos
307 una vez. Por contra, un bucle normal no se ejecutará jamás si la
308 condición inicial es falsa.
309 </p>
310 </body>
311 </section>
312
313 <section>
314 <title>bucles for</title>
315 <body>
316
317 <p>
318 Awk permite la creación de bucles for, que, tal y como los while, son
319 idénticos a sus versiones en C:
320 </p>
321
322 <pre caption="Bucle de ejemplo">
323 for ( initial assignment; comparison; increment ) {
324 code block
325 }
326 </pre>
327
328 <p>
329 Aquí hay un ejemplo rápido:
330 </p>
331
332 <pre caption="Ejemplo rápido">
333 for ( x = 1; x &lt;= 4; x++ ) {
334 print "vuelta",x
335 }
336 </pre>
337
338 <p>
339 Este código imprimirá lo siguiente:
340 </p>
341
342 <pre caption="Salida del código de arriba">
343 vuelta 1
344 vuelta 2
345 vuelta 3
346 vuelta 4
347 </pre>
348 </body>
349 </section>
350
351 <section>
352 <title>Break y continue</title>
353 <body>
354
355 <p>
356 De nuevo, al igual que en C, awk provee instrucciones break y
357 continue. Dichas sentencias nos brindan un mejor control sobre varios
358 tipos de bucle en awk. Aquí se expone un pedazo de código que necesita
359 desesperadamente una instrucción break:
360 </p>
361
362 <pre caption="Ejemplo de código que necesita una instrucción break">
363 while (1) {
364 print "forever and ever..."
365 }
366 </pre>
367
368 <p>
369 Como 1 siempre es cierto, este bucle while duraría para siempre. Aquí
370 hay un bucle que se ejecutaría solo 10 veces:
371 </p>
372
373 <pre caption="Bucle que se ejecuta 10 veces">
374 x=1
375 while(1) {
376 print "vuelta",x
377 if ( x == 10 ) {
378 break
379 }
380 x++
381 }
382 </pre>
383
384 <p>
385 Aquí, el break se usa para salir del bucle más interior. Se sale del
386 bucle y la ejecución continúa en la línea justo después del bloque del
387 bucle.
388 </p>
389
390 <p>
391 La instrucción continue complementa a break, y funciona de esta forma:
392 </p>
393
394 <pre caption="La instrucción continue complementando a break">
395 x=1
396 while (1) {
397 if ( x == 4 ) {
398 x++
399 continue
400 }
401 print "vuelta",x
402 if ( x > 20 ) {
403 break
404 }
405 x++
406 }
407 </pre>
408
409 <p>
410 Este código imprimirá desde "iteration 1" hasta "iteration 21",
411 exceptuando a "iteration 4". En la vuelta número 4, x es incrementado
412 y la instrucción continue es ejecutada, lo cual provoca el salto a la
413 siguiente iteración sin ejecutar ni una línea más de la vuelta
414 actual. La instrucción continue funciona para todos los tipos de bucle
415 interactivo en awk, al igual que break. Cuando se usa en el cuerpo de
416 un bucle for, continue provoca el incremento de la variable de
417 control. Veamos el equivalente en un bucle for:
418 </p>
419
420 <pre caption="Bucle for equivalente">
421 for ( x=1; x&lt;=21; x++ ) {
422 if ( x == 4 ) {
423 continue
424 }
425 print "vuelta",x
426 }
427 </pre>
428
429 <p>
430 No es necesario incrementar x tras el continue, porque el bucle for,
431 al contrario que el while, la incrementa de forma automática.
432 </p>
433 </body>
434 </section>
435
436 <section>
437 <title>Matrices</title>
438 <body>
439
440 <p>
441 Te gustará saber que awk es capaz de manejar matrices. Sin embargo,
442 aquí comienzan en 1 en lugar de 0:
443 </p>
444
445 <pre caption="Ejemplo de matriz en awk">
446 myarray[1]="jim"
447 myarray[2]=456
448 </pre>
449
450 <p>
451 Cuando awk encuentra la primera asignación de myarray, la matriz es
452 creada, y myarray[1] pasa a contener "jim". Al pasar a la siguiente
453 asignación, la matriz pasa a contener dos elementos diferenciados.
454 has two elements.
455 </p>
456
457 <p>
458 Una vez definida la matriz, awk tiene mecanismos para iterar sobre sus
459 elementos de la forma siguiente:
460 </p>
461
462 <pre caption="Iterando sobre matrices">
463 for ( x in myarray ) {
464 print myarray[x]
465 }
466 </pre>
467
468 <p>
469 Este código imprimirá todos los elementos dentro de myarray. Al usar
470 esta forma especial "in" en un bucle for, awk asignará todos y cada
471 uno de los índices existentes en myarray a x (la variable de control
472 del bucle) en sucesivos turnos, ejecutando el código del bucle una vez
473 tras cada reasignación de x. Si bien se trata de una peculiaridad
474 bastante cómoda en awk, también es cierto que tiene una parte negativa
475 -- cuando awk cicla entre los índices de la matriz, no sigue ningún
476 orden particular. Esto quiere decir que no tenemos forma de saber si
477 la salida del código de arriba será:
478 </p>
479
480 <pre caption="Salida del código de arriba">
481 jim
482 456
483 </pre>
484
485 <p>
486 o
487 </p>
488
489 <pre caption="Otra salida para el código de arriba">
490 456
491 jim
492 </pre>
493
494 <p>
495 Parafraseando libremente a Forrest Gump, iterar sobre los elementos de
496 una matriz en awk es como abrir una caja de bombones -- nunca sabes lo
497 que te va a tocar. Todo esto se debe al caracter encadenado de las
498 matrices en awk, aspecto que veremos ahora.
499 </p>
500 </body>
501 </section>
502
503 <section>
504 <title>Naturaleza de cadena de las matrices</title>
505 <body>
506
507 <p>
508 <uri link="/doc/es/articles/l-awk1.xml">En mi artículo anterior</uri>,
509 ya comenté que awk almacena las variables numéricas como cadenas de
510 caracteres. Si bien awk realiza las conversiones para que todo
511 funcione, también es verdad que abre las puertas a la escritura de
512 código de aspecto extraño:
513 </p>
514
515 <pre caption="Código de aspecto extraño">
516 a="1"
517 b="2"
518 c=a+b+3
519 </pre>
520
521 <p>
522 Tras de que este código se ejecute, c es igual a 6 ya que awk suma las
523 cadenas "1" y "2" tal y como si fueran los números 1 y 2. En ambos
524 casos, awk solucionará el problema correctamente. Esta naturarleza de
525 awk es bastante intrigante -- te preguntarás que es lo que pasará si
526 usamos índices de cadena para las matrices, por ejemplo, en este
527 código:
528 </p>
529
530 <pre caption="Código de ejemplo">
531 myarr["1"]="Mr. Whipple"
532 print myarr["1"]
533 </pre>
534
535 <p>
536 Tal y como esperarías, este código imprime "Mr. Whipple". Pero ¿y si
537 omitimos las comillas alrededor del segundo 1?
538 </p>
539
540 <pre caption="Quitando las comillas en el print">
541 myarr["1"]="Mr. Whipple"
542 print myarr[1]
543 </pre>
544
545
546 <p>
547 Adinivar el resultado de esto es más complicado. ¿Considerará awk que
548 myarr["1"] y myarr[1] son dos elementos distintos de la matriz? ¿o
549 quizás los considere como referencias al mismo elemento? La respuesta
550 correcta es que awk los considerará como el mismo elemento, y por
551 tanto, imprimirá "Mr. Whipple", tal y como hizo en el otro
552 ejemplo. Aunque parezca extraño, awk ha estado usando internamente
553 índices de cadena todo el rato.
554 </p>
555
556 <p>
557 Y tras conocer este comportamiento, alguien estará tentado de ejecutar
558 algo como esto:
559 </p>
560
561 <pre caption="Código extraño">
562 myarr["name"]="Mr. Whipple"
563 print myarr["name"]
564 </pre>
565
566 <p>
567 Y no solo no provoca un error, sino que además funciona de forma
568 idéntica a los ejemplos anteriores, e imprimirá también el mismo
569 resultado, "Mr. Whipple". Como ves, awk no nos limita al uso de
570 índices enteros, podemos usar cadenas sin problema alguno. Cuando
571 usamos una cadena a modo de índice como en myarr["name"], estamos
572 usando matrices asociativas. Técnicamente awk no está haciendo nada
573 distinto en la trastienda ya que incluso cuando usamos un entero, awk
574 lo trata como si éste fuera una cadena. Sin embargo, aún deberiamos
575 llamarlas matrices asociativas -- suena bien e impresionará a tu
576 jefe. El índice númerico que en realidad es una cadena será nuestro
577 pequeño secreto. ;)
578 </p>
579 </body>
580 </section>
581
582 <section>
583 <title>Utilidades de matrices</title>
584 <body>
585
586 <p>
587 Cuando nos referimos a matrices, awk nos ofrece bastante
588 versatilidad. Podemos usar índices de cadena y no estamos obligados a
589 tener secuencias de índices numéricos contínuos (por ejemplo, podemos
590 definir myarr[1] y myarr[1000], y dejar los demás sin definir). Si
591 bien todo esto puede ser útil, en algunas circunstancias puede crear
592 confusión. Afortunadamente, awk ofrece un par de ventajas que hacen
593 que las matrices sean algo más manejables.
594 </p>
595
596 <p>
597 Podemos borrar elementos de una matriz, por ejemplo, para borrar un
598 elemento haríamos:
599 </p>
600
601 <pre caption="Borrando elementos de la matriz">
602 delete fooarray[1]
603 </pre>
604
605 <p>
606 Si quieres ver si un elemento particular existe, puedes usar el
607 operador lógico especial "in" tal y como sigue:
608 </p>
609
610 <pre caption="Comprobar si un elemento existe">
611 if ( 1 in fooarray ) {
612 print "Si! Aquí está."
613 } else {
614 print "No! No se pudo encontrar."
615 }
616 </pre>
617 </body>
618 </section>
619
620 <section>
621 <title>La próxima vez</title>
622 <body>
623
624 <p>
625 Hemos cubierto mucha materia en este artículo. La próxima vez,
626 redondeare un poco el conocimiento sobre awk enseñándote como usar
627 funciones matemáticas y de cadenas en awk, y verás como crear tus
628 propias funciones. También te guiaré sobre la creación de un programa
629 de gestión contable. Mientras tanto, te recomiendo que escribas tus
630 propios programas en awk, y también los siguientes recursos en la web:
631 </p>
632 </body>
633 </section>
634 </chapter>
635
636 <chapter>
637 <title>Recursos</title>
638 <section>
639 <title>Enlaces útiles</title>
640 <body>
641
642 <ul>
643 <li>
644 Lee otros artículos de Daniel sobre awk publicados en
645 developerWorks: Awk mediante ejemplos, <uri
646 link="l-awk1.xml">Parte 1</uri> y <uri link="l-awk3.xml">Parte
647 3</uri>.
648 </li>
649 <li>
650 Si prefieres un buen libro a la vieja usanza, el <uri
651 link="http://www.oreilly.com/catalog/sed2/">sed &amp; awk, 2ª
652 Edición</uri> de O'Reilly es una buena opción.
653 </li>
654 <li>
655 Asegúrate también de examinar <uri
656 link="http://www.faqs.org/faqs/computer-lang/awk/faq/">comp.lang.awk
657 FAQ</uri>. También contiene varios enlaces sobre awk.
658 </li>
659 <li>
660 El <uri link="http://sparky.rice.edu/~hartigan/awk.html">tutorial
661 de awk</uri> de Patrick Hartigan viene con varios guiones
662 interesantes de awk.
663 </li>
664 <li>
665 El <uri link="http://www.tasoft.com/tawk.html">Compilador TAWK de
666 Thompson</uri> compila guiones de awk transformándolos en binarios
667 ejecutables. Hay versiones para Widows, OS/2, DOS, y UNIX.
668 </li>
669 <li>
670 <uri link="http://www.gnu.org/software/gawk/manual/gawk.html">La
671 guía de usuario de GNU Awk</uri> también está disponible para
672 referencia en línea.
673 </li>
674 </ul>
675 </body>
676 </section>
677 </chapter>
678 </guide>
679
680
681
682 --
683 gentoo-commits@l.g.o mailing list