martes, 30 de agosto de 2016

Herramientas de formateo simple

Primero veremos algunas de las herramientas de formateo simple. Estas son, principalmente, programas de un único propósito, y poco sofisticadas en lo que hacen, pero pueden usarse para pequeñas tareas y como parte de tuberías y scripts.

lunes, 29 de agosto de 2016

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 posible impresión, un tema 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

viernes, 26 de agosto de 2016

Punto extra

Hay algunos comandos interesantes de manipulación de texto que merece la pena investigar. Entre ellos: split (divide archivos en trozos), csplit (divide archivos en trozos basándose en patrones) y sdiff (combinación, a dos columnas, de las diferencias entre archivos.)

lunes, 22 de agosto de 2016

Resumiendo

En este capítulo, hemos visto algunas de las muchas herramientas de la línea de comandos que operan sobre textos. En el siguiente capítulo, veremos algunas más. Cierto es que, puede no ser inmediatamente obvio cómo o por qué tendrías que usar estas herramientas en el día a día, por lo que hemos tratado de mostrar algunos ejemplos semi-prácticos de su uso. Encontraremos en capítulos posteriores que estas herramientas forman la base de una colección de herramientas que se usa para resolver una serie de problemas prácticos. Esto será particularmente cierto cuando entremos en el shell scripting, donde estas herramientas mostrarán realmente su valor.

jueves, 18 de agosto de 2016

aspell

La última herramienta que veremos es aspell, un corrector ortográfico interactivo. El programa aspell es el sucesor de un programa anterior llamado ispell, y se puede usar, en su mayor parte, como reemplazo de este. Aunque el programa aspell es usado, principalmente, por otros programas que requieren capacidad de corrección ortográfica, también puede usarse muy eficientemente como una herramienta independiente de la línea de comandos. Tiene la capacidad de comprobar inteligentemente varios tipos de archivos de texto, incluyendo documentos HTML, programas C/C++, mensajes de correo y otros tipos de textos especializados.

Para comprobar ortográficamente un archivo de texto que contenga una prosa simple, puede usarse así:

aspell check textfile

donde textfile es el nombre del archivo a comprobar. Como ejemplo práctico, crearemos un archivo de texto simple llamado foo.txt que contengan algunos errores ortográficos intencionados:

[me@linuxbox ~]$ cat > foo.txt
The quick brown fox jimped over the laxy dog.

A continuación, comprobamos el archivo usando aspell:

[me@linuxbox ~]$ aspell check foo.txt

Como aspell es interactivo en el modo de comprobación, veremos una pantalla como esta:

The quick brown fox jimped over the laxy dog.

                                              
1) jumped               6) wimped
2) gimped               7) camped
3) comped               8) humped
4) limped               9) impede
5) pimped               0) umped
i) Ignore               I) Ignore all
r) Replace              R) Replace all
a) Add                  l) Add Lower
b) Abort                x) Exit
                                              
?

En la parte de arriba de la pantalla, vemos nuestro texto con una palabra sospechosa señalada. En el centro, vemos diez sugerencias ortográficas numeradas del cero al nueve, seguida de una lista de otras opciones posibles. Finalmente, en la parte inferior, vemos un prompt preparado para aceptar nuestra elección.

Si pulsamos la tecla 1, aspell reemplaza la palabra incorrecta por la palabra "jumped" y se mueve a la siguiente palabra mal escrita, que es "laxy". Si seleccionamos el reemplazo "lazy", aspell la cambia y termina. Una vez que aspell ha terminado, podemos examinar nuestro archivo y ver que los errores han sido corregidos:

[me@linuxbox ~]$ cat foo.txt
The quick brown fox jumped over the lazy dog.

A menos que se le indique en la línea de comandos la opción --dont-backup, aspell crea una copia de seguridad del archivo con el texto original añadiéndole la extensión .bak al nombre del archivo.

Presumiendo de nuestra habilidad editando con sed, pondremos nuestros errores ortográficos de nuevo para que podamos reutilizar nuestro archivo.

[me@linuxbox ~]$ sed -i 's/lazy/laxy/; s/jumped/jimped/' foo.txt

La opción -i de sed para editar el archivo "in-situ", significa que en lugar de enviar la salida editada a la salida estándar, reescribirá el archivo con los cambios aplicados. También vemos la capacidad de colocar más de un comando de edición en la línea separándolos con un punto y coma.

A continuación, veremos como aspell puede manejar diferentes tipos de archivos de texto. Usando un editor de texto como vim (los aventureros pueden probar sed), añadiremos algunas etiquetas HTML a nuestro archivo:

<html>
  <head>
    <title>Mispelled HTML file</title>
  </head>
  <body>
    <p>The quick brown fox jimped over the laxy dog.</p>
  </body>
</html>

Ahora, si tratamos de comprobar la ortografía de nuestro archivo modificado, encontramos un problema. Si lo hacemos así:

[me@linuxbox ~]$ aspell check foo.txt

tendremos esto:

<html>
  <head>
    <title>Mispelled HTML file</title>
  </head>
  <body>
    <p>The quick brown fox jimped over the laxy dog.</p>
  </body>
</html>

                                          
1) HTML            4) Hamel
2) ht ml           5) Hamil
3) ht-ml           6) hotel


i) Ignore          I) Ignore all
r) Replace         R) Replace all
a) Add             l) Add Lower
b) Abort           x) Exit
                                          
?

aspell verá el contenido de las etiquetas HTML como errores ortográficos. El problema puede solucionarse incluyendo la opción de modo de comprobación -H (HTML), como esta:

[me@linuxbox ~]$ aspell -H check foo.txt

que tendrá como resultado esto:

<html>
  <head>
    <title>Mispelled HTML file</title>
  </head>
  <body>
    <p>The quick brown fox jimped over the laxy dog.</p>
  </body>
</html>

                                                                                                
1) Mi spelled       6) Misapplied
2) Mi-spelled       7) Miscalled
3) Misspelled       8) Respelled
4) Dispelled        9) Misspell
5) Spelled          0) Misled
i) Ignore           I) Ignore all
r) Replace          R) Replace all
a) Add              l) Add Lower
b) Abort            x) Exit
                                        
?

El HTML se ignora y sólo las porciones no-HTML del archivo se comprueban. En este modo, el contenido de las etiquetas HTML son ignoradas y no se les comprueba la ortografía. Sin embargo, el contenido de las etiquetas ALT, que es bueno que se comprueben, sí se chequean en este modo.

Nota: Por defecto, aspell ignorará URLs y direcciones de correo electrónico en el texto. Este comportamiento puede ser modificado con opciones de la línea de comandos. También es posible especificar qué etiquetas son comprobadas y cuales son obviadas. Mira la man page de aspell para más detalles.

miércoles, 17 de agosto de 2016

A la gente que le gusta sed también le gusta...

sed es un programa muy competente, permite realizar tareas de edición muy complejas a secuencias de texto. A menudo se usa para tareas de una línea en lugar de para scripts. Muchos usuarios prefieren otras herramientas para tareas más grandes. Los más populares son awk y perl. Estos van más allá de meras herramientas, como los programas que hemos visto aquí, y se adentran en el reino de los lenguajes de programación completos. perl, en particular, se usa a menudo en lugar de los scripts de shell para muchas tareas de administración y mantenimiento de sistemas, así como un medio muy popular en desarrollos web. awk es un poco más especializado. Su fuerza específica es la capacidad de manipular datos tabulares. Se parece a sed en que los programas awk normalmente procesan archivos de texto línea a línea, usando un esquema similar al concepto de sed de dirección seguida de una acción. Aunque tanto awk como perl quedan fuera del objetivo de este libro, son habilidades muy buenas para el usuario de la línea de comandos de Linux.

lunes, 15 de agosto de 2016

sed

El nombre sed es una abreviatura de stream editor (editor de flujo). Realiza edición de texto en una secuencia de texto, ya sea una serie de archivos especificados o en la entrada estándar. sed es un programa potente y algo complejo (hay libros enteros sobre él), así que no lo veremos completamente aquí.

En general, la forma en que funciona sed es que se le da un comando simple de edición (en la línea de comandos) o el nombre de un archivo de script que contenga múltiples comandos, y ejecuta estos comandos sobre cada línea de la secuencia de texto. Aquí tenemos un ejemplo muy simple de sed en acción:

[me@linuxbox ~]$ echo "front" | sed 's/front/back/'
back

En este ejemplo, producimos una secuencia de texto de una palabra usando echo y lo canalizamos a sed. sed, a su vez, ejecuta la instrucción s/front/back/ en el texto de la secuencia y produce la salida "back" como resultado. También podemos considerar este comando de forma parecida al comando "sustitución" (buscar y reemplazar) de vi.

Los comandos en sed comienzan con una sola letra. En el ejemplo anterior, el comando sustitución se representa con la letra s y es seguido por las cadenas de búsqueda y reemplazo, separadas por el carácter de la barra inclinada como separador. La elección del carácter separador es arbitraria. Por consenso, el carácter de la barra inclinada se usa a menudo, pero sed aceptará cualquier carácter que siga inmediatamente al comando como separador. Podríamos realizar el mismo comando de la siguiente forma:

[me@linuxbox ~]$ echo "front" | sed 's_front_back_'
back

Usar el carácter guion bajo inmediatamente tras el comando, lo convierte en el separador. La capacidad de establecer el separador puede usarse para realizar los comandos más legibles, como veremos.

La mayoría de los comandos en sed irán precedidos por una dirección , que especifica qué línea(s) de la secuencia de entrada será editada. Si se omite la dirección, el comando editor se ejecutará en cada línea de la secuencia de entrada. La forma más simple de dirección es un número de línea. Podemos añadir uno a nuestro ejemplo:

[me@linuxbox ~]$ echo "front" | sed '1s/front/back/'
back

Añadiendo la dirección 1 a nuestro comando hacemos que nuestra sustitución se realice en la primera línea de nuestra entrada de una línea. Si especificamos otro número:

[me@linuxbox ~]$ echo "front" | sed '2s/front/back/'
front

vemos que la edición no se ha realizado, ya que nuestra secuencia de entrada no tiene línea 2.

Las direcciones pueden ser expresadas de muchas formas. Aquí tenemos las más comunes:

Tabla 20-7: Notación de direcciones en sed
Dirección Descripción
n Un número de línea donde n es un entero positivo.
$ La última línea
/regexp/ Lineas que coinciden con una expresión regular básica POSIX. Fíjate que la expresión regular se delimita por barras inclinadas. Opcionalmente, la expresión regular puede delimitarse por un carácter alternativo, especificando la expresión con \cregexpc, donde c es el carácter alternativo.
addr1, addr2 Un rango de líneas desde addr1 a addr2, ambos incluidos. Las direcciones pueden estar en cualquiera de las formas que vimos antes.
first~step Encuentra la línea representada por el número first, y luego cada línea siguiente con el intervalo step. Por ejemplo 1~2 se refiere a cada línea impar, 5~5 se refiere a la quinta línea y cada quinta línea después de ella.
addr1, +n Encuentra addr1 y las siguientes n líneas.
addr! Encuentra todas las líneas excepto addr, que puede estar en cualquiera de las formas anteriores.

Probaremos diferentes tipos de direcciones usando el archivo distros.txt que usamos antes en este capítulo. Primero, una rango de números de línea:

[me@linuxbox ~]$ sed -n '1,5p' distros.txt
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

En este ejemplo, obtenemos un rango de líneas, comenzando con la línea 1 y continuando hasta la línea 5. Para hacerlo, usamos el comando p, que simplemente hace que una línea encontrada se muestre. Sin embargo, para que esto sea efectivo, tenemos que incluir la opción -n (la opción sin autoimpresión) para hacer que sed no muestre cada línea por defecto.

A continuación, probemos algunas expresiones regulares:

[me@linuxbox ~]$ sed -n '/SUSE/p' distros.txt
SUSE 10.2 12/07/2006
SUSE 11.0 06/19/2008
SUSE 10.3 10/04/2007
SUSE 10.1 05/11/2006

Incluyendo la expresión regular delimitada por barras inclinadas /SUSE/, podemos aislar las líneas que la contengan casi de la misma forma que con grep.

Finalmente, probaremos la negación añadiendo un signo de exclamación (!) a la dirección:

[me@linuxbox ~]$ sed -n '/SUSE/!p' distros.txt
Fedora 10   11/25/2008
Ubuntu 8.04 04/24/2008
Fedora 8    11/08/2007
Ubuntu 6.10 10/26/2006
Fedora 7    05/31/2007
Ubuntu 7.10 10/18/2007
Ubuntu 7.04 04/19/2007
Fedora 6    10/24/2006
Fedora 9    05/13/2008
Ubuntu 6.06 06/01/2006
Ubuntu 8.10 10/30/2008
Fedora 5    03/20/2006

Aquí vemos el resultado esperado: todas las líneas en el archivo excepto las que coinciden con la expresión regular.

Hasta ahora, hemos visto dos de los comandos de edición de sed, s y p. Aquí tenemos una lista completa de los comandos de edición básicos:

Tabla 20-8: comandos de edición básicos de sed
Comando Descripción
= Muestra el número de línea actual.
a Añade texto tras la línea actual.
d Borra la línea actual.
i Inserta texto antes de la línea actual.
p Muestra la línea actual. Por defecto, sed imprime cada línea y sólo edita líneas que coinciden con una dirección especificada dentro del archivo. El comportamiento por defecto puede sobrescribirse especificando la opción -n.
q Sale de sed sin procesar más líneas. Si la opción -n no se especifica, muestra la línea actual.
Q Sale de sed sin procesar más líneas.
s/regexp/replacement/ Sustituye el contenido de replacement donde se encuentre regexp, replacement puede incluir el carácter &, que es equivalente al texto correspondiente a regexp. Además, replacement puede incluir las secuencias \1 a \9, que son el contenido de las correspondientes subexpresiones de regexp. Para saber más sobre ésto, mira el tema de retroreferencias que está más adelante. Tras la barra invertida posterior a replacement, puede especificarse una marca para modificar el comportamiento del comando s.
y/set1/set2 Realiza transliteración convirtiendo caracteres de set1 a los correspondientes caracteres en set2. Fíjate que al contrario que tr, sed requiere que ambas series tengan la misma longitud.

El comando s es de lejos el comando de edición más comúnmente usado. Probaremos sólo algunas de sus capacidades realizando edición en nuestro archivo distros.txt. Vimos antes cómo el campo fecha en distros.txt no estaba en formato "compatible con ordenadores". Ya que la fecha está formateada MM/DD/YYY, y sería mejor (para ordenarla más fácilmente) si el formato fuera YYYY-MM-DD. Realizar este cambio en el archivo a mano llevaría mucho tiempo y sería propenso a errores, pero con sed, este cambio puede realizarse en un paso:

[me@linuxbox ~]$ sed 's/\([0-9]\{2\}\)\/\([0-9]\{2\}\)\/\([0-9]\{4\}\)$/\3-\1-\2/' distros.txt
SUSE   10.2 2006-12-07
Fedora 10   2008-11-25
SUSE   11.0 2008-06-19
Ubuntu 8.04 2008-04-24
Fedora 8    2007-11-08
SUSE   10.3 2007-10-04
Ubuntu 6.10 2006-10-26
Fedora 7    2007-05-31
Ubuntu 7.10 2007-10-18
Ubuntu 7.04 2007-04-19
SUSE   10.1 2006-05-11
Fedora 6    2006-10-24
Fedora 9    2008-05-13
Ubuntu 6.06 2006-06-01
Ubuntu 8.10 2008-10-30
Fedora 5    2006-03-20

¡Guau! Es un comando muy feo. Pero funciona. En sólo un paso, hemos cambiado el formato de fecha de nuestro archivo. También es un ejemplo perfecto de por qué las expresiones regulares son llamadas, a veces en broma, un medio de "sólo-escritura". Podemos escribirlas, pero a menudo no podemos leerlas. Antes de que estemos tentados a huir aterrorizados de este comando, veamos como ha sido construido. Primero, sabemos que el comando tendrá esta estructura básica:

sed 's/regexp/replacement/' distros.txt

Nuestro próximo paso es encontrar una expresión regular que aisle la fecha. Como está en formato MM/DD/YYYY y aparece al final de la línea, podemos usar una expresión como esta:

[0-9]{2}/[0-9]{2}/[0-9]{4}$

que encuentra dos dígitos, una barra, dos dígitos, una barra, cuatro dígitos y el final de la línea. Así que esto se ocupa de regexp, pero ¿qué pasa con replacement? Para manejar esto, tenemos que introducir una nueva capacidad de las expresiones regulares que aparecen en algunas aplicaciones que usan BRE. Esta capacidad se llama retroreferencias y funcionan así: Si aparece la secuencia \n en replacement donde n es un número del 1 al 9, la secuencia se referirá a la subexpresión correspondiente en la expresión regular precedente. Para crear las subexpresiones, simplemente la metemos entre paréntesis así:

([0-9]{2})/([0-9]{2})/([0-9]{4})$

Ahora tenemos tres subexpresiones. La primera contiene el mes, la segunda contiene el día del mes y la tercera contiene el año. Ahora podemos construir replacement como sigue:

\3-\1-\2

que nos da el año, un guión, el mes, un guión y el día.

Ahora nuestro comando tiene esta pinta:

sed 's/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/' distros.txt

Tenemos dos problemas todavía. El primero es que las barras extra en nuestra expresión confundirán a sed cuando trate de interpretar el comando s. El segundo es que como sed, por defecto, acepta sólo expresiones regulares básicas, varios caracteres en nuestra expresión regular serán tratados como literales, en lugar de como metacaracteres. Podemos resolver ambos problemas con una generosa utilización de barras invertidas para escapar los caracteres ofensivos:

sed 's/\([0-9]\{2\}\)\/\([0-9]\{2\}\)\/\([0-9]\{4\}\)$/\3-\1-\2/' distros.txt

¡Y aquí lo tenemos!

Otra característica del comando s es el uso de marcas opcionales que pueden seguir a la cadena de reemplazo. La más importante es la marca g, que ordena a sed que aplique una búsqueda y reemplazo global a una línea, no sólo a la primera instancia, que es la opción por defecto. Aquí tenemos un ejemplo:

[me@linuxbox ~]$ echo "aaabbbccc" | sed 's/b/B/'
aaaBbbccc

Vemos que se ha realizado el reemplazo, pero sólo en la primera instancia de la letra "b", mientras que las siguientes instancias se han dejado sin cambios. Añadiendo la marca g, podemos cambiar todas las instancias:

[me@linuxbox ~]$ echo "aaabbbccc" | sed 's/b/B/g'
aaaBBBccc

Hasta ahora, sólo le hemos dado a sed comandos individuales a través de la línea de comandos. También es posible construir comandos más complejos en un archivo de script usando la opción -f. Para hacer una demostración, usaremos sed con nuestro archivo distros.txt para crear un informe. Nuestro informe mostrará un título arriba, nuestras fechas modificadas y todos los nombres de las distribuciones convertidos a mayúsculas. Para hacerlo, necesitaremos escribir un script, así que arrancaremos nuestro editor de texto y escribiremos los siguiente:

# sed script to produce Linux distributions report

1 i\
\
Linux Distributions Report\

s/\([0-9]\{2\}\)\/\([0-9]\{2\}\)\/\([0-9]\{4\}\)$/\3-\1-\2/
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/

Guardaremos nuestro script de sed como distros.sed y lo ejecutaremos así:

[me@linuxbox ~]$ sed -f distros.sed distros.txt

Linux Distributions Report

SUSE   10.2 2006-12-07
FEDORA 10   2008-11-25
SUSE   11.0 2008-06-19
UBUNTU 8.04 2008-04-24
FEDORA 8    2007-11-08
SUSE   10.3 2007-10-04
UBUNTU 6.10 2006-10-26
FEDORA 7    2007-05-31
UBUNTU 7.10 2007-10-18
UBUNTU 7.04 2007-04-19
SUSE   10.1 2006-05-11
FEDORA 6    2006-10-24
FEDORA 9    2008-05-13
UBUNTU 6.06 2006-06-01
UBUNTU 8.10 2008-10-30
FEDORA 5    2006-03-20

Como podemos ver, nuestro script produce el resultado esperado, pero ¿cómo lo ha hecho? Echemos otro vistazo a nuestro script. Usaremos cat para numerar las líneas:

[me@linuxbox ~]$ cat -n distros.sed
  1 # sed script to produce Linux distributions report
  2
  3 1 i\
  4 \
  5 Linux Distributions Report\
  6
  7 s/\([0-9]\{2\}\)\/\([0-9]\{2\}\)\/\([0-9]\{4\}\)
$/\3-\1-\2/
  8 y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/

La línea uno de nuestro script es un comentario. Como muchos archivos de configuración y lenguajes de programación en sistemas Linux, los comentarios comienzan con el caracter # y siguen con texto legible por humanos. Los comentarios pueden colocarse en cualquier parte del script (aunque no dentro de los propios comandos) y son útiles para las personas que necesiten identificar y/o mantener el script.

La línea 2 es una línea en blanco. Como los comentarios, las líneas en blanco deben añadirse para mejorar la legibilidad.

Muchos comandos sed soportan direcciones de línea. Se usan para especificar en qué líneas de entrada hay que actuar. Las direcciones de línea pueden expresarse como números de línea individuales, rangos de números de líneas y el número especial de línea "$" que indica la última línea de la entrada.

Las líneas 3 a 6 contienen texto a insertar en la dirección 1, la primera línea de la entrada. Al comando i le sigue la secuencia barra invertida-retorno de carro para producir un retorno de carro escapado, o lo que se llama un carácter de continuación de línea. Esta secuencia, que puede usarse en muchas circunstancias incluyendo scripts de shell, permite insertar un retorno de carro en una secuencia de texto sin indicar al intérprete (en este caso sed) que se ha alcanzado el final de la línea. Los comandos i, y de igual forma, a (que añade texto, en lugar de insertarlo) y c (que reemplaza texto), permiten múltiples líneas de texto siempre que cada línea, excepto la última, termine con el carácter continuación de línea. La sexta línea de nuestro script es realmente el final de nuestro texto insertado y termina con un simple retorno de carro, en lugar de con un carácter de continuación de línea, señalándole el final al comando i.

Nota: Un carácter de continuación de línea lo forman una barra invertida seguida inmediatamente por un retorno de carro. No se permiten espacios intermedios.

La línea 7 es nuestro comando de búsqueda y reemplazo. Como no está precedido por una dirección, cada línea de la secuencia de entrada está sometida a su acción.

La línea 8 realiza transliteración de las letras minúsculas a letras mayúsculas. Fíjate que al contrario que tr, el comando y de sed no soporta rangos de caracteres (por ejemplo, [a-z]), ni soporta clases de caracteres POSIX. De nuevo, como el comando y no está precedido por una dirección, se aplica a cada línea de la secuencia de entrada.

sábado, 13 de agosto de 2016

ROT13: El anillo decodificador no tan secreto

Un uso divertido de tr es realizar un cifrado de texto ROT13. ROT13 es un tipo de cifrado trivial basado en un cifrado de sustitución simple. Llamar a ROT13 "cifrado" es ser generosos; "ofuscación de texto" es más correcto. Se usa algunas veces en texto para esconder contenido potencialmente ofensivo. El método simplemente mueve cada carácter 13 posiciones hacia delante en el alfabeto. Como esto supone la mitad de los 26 caracteres posibles, aplicando el algoritmo una segunda vez al texto lo restauramos a su forma original. Para realizar este cifrado con tr:

echo "secret text" | tr a-zA-Z n-za-mN-ZA-M
frperg grkg

Realizando el mismo procedimiento una segunda vez obtenemos la traducción:

echo "frperg grkg" | tr a-zA-Z n-za-mN-ZA-M
secret text

Numerosos programas de correo electrónico y lectores de noticias de Usenet soportan cifrado ROT13. Wikipedia tiene un buen artículo sobre el asunto:

http://en.wikipedia.org/wiki/ROT13

viernes, 12 de agosto de 2016

tr

El programa tr se usa para transliterar caracteres. Podemos pensar en esto como un tipo de operación de búsqueda y reemplazo basada en caracteres. La transliteración es el proceso de cambiar caracteres de un alfabeto a otro. Por ejemplo, convertir caracteres de minúscula a mayúscula es transliteración. Podemos realizar esa conversión con tr como sigue:

[me@linuxbox ~]$ echo "lowercase letters" | tr a-z A-Z
LOWERCASE LETTERS

Como podemos ver, tr funciona con la entrada estándar, y muestra sus resultados en la salida estándar. tr acepta dos argumentos: una serie de caracteres de donde convertir y una serie correspondiente de caracteres hacia donde convertir. Las series de caracteres pueden expresarse de tres formas diferentes:
  1. Una lista enumerada. Por ejemplo, ABCDEFGHIJKLMNOPQRSTUVWXYZ
  2. Un rango de caracteres. Por ejemplo, A-Z. Fíjate que este método está sujeto a menudo a los mismos problemas que otros comandos, debido a la secuencia de ordenado de la configuración regional, y por lo tanto hay que usarlo con precaución.
  3. Clases de caracteres POSIX. Por ejemplo, [:upper:].
En la mayoría de los casos, ambas series de caracteres serán de igual longitud; sin embargo, es posible que la primera serie sea más grande que la segunda, particularmente si queremos convertir muchos caracteres a uno único:

[me@linuxbox ~]$ echo "lowercase letters" | tr [:lower:] A
AAAAAAAAA AAAAAAA

Además de la transliteración, tr permite que los caracteres sean simplemente eliminados de la cadena de entrada. Antes, en este capítulo, vimos el problema de convertir archivos de texto MS-DOS a texto estilo-Unix. Para realizar esta conversión, los caracteres de retorno de carro necesitan ser eliminados del final de cada línea. Esto puede realizarse con tr de la forma siguiente:

tr -d '\r' < dos_file > unix_file

donde dos_file es el archivo a convertir y unix_file es el resultado. Esta forma del comando utiliza la secuencia de escape \r para representar el carácter retorno de carro. Para ver una lista completa de las secuencias y clases de caracteres que soporta tr, prueba:

[me@linuxbox ~]$ tr --help

tr también puede realizar otro truco. Usando la opción -s, tr puede "estrujar" (borrar) las instancias repetidas de un caracter:

[me@linuxbox ~]$ echo "aaabbbccc" | tr -s ab
abccc

Aquí tenemos un cadena que contiene caracteres repetidos. Especificando la lista "ab" a tr, eliminamos las instancias repetidas de las letras de la lista, mientras que deja los caracteres que no aparecen en la lista ("c") sin cambios. Fíjate que los caracteres repetidos debe estar contiguos. Si no lo están:

[me@linuxbox ~]$ echo "abcabcabc" | tr -s ab
abcabcabc

la eliminación no tendrá efecto.

jueves, 11 de agosto de 2016

Editando sobre la marcha

Nuestra experiencia con editores de texto ha sido muy interactiva, ya que movemos el cursor manualmente, y luego escribimos nuestros cambios. Sin embargo, también hay formas no-interactivas de editar texto. Es posible, por ejemplo, aplicar una serie de cambios a múltiples archivos con un único comando.

miércoles, 10 de agosto de 2016

patch

El programa patch se usa para aplicar cambios a archivos de texto. Acepta salida de diff y generalmente se usa para convertir versiones antiguas de archivos en versiones más nuevas. Consideremos un ejemplo famoso. El kernel Linux es desarrollado por un equipo grande y poco organizado de contribuidores que envían un flujo constante de pequeños cambios al código fuente. El kernel Linux consta de varios millones de líneas de código, mientras que los cambios que cada vez hace un contribuidor son bastante pequeños. No tiene sentido que un contribuidor envíe a cada desarrollador un código fuente completo del kernel cada vez que realiza un pequeño cambio. En su lugar, envía un archivo diff. El archivo diff contiene el cambio de la versión anterior del kernel a la nueva versión con los cambios del contribuidor. El que lo recibe usa el programa patch para aplicar el cambio a su propio código fuente. Usar diff/patch proporciona dos ventajas significativas:
  1. El archivo diff es muy pequeño, comparado con el tamaño del código fuente completo.
  2. El archivo diff muestra de forma concisa el cambio a realizar, permitiendo a los revisores del parche evaluarlo rápidamente.
Claro que, diff/patch funcionará con cualquier archivo de texto, no sólo con código fuente. Será igualmente aplicable a archivos de configuración o cualquier otro texto.

Para preparar un archivo diff para usarlo con patch, la documentación GNU (consulta la sección Para Saber Más al final del capítulo) sugiere usar diff de la siguiente manera:

diff -Naur old_file new_file > diff_file

Donde old_file y new_file son archivos individuales o directorios que contengan archivos. La opción r soporta recursividad en el árbol de directorios.

Una vez que el archivo diff ha sido creado, podemos aplicarlo para parchear el archivo antiguo y convertirlo en el nuevo archivo:

patch < diff_file

Lo demostraremos con nuestro archivo de pruebas:

[me@linuxbox ~]$ diff -Naur file1.txt file2.txt > patchfile.txt
[me@linuxbox ~]$ patch < patchfile.txt
patching file file1.txt
[me@linuxbox ~]$ cat file1.txt
b
c
d
e

En este ejemplo, hemos creado un archivo diff llamado patchfile.txt y luego hemos usado el programa patch para aplicar el parche. Fíjate que no hemos tenido que especificar un archivo de destino para patch, ya que el archivo diff (en formato unificado) ya contiene los nombres de archivo en la cabecera. Una vez que se aplica el parche, podemos ver que file1.txt ahora coincide con file2.txt.

patch tiene una gran número de opciones, y hay programas adicionales que pueden usarse para analizar y editar parches.

martes, 9 de agosto de 2016

diff

Al igual que el programa comm, diff se usa para detectar diferencias entre archivos. Sin embargo, diff es una herramienta mucho más compleja, soporta muchos formatos de salida y la capacidad de procesar grandes colecciones de archivos de texto a la vez. diff a menudo se usa por los desarrolladores de software para examinar cambios entre versiones diferentes de código fuente del programa, y además tiene la capacidad de examinar recursivamente directorios de código fuente, a menudo llamados árboles fuente (source trees). Un uso común para diff es crear archivos diff o parches que se usan por programas como patch (que veremos pronto) para convertir una versión de un archivo (o archivos) a otra versión.

Si usamos diff para ver nuestros archivos de ejemplo previos:

[me@linuxbox ~]$ diff file1.txt file2.txt
1d0
< a
4a4
> e

vemos su estilo de salida por defecto: una breve descripción de las diferencias entre los dos archivos. En el formato por defecto, cada grupo de cambios va precedido por un comando de cambio en la forma de un rango operación rango para describir las posiciones y tipos de cambios requeridos para convertir el primer archivo en el segundo:

Tabla 20-4: Comandos de cambio de diff
Cambio Descripción
r1ar2 Añade las líneas en la posición r2 del segundo archivo a la posición r1 del primer archivo.
r1cr2 Cambia (reemplaza) las lineas en la posición r1 con las líneas en la posición r2 en el segundo archivo.
r1dr2 Borra las líneas en el primer archivo en la posición r1, que habrían aparecido en el rango r2 del segundo archivo.

En este formato, un rango es una lista separada por comas de la línea inicial y la línea final. Aunque este formato es el que viene por defecto (principalmente por conformidad con POSIX y retrocompatibilidad con versiones tradicionales de diff de Unix), no está tan ampliamente usado como otros formatos opcionales. Dos de los formatos más populares son el formato contextual y el formato unificado.

Cuando lo vemos usando el formato contextual (la opción -c), veremos ésto:

[me@linuxbox ~]$ diff -c file1.txt file2.txt
*** file1.txt 2008-12-23 06:40:13.000000000 -0500
--- file2.txt 2008-12-23 06:40:34.000000000 -0500
***************
*** 1,4 ****
- a
  b
  c
  d
--- 1,4 ----
  b
  c
  d
+ e

La salida comienza con los nombres de los dos archivos y sus fechas de modificación. El primer archivo está marcado con asteriscos y el segundo archivo está marcado con guiones. En el resto de la lista, estos marcadores simbolizarán sus respectivos archivos. A continuación, vemos grupos de cambios, incluyendo el número predeterminado de líneas de contexto existentes. En el primer grupo, vemos:

*** 1,4 ***

que indica líneas de la 1 a la 4 en el primer archivo. A continuación vemos:

--- 1,4 ---

que indica líneas de la 1 a la 4 en el segundo archivo. Dentro de un grupo de cambios, las líneas comienzan con uno de estos cuatro indicadores:

Tabla 20-5: Indicadores de cambios de diff en formato contextual
Indicador Significado
blank Una línea de contexto mostrada. Indica que no hay ninguna diferencia entre los dos archivos.
- Una línea borrada. Esta línea aparecerá en el primer archivo pero no en el segundo.
+ Una línea añadida. Esta línea aparecerá en el segundo archivo pero no en el primero.
! Una línea modificada. Las dos versiones de la línea se mostrarán, cada una en su respectiva sección del grupo de cambio.

El formato unificado es similar al formato contextual pero es más conciso. Se especifica con la opción -u:

[me@linuxbox ~]$ diff -u file1.txt file2.txt
--- file1.txt 2008-12-23 06:40:13.000000000 -0500
+++ file2.txt 2008-12-23 06:40:34.000000000 -0500
@@ -1,4 +1,4 @@
-a
 b
 c
 d
+e

La diferencia más destacable entre los formatos contextual y unificado es la eliminación de las líneas duplicadas de contexto, haciendo que los resultados del formato unificado sean más cortos que los del formato contextual. En nuestro ejemplo anterior, vemos las marcas de tiempo de los archivos como los del formato contextual, seguidas de la cadena @@ -1,4 +1,4 @@. Esto indica las líneas del primer archivo y las líneas del segundo archivo descritas en el grupo de cambio. Tras ésto están las líneas en sí, con las tres líneas de contexto por defecto. Cada línea comienza con uno de tres posibles caracteres:

Tabla 20-6: Indicadores de cambios de diff en formato unificado
Carácter Significado
blank Esta línea la comparten ambos archivos.
- Esta línea se eliminó del primer archivo.
+ Esta línea se añadió al primer archivo

lunes, 8 de agosto de 2016

comm

El programa comm compara dos archivos de texto y muestra las líneas que son exclusivas de cada uno y las líneas que tienen en común. Para demostrarlo, crearemos dos archivos de texto casi idénticos usando cat:

[me@linuxbox ~]$ cat > file1.txt
a
b
c
d
[
me@linuxbox ~]$ cat > file2.txt
b
c
d
e

A continuación, compararemos los dos archivos usando comm:

[me@linuxbox ~]$ comm file1.txt file2.txt
a
        b
        c
        d
    e

Como podemos ver, comm produce tres columnas de salida. La primera columna contiene líneas exclusivas del primer archivo; la segunda columna, las líneas exclusivas del segundo archivo; la tercera columna contiene las líneas compartidas por ambos archivos. comm soporta opciones en la forma -n donde n es 1, 2 o 3. Cuando se usan, estas opciones especifican qué columna(s) suprimir. Por ejemplo, si sólo queremos obtener las líneas compartidas por ambos archivos, suprimiremos la salida de las columnas uno y dos:

[me@linuxbox ~]$ comm -12 file1.txt file2.txt
b
c
d

viernes, 5 de agosto de 2016

Comparando texto

A menudo es útil comparar versiones de archivos de texto. Para administradores de sistemas y desarrolladores de software, es particularmente importante. Un administrador de sistemas podría, por ejemplo, necesitar comparar un archivo de configuración existente con una versión anterior para diagnosticar un problema del sistema. Igualmente, un programador necesita frecuentemente ver qué cambios se han realizado a los programas a lo largo del tiempo.

jueves, 4 de agosto de 2016

join

En algunos aspectos, join es como paste en cuanto a que añade columnas a un archivo, pero usa una forma única para hacerlo. Un join es una operación normalmente asociada con las bases de datos relacionales donde los datos de múltiples tablas con un campo clave compartido se combinan para dar el resultado deseado. El programa join realiza la misma operación. Une datos de múltiples archivos basándose en un campo clave compartido.

Para ver cómo se usa una operación join en una base de datos relacional, imaginemos una base de datos muy pequeña consistente en dos tablas, cada una con un único registro. La primera tabla, llamada CUSTOMERS, tiene tres campos: un número de cliente (CUSTNUM), el nombre del cliente (FNAME) y el apellido del cliente (LNAME):

CUSTNUM  FNAME  LNAME
======== =====  ======
4681934  John   Smith

La segunda tabla se llama ORDERS y contiene cuatro campos: un número de pedido (ORDER-NUM), el número de cliente (CUSTNUM), la cantidad (QUAN) y el producto pedido (ITEM).

ORDERNUM    CUSTNUM QUAN ITEM
========    ======= ==== ====
3014953305  4681934 1    Blue Widget

Fíjate que ambas tablas comparten el campo CUSTNUM. Esto es importante, ya que permite la relación entre las tablas.

Realizar una operación join nos permitiría combinar los campos en las dos tablas para conseguir un resultado útil, como preparar una factura. Usando los valores coincidentes en los campos CUSTNUM de ambas tablas, una operación join podría producir lo siguiente:

FNAME LNAME QUAN ITEM
===== ===== ==== ====
John  Smith 1    Blue Widget

Para probar el programa join, necesitaremos crear un par de archivos con una clave compartida. Para hacerlo, usaremos nuestro archivo distros-by-date.txt. Desde este archivo, construiremos dos archivos adicionales, uno conteniendo las fechas de lanzamiento (que será nuestra clave compartida para esta demostración) y los nombres de la versión:

[me@linuxbox ~]$ cut -f 1,1 distros-by-date.txt > distros-names.txt
[me@linuxbox ~]$ paste distros-dates.txt distros-names.txt > distros-key-names.txt
[me@linuxbox ~]$ head distros-key-names.txt
11/25/2008 Fedora
10/30/2008 Ubuntu
06/19/2008 SUSE
05/13/2008 Fedora
04/24/2008 Ubuntu
11/08/2007 Fedora
10/18/2007 Ubuntu
10/04/2007 SUSE
05/31/2007 Fedora
04/19/2007 Ubuntu

y el segundo archivo, que contiene las fechas de lanzamiento y los números de versión:

[me@linuxbox ~]$ cut -f 2,2 distros-by-date.txt > distros-vernums.txt
[me@linuxbox ~]$ paste distros-dates.txt distros-vernums.txt > distros-key-vernums.txt
[me@linuxbox ~]$ head distros-key-vernums.txt
11/25/2008 10
10/30/2008 8.10
06/19/2008 11.0
05/13/2008 9
04/24/2008 8.04
11/08/2007 8
10/18/2007 7.10
10/04/2007 10.3
05/31/2007 7
04/19/2007 7.04

Ahora tenemos dos archivos con una clave compartida (el campo "fecha de lanzamiento"). Es importante señalar que los archivos deben estar ordenados por el campo clave para que join funcione apropiadamente.

[me@linuxbox ~]$ join distros-key-names.txt distros-key-vernums.txt | head
11/25/2008 Fedora 10
10/30/2008 Ubuntu 8.10
06/19/2008 SUSE 11.0
05/13/2008 Fedora 9
04/24/2008 Ubuntu 8.04
11/08/2007 Fedora 8
10/18/2007 Ubuntu 7.10
10/04/2007 SUSE 10.3
05/31/2007 Fedora 7
04/19/2007 Ubuntu 7.04

Fíjate también que, por defecto, join usa el espacio en blanco como el delimitador de campos de entrada y un espacio individual como el delimitador de campos de salida. Este comportamiento puede modificarse especificando opciones. Mira la man page de join para más detalles.