jueves, 30 de junio de 2016

+ - Encuentra un elemento una o más veces

El metacarácter + funciona de forma muy parecida a *, excepto que requiere al menos una instancia del elemento precedente para que ocurra una coincidencia. Aquí tenemos una expresión regular que sólo encontrará líneas consistentes en grupos de uno o más caracteres alfabéticos separados por un espacio:

^([[:alpha:]]+ ?)+$
[me@linuxbox ~]$ echo "This that" | grep -E '^([[:alpha:]]+ ?)+$'
This that
[me@linuxbox ~]$ echo "a b c" | grep -E '^([[:alpha:]]+ ?)+$'
a b c
[me@linuxbox ~]$ echo "a b 9" | grep -E '^([[:alpha:]]+ ?)+$'
[me@linuxbox ~]$ echo "abc  d" | grep -E '^([[:alpha:]]+ ?)+$'
[me@linuxbox ~]$

Vemos que esta expresión no encuentra la línea "a b 9", porque contiene un carácter no alfabético; ni encuentra "abc d", por que los caracteres "c" y "d" están separados por más de un espacio en blanco.

miércoles, 29 de junio de 2016

* - Encuentra un elemento cero o más veces

Como el metacarácter ?, el * se usa para señalar un elemento opcional; sin embargo, al contrario que ?, el elemento puede aparecer varias veces, no sólo una vez. Digamos que queremos ver si una cadena era una frase; es decir, que comienza con una mayúscula, luego contiene cualquier número de letras mayúsculas o minúsculas y espacios, y termina con un punto. Para encontrar esta definición (muy básica) de una frase, podríamos usar una expresión regular como ésta:

[[:upper:]][[:upper:][:lower:] ]*\.

La expresión consta de tres elementos: un expresión entre corchetes conteniendo la clase de caracteres [:upper:], una expresión entre corchetes conteniendo tanto la clase de caracteres [:upper:] como [:lower:] y un espacio, y un punto escapado por una barra invertida. El segundo elemento está seguido de un metacarácter *, de forma que tras la letra mayúscula del principio de nuestra frase, cualquier cantidad de letras mayúculas o minúsculas y espacios que le sigan serán encontrados:

[me@linuxbox ~]$ echo "This works." | grep -E '[[:upper:]][[:upper:][:lower:] ]*\.'
This works.
[me@linuxbox ~]$ echo "This Works." | grep -E '[[:upper:]][[:upper:][:lower:] ]*\.'
This Works.
[me@linuxbox ~]$ echo "this does not" | grep -E '[[:upper:]][[:upper:][:lower:] ]*\.'
[me@linuxbox ~]$

La expresión coincide en los dos primeros test, pero no en el tercero, ya que carece de la mayúscula al principio y del punto al final.

martes, 28 de junio de 2016

? - Encuentra un elemento cero o una vez

Este cuantificador significa, en la práctica, "Haz el elemento precedente opcional." Digamos que queremos comprobar la validez de un número de teléfono y consideramos que un número de teléfono es válido si encaja en alguna de estas dos formas:

(nnn) nnn-nnnn
nnn nnn-nnnn

donde "n" es un número. Podríamos construir una expresión regular como esta:

^\(?[0-9][0-9][0-9]\)? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$

En esta expresión escribimos, después de los caracteres de los paréntesis, signos de interrogación para indicar que tienen que ser comprobados cero o una vez. De nuevo, como los paréntesis son normalmente metacaracteres (en ERE), los precedemos con barras invertidas para hacer que sean tratados como literales.

Probémoslo:

[me@linuxbox ~]$ echo "(555) 123-4567" | grep -E '^\(?[0-9][0-9][0-9]\)? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$'
(555) 123-4567
[me@linuxbox ~]$ echo "555 123-4567" | grep -E '^\(?[0-9][0-9][0-9]\)? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$'
555 123-4567
[me@linuxbox ~]$ echo "AAA 123-4567" | grep -E '^\(?[0-9][0-9][0-9]\)? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$'
[me@linuxbox ~]$

Aquí vemos que la expresión encuentra ambos formatos del número de teléfono, pero no encuentra ninguno que contenga caracteres no numéricos.

lunes, 27 de junio de 2016

Cuantificadores

Las expresiones regulares extendidas soportan varias formas de especificar el número de veces que se encuentra un elemento.

viernes, 24 de junio de 2016

Alternancia

La primera característica de las expresiones regulares extendidas que veremos se llama alternancia, que es la función que nos permite que se produzca una coincidencia entre un conjunto de expresiones. Del mismo modo que una expresión entre corchetes permite que un carácter único se encuentre dentro de una serie de caracteres especificados, la alternancia permite coincidencias dentro de una serie de cadenas u otras expresiones regulares.

Para demostrarlo, usaremos grep junto con echo. Primero, probemos una antigua y sencilla coincidencia:

[me@linuxbox ~]$ echo "AAA" | grep AAA
AAA
[me@linuxbox ~]$ echo "BBB" | grep AAA
[me@linuxbox ~]$

Un ejemplo muy sencillo, en el que canalizamos la salida de echo a grep y vemos el resultado. Cuando ocurre una coincidencia, vemos que se imprime; cuando no hay coincidencias, no vemos resultados.

Ahora añadiremos alternancia, indicada por el metacarácter de la barra vertical:

[me@linuxbox ~]$ echo "AAA" | grep -E 'AAA|BBB'
AAA
[me@linuxbox ~]$ echo "BBB" | grep -E 'AAA|BBB'
BBB
[me@linuxbox ~]$ echo "CCC" | grep -E 'AAA|BBB'
[me@linuxbox ~]$

Aquí vemos la expresión regular 'AAA|BBB', que significa "encuentra la cadena AAA o la cadena BBB." Fíjate que como es una característica extendida, añadimos la opción -E a grep (aunque podríamos simplemente usar el programa egrep en su lugar), e incluiremos la expresión regular entre comillas para prevenir que el shell interprete el metacaracter de la barra vertical como el operador de canalizar. La alternancia no está limitada a dos opciones:

[me@linuxbox ~]$ echo "AAA" | grep -E 'AAA|BBB|CCC'
AAA

Para combinar la alternancia con otros elementos de expresiones regulares, podemos usar () para separar la alternancia:

[me@linuxbox ~]$ grep -Eh '^(bz|gz|zip)' dirlist*.txt

Esta expresión encontrará los nombres de archivo en nuestras listas que empiecen con "bz", "gz" o "zip". Si eliminamos los paréntesis, el significado de esta expresión regular:

[me@linuxbox ~]$ grep -Eh '^bz|gz|zip' dirlist*.txt

cambia y encuentra cualquier nombre de archivo que comience por "bz" o contenga "gz" o "zip".

jueves, 23 de junio de 2016

POSIX

Durante los años ochenta, Unix se volvió un sistema operativo comercial muy popular, pero alrededor de 1988, el mundo Unix se volvió muy confuso. Muchos fabricantes de ordenadores obtuvieron la licencia del código fuente de Unix de sus creadores, AT&T, y vendieron varias versiones del sistema operativo con sus equipos. Sin embargo, en su esfuerzo de crear diferenciación de producto, cada fabricante añadió cambios y extensiones propietarias. Esto empezó a limitar la compatibilidad del software. Como siempre pasa con los vendedores propietarios, cada uno intentaba un juego ganador de "atrapar" a sus clientes. La época oscura de la historia de Unix es conocida hoy en día como "la Balcanización".

Aparece el IEEE (Institute of Electrical and Electronics Engineers - Instituto de Ingenieros Eléctricos y Electrónicos). A mediados de los 80, el IEEE comenzó a desarrollar una serie de estándares que definirían cómo se comportarían los sistemas Unix (y tipo Unix). Estos estándares, formalmente conocidos como IEEE 1003, definen las application programing interfaces - Interfaces de programación de aplicaciones (APIs), shell y utilidades que deben encontrarse en un sistema tipo Unix estándar. El nombre "POSIX", que viene de Portable Operating System Interface - Interfaz de Sistema Operativo Portable (con la "X" al final para que tenga más chispa), fue sugerido por Richard Stallman (si, ese Richard Stallman), y fué adoptado por el IEEE.

miércoles, 22 de junio de 2016

POSIX básico vs. Expresiones regulares extendidas

Justo cuando pensábamos que no podía ser más confuso, descubrimos que POSIX también divide las expresiones regulares en dos tipos: expresiones regulares básicas (BRE - Basic regular expressions) y expresiones regulares extendidas (ERE - extended regular expressions). Las características que hemos visto hasta ahora son soportadas por cualquier aplicación que sea compatible con POSIX e implemente BRE. Nuestro programa grep es uno de esos programas.

¿Cuál es la diferencia entre BRE y ERE? Es un asunto de metacaracteres. Con BRE, se reconocen los siguientes metacaracteres:

^ $ . [ ] *

Todos los demás caracteres se consideran literales. Con ERE, se añaden los siguientes metacaracteres (y sus funciones asociadas):

( ) { } ? + |

Sin embargo (y esta es la parte divertida), los caracteres "(", ")", "{" y "}" se tratan como metacaracteres en BRE si son escapados con una barra invertida, mientras que con ERE, preceder cualquier metacaracter con una barra invertida hace que sea tratado como un literal. Cualquier rareza que se presente será tratada en los temas siguientes.

Como las características que vamos a ver a continuación son parte de ERE, vamos a necesitar un grep diferente. Tradicionalmente, se ha utilizado el programa egrep, pero la versión GNU de grep también soporta expresiones regulares extendidas cuando se usa la opción -E.

martes, 21 de junio de 2016

Volviendo a la secuencia de ordenado tradicional

Puedes optar porque tu sistema use la secuencia de ordenado tradicional (ASCII) cambiando el valor de la variable de entorno LANG. Como vimos antes, la variable LANG contiene el nombre del idioma y el catálogo de caracteres usados en la configuración regional. Este valor se determinó, originalmente, cuando seleccionaste un idioma de instalación al instalar Linux.

Para ver la configuración regional, usa el comando locale:

[me@linuxbox ~]$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

Para cambiar la configuración regional para usar el comportamiento tradicional de Unix, cambia la variable LANG a POSIX:

[me@linuxbox ~]$ export LANG=POSIX

Fíjate que este cambio convierte en tu sistema el inglés de EE.UU. (más específicamente, ASCII) en su catálogo de caracteres, así que asegúrate que es realmente lo que quieres.

Puedes hacer que este cambio sea permanente añadiendo esta línea a tu archivo .bashrc:

export LANG=POSIX

lunes, 20 de junio de 2016

Clases de caracteres POSIX

Los rangos tradicionales de caracteres son una forma fácilmente compresible y efectiva de manejar el problema de especificar colecciones de caracteres rápidamente. Desafortunadamente, no siempre funcionan. Aunque no hemos encontrado problemas usando grep hasta ahora, podríamos tener problemas con otros programas.

Volviendo al capítulo 4, vimos cómo los comodines se usan para realizar expansiones en los nombres de las rutas. En dicho tema, dijimos que los rangos de caracteres podían usarse de forma casi idéntica a la forma en que se usan en expresiones regulares, pero aquí está el problema:

[me@linuxbox ~]$ ls /usr/sbin/[ABCDEFGHIJKLMNOPQRSTUVWXYZ]*
/usr/sbin/MAKEFLOPPIES
/usr/sbin/NetworkManagerDispatcher
/usr/sbin/NetworkManager

(Dependiendo de la distribución Linux, obtendremos una lista diferente de archivos, posiblemente una lista vacía. Este ejemplo es de Ubuntu). Este comando produce el resultado esperado - una lista de los archivos cuyos nombres comienzan con una letra mayúscula, pero:

[me@linuxbox ~]$ ls /usr/sbin/[A-Z]*
/usr/sbin/biosdecode
/usr/sbin/chat
/usr/sbin/chgpasswd
/usr/sbin/chpasswd
/usr/sbin/chroot
/usr/sbin/cleanup-info
/usr/sbin/complain
/usr/sbin/console-kit-daemon

con este comando obtenemos un resultado completamente diferente (sólo se muestra una lista parcial de resultados). ¿Por qué? Es una larga historia, pero aquí tienes un resumen:

En la época en que Unix fue desarrollado por primera vez, sólo entendía caracteres ASCII, y esta característica refleja este hecho. En ASCII, los primeros 32 caracteres (los números 0 a 31) son códigos de control (cosas como tabuladores, retrocesos y retornos de carro). Los siguientes 32 (32 a 63) contienen caracteres imprimibles, incluyendo la mayoría de los signos de puntuación y los números del cero al nueve. Los siguientes 32 (64 a 95) contienen las letras mayúsculas y algunos signos de puntuación más. Los últimos 31 (números del 96 al 127) contienen las letras minúsculas y todavía más signos de puntuación. Basándose en esta disposición, los sistemas que usan ASCII utilizan una secuencia de ordenado (collation order) que tiene esta pinta:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

Esto difiere del orden correcto del diccionario, que es así:

aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ

Cuando la popularidad de Unix se expandió fuera de los Estados Unidos, creció la necesidad de soportar caracteres que no existen en el inglés de EE.UU. La tabla ASCII se incrementó para usar ochos bits completos, añadiendo los números de caracteres 128-255, que albergan muchos más idiomas. Para soportar esta capacidad, los estándares POSIX introdujeron un concepto llamado un locale o configuración regional, que puede ajustarse para seleccionar el conjunto de caracteres necesarios para una localización particular. Podemos ver la configuración de idioma de nuestro sistema usando este comando:

[me@linuxbox ~]$ echo $LANG
en_US.UTF-8

Con esta configuración, las aplicaciones compatibles con POSIX usarán la secuencia de ordenado del diccionario en lugar del orden ASCII. Esto explica el comportamiento de los comandos anteriores. Un rango de caracteres de [A-Z] cuando es interpretado en orden del diccionario incluye todos los caracteres alfabéticos excepto la "a" minúscula, de ahí nuestros resultados.

Para evitar este problema, el estándar POSIX incluye un número de clases de caracteres que proporcionan rangos útiles de caracteres. Están descritas en la siguiente tabla:

Tabla 19-2: Clases de caracteres POSIX
Clases de Caracteres Descripción
[:alnum:] Los caracteres alfanuméricos. En ASCII, equivalente a:
[A-Za-z0-9]
[:word:] Los mismo que [:alnum:], con el añadido del carácter subrayado (_).
[:alpha:] Los caracteres alfabéticos. En ASCII, equivalente a:
[A-Za-z]
[:blank:] Incluye los caracteres del espacio y tabulador.
[:cntrl:] Los caracteres de control ASCII. Incluyen los caracteres ASCII del 0 al 31 y el 127.
[:digit:] Los números del cero al nueve.
[:graph:] Los caracteres visibles. En ASCII, incluye los caracteres del 33 al 126.
[:lower:] Las letras minúsculas.
[:punct:] Los símbolos de puntuación. En ASCII, equivalente a:
[-!"#$%&'()*+,./:;<=>?@[\\\]_`{|}~]
[:print:] Los caracteres imprimibles. Los caracteres de [:graph:] más el carácter espacio.
[:space:] Los caracteres de espacio en blanco, incluyendo el espacio, el tabulador, el retorno de carro, nueva linea, tabulador vertical, y salto de página. En ASCII equivalente :
[ \t\r\n\v\f]
[:upper:] Los caracteres en mayúsculas.
[:xdigit:] Los caracteres usados para expresar números hexadecimales. En ASCII, equivalente a:
[0-9A-Fa-f]

Incluso con las clases de caracteres, sigue sin haber una forma conveniente de expresar rangos parciales, como [A-M].

Usando las clases de caracteres, podemos repetir nuestro listado de directorios y ver un resultado mejorado:

[me@linuxbox ~]$ ls /usr/sbin/[[:upper:]]*
/usr/sbin/MAKEFLOPPIES
/usr/sbin/NetworkManagerDispatcher
/usr/sbin/NetworkManager

Recuerda, sin embargo, que esto no es un ejemplo de expresión regular, es por el contrario el resultado de una expansión de ruta que realiza el shell. Lo vemos porque las clases de caracteres POSIX pueden usarse en ambos casos.

viernes, 17 de junio de 2016

Rangos de caracteres tradicionales

Si queremos construir una expresión regular que encuentre cualquier archivo en nuestra lista que empiece con una letra mayúscula, podríamos hacer ésto:

[me@linuxbox ~]$ grep -h '^[ABCDEFGHIJKLMNOPQRSTUVWXZY]' dirlist*.txt

Es simplemente una forma de poner todas las letras mayúsculas en una expresión entre corchetes. Pero la idea de escribir todo eso es muy inquietante, así que aquí tenemos otra forma:

[me@linuxbox ~]$ grep -h '^[A-Z]' dirlist*.txt
MAKEDEV
ControlPanel
GET
HEAD
POST
X
X11
Xorg
MAKEFLOPPIES
NetworkManager
NetworkManagerDispatcher

Usando un rango de tres caracteres, podemos abreviar las 26 letras. Cualquier rango de caracteres puede expresarse de esta forma incluyendo rangos múltiples, como esta expresión que encuentra todos los archivos cuyo nombre empieza con letras y números:

[me@linuxbox ~]$ grep -h '^[A-Za-z0-9]' dirlist*.txt

En los rangos de caracteres, vemos que el carácter guión se trata especialmente, entonces ¿cómo incluimos el carácter guión en una expresión entre corchetes? Haciéndolo el primer carácter en la expresión. Considera estos dos ejemplos:

[me@linuxbox ~]$ grep -h '[A-Z]' dirlist*.txt

Ésto encontrará todos los archivos cuyo nombre contiene una letra mayúscula. Mientras que:

[me@linuxbox ~]$ grep -h '[-AZ]' dirlist*.txt

encontrará todos los archivos cuyo nombre contiene un guión, o una letra "A" mayúscula o una letra "Z" mayúscula.

jueves, 16 de junio de 2016

Negación

Si el primer carácter en una expresión entre corchetes es el símbolo de intercalación (^), los caracteres restantes se toman como una colección de caracteres que no deben estar presentes en la posición dada. Podemos hacer esto modificando nuestro ejemplo anterior:

[me@linuxbox ~]$ grep -h '[^bg]zip' dirlist*.txt
bunzip2
gunzip
funzip
gpg-zip
preunzip
prezip
prezip-bin
unzip
unzipsfx

Con la negación activada, obtenemos una lista de archivos que contienen la cadena "zip" precedida de cualquier carácter excepto "b" o "g". Fíjate que el archivo zip no ha sido encontrado. Una configuración de negación de carácter requiere todavía un carácter en la posición dada, pero el carácter no debe ser un miembro del conjunto negado.

El símbolo de intercalación sólo implica negación si es el primer carácter dentro de una expresión entre corchetes; de otra forma, pierde su significado especial y pasa a ser un carácter ordinario en la colección de caracteres.

miércoles, 15 de junio de 2016

Expresiones entre corchetes y clases de caracteres

Además de encontrar un carácter en una determinada posición en nuestra expresión regular, podemos también encontrar un único carácter de una colección específica de caracteres usando expresiones entre corchetes. Con las expresiones entre corchetes, podemos especificar una colección de caracteres (incluyendo caracteres que en otro caso serían interpretados como metacaracteres) para que sean encontrados. En este ejemplo, usamos una colección de dos caracteres:

[me@linuxbox ~]$ grep -h '[bg]zip' dirlist*.txt
bzip2
bzip2recover
gzip

encontramos cualquier línea que contenga la cadena "bzip" o "gzip".

Una colección puede contener cualquier número de caracteres, y los metacaracteres perder su significado especial cuando se coloquen entre corchetes. Sin embargo, hay dos casos en que los metacaracteres se usan dentro de las expresiones entre corchetes, y tienen significados diferentes. El primero es el símbolo de intercalación (^), que se usa para indicar negación; el segundo es el guión (-), que se usa para indicar un rango de caracteres.

martes, 14 de junio de 2016

Un ayudante de crucigramas

Incluso con nuestro limitado conocimiento de las expresiones regulares en este momento, podemos hacer algo útil.

A mi esposa le encantan los crucigramas y a veces me pide ayuda con alguna pregunta concreta. Algo como, "¿Qué palabra de cinco letras cuya tercera letra es "j" y la ultima letra es "r" significa...?" Este tipo de preguntas me hacen pensar. ¿Sabes que tu sistema Linux tiene un diccionario? Lo tiene. Echa un vistazo al directorio /usr/share/dict y encontrarás uno, o varios. Los archivos de diccionario localizados allí son sólo largas listas de palabras, una por línea, ordenadas alfabéticamente. En mi sistema, el archivo words contiene alrededor de 98.500 palabras.
Para encontrar posibles respuestas a la pregunta del crucigrama anterior, podríamos hacer ésto:

[me@linuxbox ~]$ grep -i '^..j.r$' /usr/share/dict/words
Major
major

Usando la expresión regular, podemos encontrar todas las palabras del archivo de diccionario que son de cinco letras y tienen una "j" en la tercera posición y una "r" en la última posición.

lunes, 13 de junio de 2016

Anclas

El símbolo de intercalación (^) y el signo del dolar ($) se tratan como anclas en la expresiones regulares. Esto significa que hacen que la coincidencia ocurra sólo si la expresión regular se encuentra al principio de la línea (^) o al final de la línea ($):

[me@linuxbox ~]$ grep -h '^zip' dirlist*.txt
zip
zipcloak
zipgrep
zipinfo
zipnote
zipsplit
[me@linuxbox ~]$ grep -h 'zip$' dirlist*.txt
gunzip
gzip
funzip
gpg-zip
preunzip
prezip
unzip
zip
[me@linuxbox ~]$ grep -h '^zip$' dirlist*.txt
zip

Aquí hemos buscado en la lista de archivos si la cadena "zip" se encuentra al principio de línea, al final de la línea o en una línea donde está tanto al principio como al final de la línea (es decir, que él mismo sea la línea). Fíjate que la expresión ‘^$’ (un principio y un final con nada en medio) encontrará líneas en blanco.

viernes, 10 de junio de 2016

El carácter cualquiera

El primer metacarácter que veremos es el punto, que se usa para buscar cualquier carácter. Si lo incluimos en una expresión regular, encontrará cualquier carácter en esa posición. Aquí tenemos un ejemplo:

[me@linuxbox ~]$ grep -h '.zip' dirlist*.txt
bunzip2
bzip2
bzip2recover
gunzip
gzip
funzip
gpg-zip
preunzip
prezip
prezip-bin
unzip
unzipsfx

Hemos buscado cualquier linea en nuestro archivo que coincida con la expresión regular ".zip". Hay un par de cosas interesantes a tener en cuenta en los resultados. Fíjate que el programa zip no ha sido encontrado. Es porque la inclusión del metacarácter punto en nuestra expresión regular incrementa la longitud de la coincidencia requerida a cuatro caracteres, y como el nombre "zip" sólo contiene tres, no coincide. Además, si algún archivo en nuestra lista contiene la extensión .zip, también debería haber coincidido, porque el punto en la extensión del archivo también es tratado como "cualquier carácter".

jueves, 9 de junio de 2016

Metacaracteres y literales

Aunque puede no parecer evidente, nuestras búsquedas con grep han usado expresiones regulares todo el tiempo, aunque muy simples. La expresión regular "bzip" significa que una coincidencia ocurrirá sólo si la línea del archivo contiene al menos cuatro caracteres y que en algún lugar de la línea los caracteres "b", "z", "i" y "p" se encuentran en ese orden, sin otros caracteres en medio. Los caracteres en la cadena "bzip" son caracteres literales, en el sentido en que coinciden consigo mismos. Además de los literales, las expresiones regulares pueden también incluir metacaracteres que se usan para especificar coincidencias más complejas. Los metacaracteres para las expresiones regulares son los siguientes:

^ $ . [ ] { } - ? * + ( ) | \

Todos los demás caracteres se consideran literales, aunque la barra invertida se utiliza en algunos casos para crear meta secuencias, así como para que los metacaracteres puedan ser "escapados" y tratados como literales en lugar de ser interpretados como metacaracteres.

Nota: Como podemos ver, muchos metacaracteres de expresiones regulares son también caracteres que tienen significado para el shell cuando se realiza la expansión. Cuando pasamos expresiones regulares que contienen metacaracteres en la línea de comandos, es vital que estén entrecomillados para prevenir que el shell trate de expandirlos.

miércoles, 8 de junio de 2016

grep

El principal programa que usaremos para trabajar con expresiones regulares es nuestro antiguo colega, grep. El nombre "grep" deriva realmente de la frase "global regular expresión print - búsqueda global e impresión de expresiones regulares (del inglés: globally search a regular expression and print)", luego podemos ver que grep tiene algo que ver con las expresiones regulares. En esencia, grep busca en archivos de texto la coincidencia con una expresión regular especificada e imprime, en la salida estándar, líneas que contengan una coincidencia.

Hasta ahora, hemos usado grep con cadenas concretas, así:

[me@linuxbox ~]$ ls /usr/bin | grep zip

Esto listará todos los archivos en el directorio /usr/bin cuyos nombres contengan la cadena "zip".

El programa grep acepta opciones y argumentos de esta forma:

grep [opciones] regex [archivo...]

donde regex es una expresión regular.

Aquí tenemos una lista de las opciones más comunes usadas en grep:

Tabla 20-1: Opciones de grep
Opción Descripción
-i Ignora las mayúsculas. No distingue entre caracteres mayúscula y minúscula. También puede indicarse como --ignore-case.
-v Coincidencia inversa. Normalmente, grep muestra líneas que contienen un patrón. Esta opción hace que grep muestre todas las líneas que no contengan un patrón. También puede indicarse como --invert-match.
-c Muestra el número de coincidencias (o no-coincidencias si también se especifica la opción -v) en lugar de las propias líneas. También puede indicarse como --count.
-l Muestra el nombre de cada archivo que contenga un patrón en lugar de las propias líneas. También puede indicarse como --files-with-matches.
-L Como la opción -l, pero muestra sólo los nombres de los archivos que no contengan el patrón. También puede indicarse como --files-without-match.
-n Precede cada línea coincidente con el número de la línea dentro del archivo. También puede indicarse como --line-number.
-h Para búsquedas multi-archivo, suprime los nombres de archivo de la salida. También puede indicarse como --no-filename.

Para explorar más a fondo grep, creemos algunos archivos de texto para buscar:

[me@linuxbox ~]$ ls /bin > dirlist-bin.txt
[me@linuxbox ~]$ ls /usr/bin > dirlist-usr-bin.txt
[me@linuxbox ~]$ ls /sbin > dirlist-sbin.txt
[me@linuxbox ~]$ ls /usr/sbin > dirlist-usr-sbin.txt
[me@linuxbox ~]$ ls dirlist*.txt
dirlist-bin.txt dirlist-sbin.txt dirlist-usr-sbin.txt
dirlist-usr-bin.txt

Podemos realizar una búsqueda simple de nuestra lista de archivos así:

[me@linuxbox ~]$ grep bzip dirlist*.txt
dirlist-bin.txt:bzip2
dirlist-bin.txt:bzip2recover

En este ejemplo, grep busca en todos los archivos listados la cadena bzip y encuentra dos coincidencias, ambas en el archivo dirlist-bin.txt. Si sólo estuviéramos interesados en la lista de archivos que contienen las coincidencias, podríamos especificar la opción -l:

[me@linuxbox ~]$ grep -l bzip dirlist*.txt
dirlist-bin.txt

Por el contrario, si sólo quisiéramos ver una lista de los archivos que no contienen una coincidencia, podríamos hacer esto:

[me@linuxbox ~]$ grep -L bzip dirlist*.txt
dirlist-sbin.txt
dirlist-usr-bin.txt
dirlist-usr-sbin.txt

martes, 7 de junio de 2016

¿Qué son las expresiones regulares?

En pocas palabras, las expresiones regulares son notaciones simbólicas usadas para identificar patrones en el texto. En muchos aspectos, se parecen al método de los comodines del shell para encontrar archivos y rutas, pero en una escala mucho más grande. Las expresiones regulares están soportadas por muchas herramientas de la línea de comandos y por la mayoría de los lenguajes de programación para facilitar la solución de problemas de manipulación de texto. Sin embargo, para confundir más las cosas, no todas las expresiones regulares son iguales; varían un poco de herramienta a herramienta y de lenguaje de programación a lenguaje de programación. Para nuestro tema, nos limitaremos a expresiones regulares como se describen en el estándar POSIX (que cubrirá la mayoría de las herramientas de la línea de comandos), al contrario que muchos lenguajes de programación (principalmente Perl), que usan un catálogo algo más grande y rico de notaciones.

lunes, 6 de junio de 2016

Expresiones regulares

En los próximos capítulos, vamos a ver herramientas que se usan para manipular texto. Como hemos visto, los datos de texto juegan un papel importante en todos los sistemas tipo Unix, por ejemplo Linux. Pero antes de poder apreciar al completo todas las funciones ofrecidas por estas herramientas, tenemos que examinar primero una tecnología que está frecuentemente asociada a los usos más sofisticados de estas herramientas: las expresiones regulares.

Según hemos ido navegando por las muchas funciones y soluciones ofrecidas por la línea de comandos, hemos encontrado algunas funciones y comandos verdaderamente antiguos, como la expansión del shell y el entrecomillado, los atajos de teclado y el historial de comandos, no hace falta mencionar al editor vi. Las expresiones regulares continúan esta "tradición" y pueden ser (posiblemente) la función más arcana de todas ellas. Esto no quiere decir que el tiempo que nos lleve aprender sobre ellas no merezcan el esfuerzo. Justo lo contrario. Un buen entendimiento nos permitirá usarlas para realizar cosas impresionantes, aunque su verdadero valor no sea evidente inicialmente.

viernes, 3 de junio de 2016

Para saber más

  • Las man pages de todos los comandos vistos aquí son muy claras y contienen ejemplos útiles. Además, el proyecto GNU tiene un buen manual online para su versión de tar. Puede encontrarse aquí:
    http://www.gnu.org/software/tar/manual/index.html

jueves, 2 de junio de 2016

Resumiendo

Hemos visto los programas más comunes de compresión y empaquetado en Linux y otros sistemas operativos tipo Unix. Para empaquetar archivos, la combinación tar/gzip es el método preferido en sistemas tipo Unix mientras que zip/unzip se usa para interoperar con sistemas Windows. Finalmente, hemos visto el programa rsync (uno de mis favoritos) que es muy útil para sincronizar eficientemente archivos y directorios entre sistemas.

miércoles, 1 de junio de 2016

Usando rsync en una red

Uno de los verdaderos encantos de rsync es que puede usarse para copiar archivos a través de una red. Después de todo, la "r" de rsync significa "remoto". La copia remota puede hacerse de dos formas. La primera es con otro sistema que tenga rsync instalado, junto con un programa de shell remoto como ssh. Digamos que tenemos otro sistema en nuestra red local con mucho espacio disponible en el disco duro y queremos realizar una operación de copia de seguridad usando el sistema remoto en lugar de un disco externo. Asumiendo que ya hay un directorio llamado /backup donde podríamos depositar nuestros archivos, podríamos hacer ésto:

[me@linuxbox ~]$ sudo rsync -av --delete --rsh=ssh /etc /home /usr/local remote-sys:/backup

Hemos hecho dos cambios en nuestro comando para facilitar el copiado a través de la red. Primero, hemos añadido la opción --rsh=ssh, que le dice a rsync que use el programa ssh como su shell remoto. De esta forma, podemos usar un tunel ssh encriptado para transferir datos con seguridad desde el sistema local hasta el host remoto. Segundo, hemos especificado el host remoto añadiendo su nombre (en este caso el host remoto se llama remote-sys) como prefijo de la ruta de destino.

La segundo forma en que podemos usar rsync para sincronizar archivos a través de una red es usando un servidor rsync. rsync puede configurarse para funcionar como un demonio y escuchar solicitudes remotas de sincronización. Ésto se hace a menudo para realizar copias espejo de un sistema remoto. Por ejemplo, Red Hat Software mantiene un gran repositorio de paquetes de software en desarrollo para su distribución Fedora. Es útil para testeadores de software replicar esta colección durante la fase de testeo del ciclo de publicación de la distribución. Como los archivos en el repositorio cambian con frecuencia (a menudo más de una vez al día), es recomendable mantener una copia espejo local con sincronización periódica, en lugar de copiar todo el repositorio. Uno de esos repositorios está alojado en Georgia Tech; podríamos clonarlo usando nuestra copia local de rsync y su servidor rsync así:

[me@linuxbox ~]$ mkdir fedora-devel
[me@linuxbox ~]$ rsync -av -delete rsync://rsync.gtlib.gatech.edu/fedora-linux-core/development/i386/os fedora-devel

En este ejemplo, hemos usado la URI del servidor rsync remoto, que consiste en un protocolo (rsync://), seguido del nombre de host remoto (rsync.gtlib.gatech.edu), seguido de la ruta del repositorio.