viernes, 29 de septiembre de 2017

Formateando la Salida

En este capítulo, continuamos viendo herramientas relacionadas con el texto, enfocándonos en programas que se usan para formatear salidas de texto, en lugar que cambiar el texto en sí. Estas herramientas se usan a menudo para preparar el texto para una eventual impresión, un caso que veremos en el próximo capítulo. Los programas que veremos en este capítulo incluyen:
  • nl - Numera líneas
  • fold - Limita cada línea a una longitud especificada
  • fmt - Un formateador de texto simple
  • pr - Prepara texto para imprimir
  • printf - Formatea e imprime datos
  • groff - Un sistema de formateo de documentos

Herramientas de formateo simple

Veremos algunas de las herramientas de formateo simple primero. Estas son mayormente programas de un único propósito, y poco sofisticadas en lo que hacen, pero pueden usarse para pequeñas tareas y como parte de entubados y scripts.

nl - Numera líneas

El programa nl es una herramienta más bien arcaica que se usa para realizar tareas simples. Numera las líneas. En su uso más simple, se parece a cat -n:

 [me@linuxbox ~]$ nl distros.txt | head
      1 SUSE       10.2      12/07/2006
      2 Fedora     10        11/25/2008
      3 SUSE       11.0      06/19/2008
      4 Ubuntu     8.04      04/24/2008
      5 Fedora     8         11/08/2007
      6 SUSE       10.3      10/04/2007
      7 Ubuntu     6.10      10/26/2006
      8 Fedora     7         05/31/2007
      9 Ubuntu     7.10      10/18/2007
     10 Ubuntu     7.04      04/19/2007

Como cat, nl puede aceptar tanto múltiples archivos como argumentos de línea de comandos, o entrada estándar. Sin embargo, nl tiene un número de opciones y soporta una forma primitiva de etiquetas para permitir tipos de numeración más complejas.

nl soporta un concepto llamado "páginas lógicas" cuando numera. Ésto permite a nlresetear (empezar de nuevo) la secuencia numérica cuando está numerando. Usando opciones, es posible establecer el número inicial a un valor específico y, en limitada medida, su formato. Una página lógica está dividida en encabezado, cuerpo y pie. Dentro de cada una de estas secciones, el numerado de líneas puede ser reseteado y/o asignarle un estilo diferentes. Si le damos a nl múltiples archivos, los trata como una cadena simple de texto. Las secciones en la cadena de texto se indican con la presencia de algún tipo de etiqueta de estilo antiguo añadida al texto:

Etiquetas nl

\:\:\: Comienzo del encabezado de la página lógica
\:\:   Comienzo del cuerpo de la página lógica
\:     Comienzo del pie de la página lógica

Cada etiqueta anterior debe aparecer sola en su propia línea. Tras procesar una etiqueta, nl la borra de la cadena de texto.

Aquí tenemos las opciones comunes de nl:

Opciones comunes de nl

-b style
Establece la numeración del cuerpo a style, donde style es uno de los siguientes:
a = numera todas las líneas
t = numera sólo las líneas que no están en blanco. Es la opción por defecto.
n = no
pregexp = numera sólo las líneas que coinciden con la expresión regular regexp

-f style
Establece la numeración del pie a style. La opción por defecto es n (no)

-h style
Establece la numeración del encabezado a style. La opción por defecto es n (no)

-i number
Establece el incremento de numeración por página a number. La opción por defecto es uno.

-n format
Establece el formato de numeración a format, donde format es:
ln = justificado a la izquierda, sin ceros delante.
rn = justificado a la derecha, sin ceros delante. Es la opción por defecto.
rz = justificado a la derecha, con ceros delante.

-p
No resetea la numeración de páginas al principio de cada página lógica.

-s string
Añade string al final de cada número de línea para crear un separador. La opción por defecto es un carácter de tabulación simple.

-v number
Establece el número de la primera línea de cada página lógica a number. La opción por defecto es uno.

-w width
Establece la anchura del campo del número de línea a width. La opción por defecto es 6.

Admitámoslo, probablemente no querremos numerar líneas tan a menudo, pero podemos usar nl para ver como podemos combinar múltiples herramientas para realizar tareas más complejas. Trabajaremos sobre nuestro ejercicio del capítulo anterior para producir un informe de distribuciones Linux. Como usaremos nl, será útil incluir sus etiquetas encabezado/cuerpo/pie. Para hacerlo, las añadiremos al script sed del último capítulo. Usando nuestro editor de texto, cambiaremos el script de la siguiente forma y lo guardaremos como distros-nl.sed:

 # sed script to produce Linux distributions report
 

 1 i\
 \\:\\:\\:\
 \
 Linux Distributions Report\
 \
 Name Ver. Released\
 ---- ---- --------\
 \\:\\:
 s/\([0-9]\{2\}\)\/\([0-9]\{2\}\)\/\([0-9]\{4\}\)$/\3-\1-\2/
 $ a\
 \\:\
 \
 End Of Report

El script ahora inserta las etiquetas nl de páginas lógicas y añade un pie al final del informe. Fíjate que hora tenemos que duplicar las barras invertidas en nuestra etiqueta, porque normalmente se interpretan como un carácter de escape en sed.

A continuación, produciremos nuestro informe mejorado combinando sort, sed y nl:

[me@linuxbox ~]$ sort -k 1,1 -k 2n distros.txt | sed -f distros-nl.s
ed | nl

       Linux Distributions Report


       Name    Ver.  Released
       ----    ----  --------


     1 Fedora  5     2006-03-20
     2 Fedora  6     2006-10-24
     3 Fedora  7     2007-05-31
     4 Fedora  8     2007-11-08
     5 Fedora  9     2008-05-13
     6 Fedora  10    2008-11-25
     7 SUSE    10.1  2006-05-11
     8 SUSE    10.2  2006-12-07
     9 SUSE    10.3  2007-10-04
    10 SUSE    11.0  2008-06-19
    11 Ubuntu  6.06  2006-06-01
    12 Ubuntu  6.10  2006-10-26
    13 Ubuntu  7.04  2007-04-19
    14 Ubuntu  7.10  2007-10-18
    15 Ubuntu  8.04  2008-04-24
    16 Ubuntu  8.10  2008-10-30



      End Of Report

Nuestro informe es el resultado de nuestro entubado de comandos. Primero, ordenamos la lista por nombre de la distribución y versión (campos 1 y 2), luego procesamos el resultado con sed, añadiendo el encabezado del informe (incluyendo la etiqueta de página lógica de nl) y el pie. Finalmente, procesamos el resultado con nl, que, por defecto, sólo numera las líneas de la cadena de texto que pertenezcan a la sección cuerpo de la página lógica.

Podemos repetir el comando y experimentar con diferentes opciones de nl. Algunas interesantes son:

 nl -n rz

 y

 nl -w 3 -s ' '

fold - Limita cada línea a una longitud especificada

Folding (plegado) es el proceso de cortar líneas de texto a una anchura especificada. Como nuestros otros comandos, fold acepta tanto uno o más archivos de texto como entrada estándar. Si mandamos a fold una cadena de texto simple, podemos ver como funciona:

[me@linuxbox ~]$ echo "The quick brown fox jumped over the lazy dog." | fold -w 12
The quick br
own fox jump
ed over the
lazy dog.

Aquí vemos a fold en acción. El texto enviado por el comando echo se divide en segmentos especificados por la opción -w. En este ejemplo, especificamos una anchura de línea de 12 caracteres. Si no se especifica anchura, la opción por defecto es 80 caracteres. Fíjate como las líneas se cortan independientemente de los límites de las palabras. Añadir la opción -s hace que fold corte la línea en el último espacio existente antes de que se alcance la longitud de la línea:

[me@linuxbox ~]$ echo "The quick brown fox jumped over the lazy dog." | fold -w 12 -s
The quick
brown fox
jumped over
the lazy
dog.


fmt - Un formateador de texto simple

El programa fmt también pliega el texto, pero un poco más. Acepta tanto archivos como entrada estándar y realiza formateo de párrafos en la cadena de texto. Básicamente, rellena y junta líneas de texto mientras que mantiene líneas en blanco y sangrías.

Para demostrarlo, necesitaremos algún texto. Tomemos algo de la página info de fmt:


   `fmt' reads from the specified FILE arguments (or standard input if none are given), and writes to standard output.

   By default, blank lines, spaces between words, and indentation are preserved in the output; successive input lines with different
indentation are not joined; tabs are expanded on input and introduced on output.

   `fmt' prefers breaking lines at the end of a sentence, and tries to avoid line breaks after the first word of a sentence or before the last word of a sentence. A "sentence break" is defined as either the end of a paragraph or a word ending in any of `.?!', followed by two spaces or end of line, ignoring any intervening parentheses or
quotes. Like TeX, `fmt' reads entire "paragraphs" before choosing line breaks; the algorithm is a variant of that given by Donald E.
Knuth and Michael F. Plass in "Breaking Paragraphs Into Lines",
`Software--Practice & Experience' 11, 11 (November 1981), 1119-1184.


Copiaremos este texto en nuestro editor de texto y guardaremos el archivo como fmt-info.txt. Ahora, digamos que queremos reformatear este texto para ajustarlo a columnas de cincuenta caracteres de ancho. Podríamos hacerlo procesando el archivo con fmt y la opción -w:

[me@linuxbox ~]$ fmt -w 50 fmt-info.txt | head
   `fmt' reads from the specified FILE arguments
   (or standard input if none are given), and writes to standard output.

   By default, blank lines, spaces between words, 

   and indentation are 
preserved in the output; successive input lines with different indentation are not joined; tabs are expanded on input and introduced on output.

Bien, es un resultado poco manejable. Quizá deberíamos leer el texto en realidad, ya que explica lo que está pasando:

Por defecto, las líneas en blanco, los espacios entre palabras y las sangrías se mantienen en la salida; las líneas de entrada sucesivas con diferentes sangría no se añaden; las tabulaciones se expanden en la entrada y se pasan a la salida.

Así que, fmt mantiene la sangría de la primera línea. Afortunadamente, fmt proporciona una opción para corregir ésto:


[me@linuxbox ~]$ fmt -cw 50 fmt-info.txt

   `fmt' reads from the specified FILE arguments
   (or standard input if
none are given), and writes to standard output.
   By default, blank lines, spaces between words, 
   and indentation are preserved in the output; successive input lines with different indentation are not joined; tabs are expanded on input and introduced on output.

   `fmt' prefers breaking lines at the end of a sentence, and tries to avoid line breaks after the first word of a sentence or before the last word of a sentence. A "sentence break" is defined as either the end of a paragraph or a word ending in any of `.?!', followed by two spaces or end of line, ignoring any intervening parentheses or quotes. Like TeX, `fmt' reads entire "paragraphs" before choosing line breaks; the algorithm is a variant of that given by Donald E. Knuth and Michael F. Plass in "Breaking Paragraphs Into Lines", `Software--Practice & Experience' 11, 11
(November 1981), 1119-1184.


Mucho mejor. Añadiendo la opción -c, ahora tenemos el resultado deseado.

fmt tiene algunas opciones interesantes:

Opciones fmt

-c
Opera en modo margen de corona. Esto preserva la tabulación de las primeras dos líneas de un párrafo. Las siguientes líneas se alinean con la tabulación de la segunda línea.

-p string

Solo formatea aquellas líneas que comienzan con el prefijostring. Tras formatear, el contenido de string se coloca como prefijo de cada línea reformateada. Esta opción puede usarse para formatear texto en comentarios de código fuente. Por ejemplo, cada lenguaje de programación o archivo de configuración que use un carácter "#" para delimitar un comentario podría formatearse especificando -p '# ' de forma que sólo los comentarios se formatearán. Mira el ejemplo a continuación.

-s

Modo sólo-división. En este modo, las líneas sólo se dividirán para ajustarse al ancho especificado de columna. Las líneas cortas no se unirán para rellenar líneas. Este modo es útil cuando formateamos texto como código donde no queremos que se unan las líneas.

-u

Realiza espaciado uniforme. Ésto aplicará el estilo tradicional de "máquina de escribir" al texto. Esto significa que se aplica un espacio entre palabras y dos espacios entre frases. Este modo es útil para eliminar la "justificación", o sea, el texto que ha sido inflado con espacios para forzar el alineamiento tanto en el margen derecho como izquierdo.

-w ancho

Formatea el texto para ajustarlo a un ancho de columna en caracteres. La opción por defecto es 75 caracteres. Nota: fmt en realidad formatea líneas ligeramente más cortas que la anchura especificada para permitir balanceo de líneas.

La opción -p es particularmente interesante. Con ella, podemos formatear fragmentos seleccionados de un archivo, siempre que las líneas a formatear comiencen con la misma secuencia de caracteres. Muchos lenguajes de programación usan el símbolo de la libra (#) para indicar el comienzo de un comentario y así pueden ser formateados usando esta opción. Creemos un archivo que simule un programa que use comentarios:


 [me@linuxbox ~]$ cat > fmt-code.txt
 # This file contains code with comments.

 # This line is a comment.
 # Followed by another comment line.
 # And another.

 This, on the other hand, is a line of code.
 And another line of code.
 And another.


Nuestro archivo de ejemplo contiene comentarios que comienzan con la cadena "# "(un # seguido por un espacio) y líneas de código que no. Ahora, usando fmt, podemos formatear los comentarios y dejar el código intacto:

 [me@linuxbox ~]$ fmt -w 50 -p '# ' fmt-code.txt
 # This file contains code with comments.

 # This line is a comment. Followed by another
 # comment line. And another.


This, on the other hand, is a line of code.
And another line of code.
And another.
Fíjate que las líneas de comentarios contiguas se unen, mientras que las líneas en blanco y las que no comienzan con el prefijo especificado se conservan.

pr - Formatea texto para imprimir

El programa pr se usa para paginar texto. Cuando imprimimos texto, a menudo es deseable separar las páginas de la salida con varias líneas en blanco, para darle un margen superior e inferior a cada página. Mas adelante, este espacio en blanco puede usarse para insertar un encabezado y un pie en cada página.

Probaremos pr formateando el archivo distros.txt en una serie de páginas muy cortas (sólo las dos primeras páginas se muestran):

 [me@linuxbox ~]$ pr -l 15 -w 65 distros.txt


 2008-12-11 18:27                distros.txt           Page 1




 SUSE    10.2    12/07/2006
 Fedora  10      11/25/2008
 SUSE    11.0    06/19/2008
 Ubuntu  8.04    04/24/2008
 Fedora  8       11/08/2007



2008-12-11 18:27                 distros.txt           Page 2

 SUSE    10.3    10/04/2007
 Ubuntu  6.10    10/26/2006
 Fedora  7       05/31/2007
 Ubuntu  7.10    10/18/2007
 Ubuntu  7.04    04/19/2007

En este ejemplo, empleamos la opción -l (para la longitud de página) y la opción -w(ancho de página) para definir una "página" de 65 columnas de ancho y 15 líneas de largo. pr pagina el contenido del archivo distros.txt, separando cada página con varias líneas en blanco y crea un encabezado por defecto que contiene la hora de modificación del archivo y el número de página. El programa pr ofrece muchas opciones para controlar la plantilla de página. Echaremos un vistazo más a fondo en el siguiente capítulo.


printf - Formatea e imprime datos


Al contrario que otros comandos de este capítulo, el comando printf no se usa para entubados (no acepta entrada estándar) ni se usa frecuentemente en la línea de comandos (se usa mayormente en scripts). Entonces ¿Por qué es tan importante? Porque se usa mucho.

printf (de la frase "print formatted" - impresión formateada) fue desarrollado originalmente para el lenguaje de programación C y ha sido implementado en muchos lenguajes de programación incluyendo el shell. De hecho, en bash, printf está incluido.

printf funciona así:

 printf "formato" argumentos

Se le da al comando una cadena que contiene una descripción del formato que se aplica a una lista de argumentos. El resultado formateado se envía a la salida estándar. Aquí hay un ejemplo trivial:

 [me@linuxbox ~]$ printf "I formatted the string: %s\n" foo
 I formatted the string: foo

La cadena de formato puede contener texto literal (como "He formateado la cadena:"), secuencias de espcape (como \n, un caracter de nueva línea), y secuencias que comiencen con el carácter %, que se llaman especificaciones de conversion. En el ejemplo anterior, la especificación de conversión %s se usa para formatear la cadena "foo" y colocarla en la salida del comando. Aquí está de nuevo:

 [me@linuxbox ~]$ printf "I formatted '%s' as a string.\n" foo
 I formatted 'foo' as a string.

Como podemos ver, la especificación de conversión %s se reemplaza por la cadena "foo" en la salida del comando. La conversión s se usa para formatear cadenas de datos. Hay otros especificadores para otros tipos de datos. Esta tabla lista los tipos de datos usados más frecuentemente:

Especificadores de tipos de datos frecuentes en printf

-d Formatea un número como un entero decimal con signo.

Formatea y manda a la salida un número de coma flotante.
Formatea un entero como un número octal.s Formatea una cadena.
Formatea un entero como número hexadecimal usando a-f en minúsculas donde sea necesario.X Lo mismo que x pero usa letras mayúsculas.
%  Imprime un símbolo % literal (p.ej.: especificando "%%")

Probaremos el efecto de cada especificador de conversión en la cadena "380":

[me@linuxbox ~]$ printf "%d, %f, %o, %s, %x, %X\n" 380 380 380 380
380 380
380, 380.000000, 574, 380, 17c, 17C

Como hemos usado seis especificadores de conversión, tenemos que añadir también seis argumentos a printf para el proceso. Los seis resultados muestran el efecto de cada especificador.
Varios componentes opciones pueden añadirse al especificador de conversión para ajustar la salida. Una especificación de conversión completa consiste en lo siguiente:

%[flags][width][.precision]conversion_specification

Múltiples componentes opcionales, cuando se usan, deben aparecer en el orden indicado arriba para que sean interpretados correctamente. Aquí tenemos una descripción de cada uno:

Componentes de especificación de conversión de printf

flags 
Hay diferentes flags: 
 # - Usa el "formato alternativo" para la salida. Esto varía según el tipo de dato. Para conversión o (número octal), la salida es precedida con 0. Para conversiones x y X (números hexadecimales), la salida es precedida con 0x o 0Xrespectivamente. 
 0 - (cero) Rellena la salida con ceros. Ésto significa que el campo se rellenará con ceros delante, como en "000380". 
 - - (guión) Alinea la salida a la izquierda. Por defecto, printf alinea a la derecha la salida. 
 ' ' - (espacio) Produce un espacio delante para números positivos. 
 + - (signo más) Pone signo a los números positivos. Por defecto, printf sólo pone signo a los números negativos.

width 
Un número especificando la anchura mínima del campo.

.precission Para número de coma flotante, especifica el número de dígitos de precisión a mostrar tras la coma. En conversión de cadenas, precisión especifica el número de caracteres a mostrar.

Aquí tenemos algunos ejemplos de diferentes formatos en acción:

Ejemplos de especificación de conversión en print

380 "%d" 380 
Formateo simple de un entero.

380 "%#x" 0x17c 
Entero formateado como un número hexadecinal usando la etiqueta "formato alternativo".

380 "%05d" 00380 Entero formateado con ceros delante (rellenado) y una anchura mínima de cinco caracteres.

380 "%05.5f" 380,00000 
Número formateado como número en coma flotante con rellenado y cinco posiciones decimales de precisión. Como la anchura de campo mínima especificada (5) es menor de la anchura real del número formateado, el rellenado no tiene efecto.

380 "%010.5f" 0380,00000 
Incrementando la anchura mínima del campo a 10 el rellenado es ahora visible.

380 "%+d" +380 
La etiqueta + pone signo a un número positivo.

380 "%-d" 380 
La etiqueta - alinea el formato a la izquierda.

abcdefghijk "%5s" abcedfghijk 
Una cadena formateada con un ancho de campo mínimo.

abcdefghijk "%.5s" abcde 
Aplicando precisión a una cadena, se trunca.

De nuevo, printf se usa mayormente en scripts donde se emplea para formatear datos tabulares, en lugar de en la línea de comandos directamente. Pero si que podemos ver como puede usarse para resolver varios problemas de formateo. Primero, obtengamos varios campos separados por caracteres tabulador:

 [me@linuxbox ~]$ printf "%s\t%s\t%s\n" str1 str2 str3
 str1 str2 str3

Insertando \t (la secuencia de escape para un tabulador), logramos el efecto deseado. A continuación, algunos número con formateo especial:

[me@linuxbox ~]$ printf "Line: %05d %15.3f Result: %+15d\n" 1071
3.14156295 32589
Line: 01071           3.142 Result:          +32589

Esto muestra el efecto del ancho mínimo de campo en el espacio de los campos. O cómo formatear una pequeña página web:

[me@linuxbox ~]$ printf "<html>\n\t<head>\n\t\t<title>%s</title>\n
\t</head>\n\t<body>\n\t\t<p>%s</p>\n\t</body>\n</html>\n" "Page Title" "Page Content"

<html>
  <head>
    <title>Page Title</title>
  </head>
  <body>
    <p>Page Content</p>
  </body>
</html>

Sistemas de formateo de documentos


Hasta ahora, hemos examinado las herramientas de formato de texto simple. Son buenas para tareas simples y pequeñas, pero ¿que pasa con grandes trabajos? Una de las razones que hizo que Unix llegara a ser un sistema operativo popular entre usuarios técnicos y científicos (ademas de proporcionar una multitarea potente y entorno multiusuario para todo tipo de desarrollo de software) es que ofrece herramientas que pueden usarse para producir muchos tipos de documentos, particularmente publicaciones científicas y académicas. De hecho, como la documentación GNU describe, la preparación de documentos era instrumental para el desarrollo de Unix:

La primera versión de UNIX fue desarrollada en un PDP-7 que estaba cercano a los laboratorios Bell. En 1971 los desarrolladores querían conseguir un PDP-11 para futuros trabajos en el sistema operativo. Para justificar el coste de este sistema, propusieron e implementaron un sistema de formateo de documentos para la división de patentes de AT&T. Este primer programa de formateo fue una reimplementación del McIllroy's 'roff', escrito por J. F. Ossanna.

Dos principales familias de formateadores de documentos dominaban el terreno. aquellos que descendían del programa roff original, incluyendo nroff y troff, y aquellos basados en el sistema de escritura TEX (pronunciado "tek") de Donald Knuth. Y sí, la "E" caída en el centro es parte del nombre.

El nombre "roff" deriva del término "run off" como en, "I'll run off a copy for you. - Imprimiré una copia para tí". El programa nroff se usa para formatear documentos para salida a dispositivos que usan fuentes mono espacio, como los terminales de caracteres e impresoras estilo máquina de escribir. En el momento de su introducción, incluía casi todos los dispositivos de impresión conectados a ordenadores. El posterior programa troff formatea documentos para salida a tipógrafos, dispositivos usados para producir escritura "preparada para cámara" para impresión comercial. La mayoría de las impresoras de ordenadores hoy en día pueden simular la salida de los tipógrafos. La familia roff también incluye algunos otros programas que se usan para preparar porciones de documentos. Estos incluyen eqn (para ecuaciones matemáticas) y tbl(para tablas).

El sistema TEX (en su forma estable) apareció por primera vez en 1989 y ha, en algún grado, desplazado a troff como la herramienta elegida para salida a tipógrafos. No veremos TEX aquí, debido a su complejidad (hay libros enteros sobre él) y al hecho de que no viene instalada por defecto en sistemas Linux modernos.

Consejo: Para aquellos interesados en instalar TEX, prueba el paquete texlive que puede encontrarse en la mayoría de los repositorios de las distribuciones, y el editor de contenido gráfico LyX.


groff

groff es una colección de programas que contienen la implementación GNU de troff. También incluye un script que se usa para emular nroff y el resto de la familia roff también.

Mientras que roff y sus descendientes se usan para crear documentos formateados, lo hacen de una forma algo extraña para los usuarios modernos. La mayoría de los documentos de hoy en día se producen usando procesadores de texto que pueden realizar tanto la composición como el formateado de un documento en un único paso. Antes de la llegada de los procesadores de texto gráficos, los documentos a menudo se producían en un proceso en dos pasos que incluía el uso de un editor de texto para realizar la composición, y un procesador, como troff, para realizar el formateo. Las instrucciones para el programa de formateo estaban incluidas en el texto compuesto a través del uso de un lenguaje de etiquetas. La analogía moderna para este tipo de procesos es la página web, que se crea usando un editor de texto de algún tipo y luego se renderiza usando un navegador web con HTML como lenguaje de etiquetas para describir el aspecto final de la página.

No vamos a ver groff en su totalidad, ya que muchos elementos de su lenguaje de etiquetas tienen que ver con detalles de tipografía muy arcaicos. En su lugar nos concentraremos en uno de sus macro paquetes que siguen usando ampliamente. Los macro paquetes concentran muchos de sus comandos de bajo nivel en pequeñas colecciones de comandos de alto nivel que hace el uso de groff mucho más sencillo.

Por un momento, consideremos la humilde man page. Se encuentra en el directorio /usr/share/man como un archivo de texto comprimido con gzip. Si fuéramos a examinar su contenido descomprimido, veríamos lo siguiente (se muestra la man page de ls en su sección 1):

 [me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | head
 .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.35.
 .TH LS "1" "April 2008" "GNU coreutils 6.10" "User Commands"
 .SH NAME
ls \- list directory contents
 .SH SYNOPSIS
 .B ls
[\fIOPTION\fR]... [\fIFILE\fR]...
 .SH DESCRIPTION
 .\" Add any additional description here
 .PP

Comparada a la man page en su presentación normal, podemos empezar a ver una correlación entre el lenguaje de marcas y su resultado:

 [me@linuxbox ~]$ man ls | head
 LS(1)                     User Commands LS(1)



 NAME
        ls - list directory contents
 SYNOPSIS
        ls [OPTION]... [FILE]...

La razón por la que esto es interesante es que las man pages están renderizadas por groff, usando el macro paquete man-doc. De hecho, podemos simular el comando man con el siguiente entubado:

[me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | groff -mandoc -T ascii | head
LS(1)                 User Commands                        LS(1)



NAME
       ls - list directory contents
SYNOPSIS
       ls [OPTION]... [FILE]...

Aquí usamos el programa groff con las opciones configuradas para especificar el macro paquete mandoc y el controlador de salida para ASCII. groff puede producir salida en varios formatos. Si no se especifica ningún formato, la salida por defecto es PostScript:

[me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | groff -mandoc | head
%!PS-Adobe-3.0
%%Creator: groff version 1.18.1
%%CreationDate: Thu Feb 5 13:44:37 2009
%%DocumentNeededResources: font Times-Roman
%%+ font Times-Bold
%%+ font Times-Italic
%%DocumentSuppliedResources: procset grops 1.18 1
%%Pages: 4
%%PageOrder: Ascend
%%Orientation: Portrait

Hemos mencionado brevemente PostScript en el capítulo anterior, y lo haremos de nuevo en el próximo capítulo. PostScript es un lenguaje de descripción de página que se usa para describir el contenido de una página impresa en un dispositivo tipo teletipo. Si tomamos la salida de nuestro comando y lo almacenamos en un archivo (asumiendo que estamos usando un entorno gráfico con un directorio Desktop):

 [me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | groff -mandoc > ~/Desktop/foo.ps

Un icono del archivo de salida aparecerá en el escritorio. Haciendo doble clic en el icono, se abrirá un visor de páginas y mostrará el archivo en su forma renderizada:

Viendo la salida PostScript con un visor de páginas en GNOME

Lo que vemos es ¡una bonita man page de ls! De hecho, es posible convertir el archivo PostScript a PDF (Portable Document Format - Formato de Documento Portable) con este comando:

 [me@linuxbox ~]$ ps2pdf ~/Desktop/foo.ps ~/Desktop/ls.pdf

El programa ps2pdf es parte del paquete ghostscript, que está instalado en la mayoría de los sistemas Linux que soportan impresión:

Consejo: Los sistemas Linux a menudo incluyen muchos programas de línea de comandos para conversión de formatos de archivos. A menudo les nombra usando la convención format2format. Prueba a usar el comando ls /usr/bin/*[[:alpha:]]2[[:alpha:]]* para identificarlos. Prueba también a buscar programas llamados formattoformat.

Para nuestro último ejercicio con groff, revisitaremos nuestro viejo amigo distros.txt una vez más. Esta vez, usaremos el programa tbl que se usa para formatear tablas para imprimir nuestra lista de distribuciones Linux. Para hacerlo, vamos a usar nuestro anterior script de sed para añadir etiquetas a una cadena de texto que luego enviaremos a groff.

Primero, necesitamos modificar nuestro script sed para añadir las peticiones necesarios que requiere tbl. Usando un editor de texto, cambiaremos distros.sed a lo siguiente:

 # sed script to produce Linux distributions report

 1 i\
 .TS\
 center box;\
 cb s s\
 cb cb cb\
 l n c.\
 Linux Distributions Report\
 =\
 Name Version Released\
 _
 s/\([0-9]\{2\}\)\/\([0-9]\{2\}\)\/\([0-9]\{4\}\)$/\3-\1-\2/ 
 $ a\
 .TE

Fíjate que para que el script funcione correctamente, hay que tener cuidado y comprobar que las palabras "Name Version Released" está separadas por tabuladores, no por espacios. Guardaremos el archivo resultante como distros-tbl.sed. tbl usa las etiquetas .TS y .TE para comenzar y finalizar la tabla. Las filas que siguen a la etiqueta .TS definen las propiedades globales de la tabla que, en nuestro ejemplo, está centrada horizontalmente en la página y rodeada por una caja. Las restantes líneas de la definición describen el estilo de cada fila de la tabla. Ahora, si ejecutamos nuestro entubado generador de reportes de nuevo con el nuevo script de sed, tendremos lo siguiente:

[me@linuxbox ~]$ sort -k 1,1 -k 2n distros.txt | sed -f distros-tbl .sed | groff -t -T ascii 2>/dev/null
                 +------------------------------+
                 | Linux Distributions Report   |
                 +------------------------------+
                 | Name    Version    Released  |
                 +------------------------------+
                 |Fedora     5       2006-03-20 |
                 |Fedora     6       2006-10-24 |
                 |Fedora     7       2007-05-31 |
                 |Fedora     8       2007-11-08 |
                 |Fedora     9       2008-05-13 |
                 |Fedora    10       2008-11-25 |
                 |SUSE      10.1     2006-05-11 |
                 |SUSE      10.2     2006-12-07 |
                 |SUSE      10.3     2007-10-04 |
                 |SUSE      11.0     2008-06-19 |
                 |Ubuntu    6.06     2006-06-01 |
                 |Ubuntu    6.10     2006-10-26 |
                 |Ubuntu    7.04     2007-04-19 |
                 |Ubuntu    7.10     2007-10-18 |
                 |Ubuntu    8.04     2008-04-24 |
                 |Ubuntu    8.10     2008-10-30 |
                 +------------------------------+

Añadiendo la opción -t a groff le ordenamos que pre-procese la cadena de texto con tbl. De igual forma, la opción -T se usa para salida ASCII en lugar del medio de salida por defecto, PostScript.

El formato de la salida es el mejor que podemos esperar si estamos limitados por las capacidades de una pantalla de terminal o una impresora tipo máquina de escribir. Si especificamos salida PostScript y vemos gráficamente la salida resultante, tenemos un resultado más satisfactorio:

[me@linuxbox ~]$ sort -k 1,1 -k 2n distros.txt | sed -f distros-tbl.sed | groff -t > ~/Desktop/foo.ps


Viendo la tabla finalizada

Resumiendo

Dado que el texto es tan fundamental para el carácter de los sistemas operativos como-Unix, tiene sentido que haya muchas herramientas que se usan para manipular y formatear texto. Como hemos visto, ¡Las hay! Las herramientas de formateo simple como fmt y pr encontrarán muchos usos en scripts que producen documentos cortos, mientras que groff (y sus amigos) pueden usarse para escribir libros. Nunca escribiremos un paper técnico usando herramientas de línea de comandos (¡Aunque hay mucha gente que lo hace!), pero es bueno saber que podríamos.

Para saber más

No hay comentarios:

Publicar un comentario

Nota: solo los miembros de este blog pueden publicar comentarios.