Oct 022007
 

La expansión de llaves

Lo primero que hace bash al analizar una orden es separarla en palabras, luego comprueba si alguna de las palabras debe ser expandida. Hay ciertas expresiones que son expandidas cuando bash las analiza para producir un resultado distinto a lo que hemos escrito. Lo primero que analiza bash es si alguna palabra contiene expresiones entre llaves ‘{}’. Te gustará este tipo de expansión porque sirve para escribir menos. Básicamente la expansión de llaves convierte una palabra en tantas como expresiones pongamos entre las llaves separadas por comas.


echo {1,2,3,4,5,6}
1 2 3 4 5 6

Visto así no parece muy práctico ¿no? :) bueno déjame continuar… Antes y después de las llaves puedes poner un prefijo y/o un sufijo, pero siempre formando una única palabra, en ese caso se combinará prefijo y sufijo, con cada uno de los elementos.

echo cant{o,as,a,amos,áis,an}
canto cantas canta cantamos cantáis cantan
echo {lenta,rápida,inmediata}mente
lentamente rápidamente inmediatamente
echo co{mun,tidiana}mente
comunmente cotidianamente

Cuando tengas soltura en bash, te verás escribiendo cosas como: ‘convert fichero.{jpg,png}’ en lugar de ‘convert fichero.jpg fichero.png’

Algunos elementos de la expansión pueden estar vacíos:

echo esta{,ba,tatica}
esta estaba estatatica

Si en una palabra aparecen varias llaves se expanden sucesivamente. Por ejemplo echo prueba{1,2}{a,b} primero se expandiría la primera llave quedando dos palabras 'prueba1{a,b} prueba2{a,b}' Como cada una de las palabras generadas sigue teniendo una llave se volverían a expandir, dándo como resultado cuatro palabras: 'prueba1a prueba1b prueba2a y prueba2b'

También se pueden anidar unas llaves dentro de otras:

$ echo hola{1{a,b,c},2,3{d,e}}
hola1a hola1b hola1c hola2 hola3d hola3e

A partir de la versión 3 de bash (La que tendréis la mayoría), también se pueden poner intervalos numéricos en la expansión de llaves. {3..9} es equivalente a {3,4,5,6,7,8,9}. {0..999} generará mil palabras 0 1 2 … 998 999. En cambio si queremos generar 000 001 002 … 998 999 tendremos que poner {0..9}{0..9}{0..9}

La expansión solo funciona a nivel de palabras. Y nunca dentro de comillas (ni simples ni dobles). Si queremos incluir espacios en la expansión hay que evitar que dividan la palabra con el caracter de escape o con comillas pero sin que estas afecten a las llaves.

$ echo hola {Juan,Carlos,Jose Manuel}
hola {Juan,Carlos,Jose Manuel}
$ echo "hola {Juan,Carlos,Jose Manuel}"
hola {Juan,Carlos,Jose Manuel}
$ echo hola {Juan,Carlos,"Jose Manuel"}
hola Juan Carlos Jose Manuel
$ echo hola\ {Juan,Carlos,Jose\ Manuel}
hola Juan hola Carlos hola Jose Manuel
$ echo "hola "{Juan,Carlos,"Jose Manuel"}
hola Juan hola Carlos hola Jose Manuel

En el primer caso no se expande nada porque la llave de abrir y la de cerrar están en palabras distintas. En el segundo caso están en la misma palabra pero tampoco se expande porque dentro de unas comillas nunca se raliza la expansión de llaves. En el tercer caso se hace la expansión pero solo de las llaves ya que hay un espacio que separa la palabra ‘hola’.
En los dos últimos utilizamos el escape y las comillas para hacer la expansión correctamente.

La expansión de nombres de ruta. Patrones, comodines…

Después de la expansión de llaves y siempre antes de ejecutar una orden el bash comprueba si alguna palabra contiene algún carácter de estos: ‘*’, ‘?’ y ‘[‘ y si es así la considera como un patrón. Un patrón es una palabra que se expande a todos los nombres de ruta que concuerden con él antes de ejecutar la orden. A los caracteres que tienen un significado especial dentro de un patrón les llamamos caracteres comodín

Al igual que pasaba con la expansión de llaves, dentro de un par de comillas, ya sean simples o dobles jamás se hace esta sustitución, así que si queremos usar alguno los caracteres comodín literalmente, sin que se produzca la expansión podemos bien encerrarlo entre comillas o bien usar el caracter de escape.

Cuando una palabra contiene un comodín bash busca en nuestro sistema de ficheros todos los nombres de ruta que concuerdan con esa palabra patrón. Si encuentra concordancias la palabra es sustituida por todos los nombres que concuerden. Esto quiere decir que una palabra puede convertirse en varias antes de ejecutar la orden. Si ningún nombre concuerda se deja la palabra tal cual estaba y el caracter o caracteres comodín pasan a considerarse caracteres completamente normales.

Pero te estarás preguntando ¿que es eso de concordar? ¿Como funcionan exactamente los comodines?. El caracter ‘?’ en un patrón significa cualquier caracter. Así que si ponemos en una órden la palabra ‘/usr/bin/?c’, por ejemplo, se buscarían todos los nombres de ruta que comiencen con ‘/usr/bin/’ luego tengan un caracter cualquiera y finalmente una ‘c’, que seguramente habrá varios, y se sustituirá la palabra que contenía el comodín por todos los nombres de ruta que se han encontrado. Personalmente me sale:


echo /usr/bin/?c
/usr/bin/bc /usr/bin/cc /usr/bin/dc /usr/bin/gc /usr/bin/mc /usr/bin/nc /usr/bin/wc

Se puede usar varias veces un comodín (o varios) en la misma palabra, por ejemplo:


echo /usr/bin/?a?
/usr/bin/cal /usr/bin/dat /usr/bin/fax /usr/bin/mag /usr/bin/man /usr/bin/pan /usr/bin/rar /usr/bin/raw /usr/bin/tac

Evidentemente en tu ordenador puedes obtener resultados distintos, porque no todos tendréis una instalación exactamente igual que la mía.

El comodín ‘*’ en un patrón significa ninguno, o más caracteres cualquiera. Es decir concordará con cualquier secuencia de caracteres de cualquier longitud, incluso con ningún caracter. Ejemplos:

$ echo /usr/bin/col*
/usr/bin/col /usr/bin/colcrt /usr/bin/colormap /usr/bin/colrm /usr/bin/column
$ ls *.jpg
$ du -c f-*.jpg

Los que estáis familiarizados con el interprete de comandos de MS/DOS o Windows, debeis tener mucho ojo con la expansión de nombres, porque puede resultar muy parecido a como el DOS trata sus patrones (formados por los caracteres llamados comodines * y ?), pero es sustancialmente distinto, para comparar os pondré un ejemplo, si en MS/DOS o Windows ponemos en la línea de comando:

EDIT *.TXT

El command.com (o el cmd.exe) no hace nada, simplemente llama al programa edit y le pasa como argumento ‘*.TXT’ es EDIT quien tras analizar el argumento entiende que es un patrón que puede designar múltiples ficheros y lo expande. Hay comandos que nunca expanden los nombres de fichero y otros que sí…

En bash, (y en las shells de UNIX en general), es la shell la que hace la expansión, y si escribo por ejemplo:

$ gimp *.jpg

La shell encuentra el *, sabe que designa un patrón y busca todos los ficheros que concuerden con ese patrón, supongamos que hay tres y que son: foto1.jpg, foto2.jpg y foto3.jpg. La orden se convierte, pues, antes de ser ejecutada en:

$ gimp foto1.jpg foto2.jpg foto3.jpg

Y el gimp recibirá tres argumentos y no uno como el EDIT en el ejemplo anterior. Esto que parece trivial hay que tenerlo muy claro, y suele desembocar (entre otros) en un error que muchos novatos en Linux que veníamos de DOS habremos cometido alguna vez, el error de usar el cp o el mv sin destino. Como anécdota voy a explicarlo ya que además de servirnos para ilustrar como funciona la expansión de nombres, te puede evitar algún disgusto:

Para los que hicimos nuestros ‘pinitos’ en DOS antes de pasarnos a Linux, conocemos perfectamente como funcionan los comandos COPY y MOVE del DOS. Copy (en DOS) copia uno o varios ficheros a un destino. Necesita normalmente dos parámetros, origen y destino, el origen puede ser un directorio, un fichero o un patrón que concuerde con varios ficheros. El destino puede ser otro fichero, o un directorio; pero puede omitirse. Si se omite se tomará como destino el directorio actual.

Supongamos que estamos en MS/DOS (o Windows) y el directorio actual es ‘\Mis Documentos’ en la unidad C y en la unidad A tenemos varios fotos (.jpg) que queremos copiar a nuestro directorio actual, es suficiente con que tecleemos:
Copy a:\*.jpg

Veamos el mismo caso en Linux y concretamente con bash:

El comando para copiar es cp, cp siempre espera al menos dos argumentos: un origen y un destino. Si recibe más de dos argumentos, entiende que todos son orígenes excepto el último que es el destino, si hay más de un fichero origen, el destino debe ser un directorio. El comando cp no entiende de comodines ni de patrones es la shell quien tiene que darle todo expandido.

Ignorantes de estas premisas y acostumbrados a MS/DOS, si en Linux queremos hacer lo mismo del ejemplo anterior, es decir, nos encontramos en nuestro directorio home y queremos copiar a él todos los ficheros .jpg que hay en un diskette, es necesario poner origen y destino. Por desgracia, creyendo que bash es como el command.com, traducimos el comando copy a:\*.jpg
por lo que nos parece lo más equivalente que es:

$ cp /mnt/floppy/*.jpg

¿Que puede ocurrir? Si tenemos la suerte de que en el disquete hay más de dos ficheros .jpg, el bash expande los nombres de fichero en orden como:
$ cp /mnt/floppy/foto1.jpg /mnt/floppy/foto2.jpg /mnt/floppy/foto3.jpg

El comando cp (al menos en las versiones modernas del cp que suele venir con Linux) ve que hay más de dos parámetros, y como el último no es un directorio, nos da un error diciendo que no se puede copiar varios ficheros sobre uno solo, y que el destino tiene que ser un directorio. (En otros UNIX o en versiones muy antiguas de cp, puede que ni siquiera nos de el error y copie los dos primeros ficheros sobre el último).

Pero si tenemos la desgracia de que el disquete solo tiene dos ficheros .jpg (fatal coincidencia), el comando cp después de que bash expanda los nombres de fichero recibirá dos parámetros como si hubiésemos tecelado:
$ cp /mnt/floppy/foto1.jpg /mnt/floppy/foto2.jpg

Y como eso es perfectamente válido copia la foto1 sobre la foto2, machacando el contenido de la segunda. Así que mucho ojo, que esto no es MS/DOS. El directorio de trabajo actual se designa (tal como vimos cuando hablábamos de los nombres de ruta) por un punto, así que lo que debieramos haber hecho es:

$ cp /mnt/floppy/*.jpg .

Así nos evitaríamos algún que otro disgusto. Otra diferencia sustancial es que por para DOS/Windows el patrón ‘*.*’ designaba a todos los ficheros del directorio de trabajo. En bash ‘*.*’ solamente designa a aquellos fichero que contienen un punto en su nombre para designar a todos debemos emplear simplemente ‘*’

Hay otro comodín que no tiene equivalente en DOS que es una serie de caracteres entre corchetes ‘[…]‘ (paréntesis cuadrados), y que concuerda con uno cualquiera de dichos caracteres, por ejemplo [afj]* concordará con cualquier fichero que comience por las letras a, f ó j. En vez de los caracteres uno a uno también se puede especificar un rango indicando el carácter inicial y el final separados por un guión como [a-z] o [0-9] que concordarán con cualquier letra minúscula o cualquier número respectivamente. Si queremos que el propio guión ‘-’ sea uno de los caracteres de la serie debemos ponerlo al principio o al final, ya que si va entre dos caracteres delimitará un rango. Si queremos que el propio ‘]’ que se usa para marcar el final del conjunto forme parte de la expresión hay que ponerlo al principio de todo.

Ejemplos de patrones con este comodin:

  • [a-fA-F]* cualquier fichero que comience por una letra desde la a hasta la F bien sea en mayúsculas o minúsculas.
  • *[0-9]* cualquier fichero que contenga un número.
  • *[0-9].txt cualquier fichero cuyo nombre termine en un número seguido de un punto y las letras ‘txt’
  • ??[1-9][0-9]* Cualquier fichero que comience por dos caracteres cualesquiera, seguidos de dos números (y que el primero no sea un cero), y cualquier serie de caracteres después.
  • *[0-36-9]* Cualquier fichero que tenga un número del 0 al 3 o del 6 al 9 en su nombre.
  • *.[jJ][pP][gG] Cualquier fichero que termine en .jpg ya sea en mayúsculas, minúsculas o cualquier combinación de mayúsculas y/o minúsculas.

Si el rango, o la serie de caracteres empieza por ^ o por ! se entenderá que es cualquier
caracter fuera de ese rango o serie: Si queremos poner cualquiera de esos dos caracteres en una serie, los pondremos en una posición que no sea la primera

  • [^0-9]* Cualquier fichero excepto los que empiezan por un número.
  • [^a-zA-Z]* Cualquier fichero que no comience por una letra.

Hay que hacer notar que cuando empleamos rangos, se tiene en cuenta la ordenación alfabética de nuestra configuración regional. Esto es realmente importante porque en casi todos los lenguajes la ordenación alfabética no tiene en cuenta mayúsculas y minúsculas. Quiere esto decir que si ponemos, [a-z] por ejemplo y dependiendo de la configuración regional que tengamos puede coincidir con las mayúsculas y las minúsculas, o incluír la ‘ñ’ o las vocales acentuadas. Ya que en la configuración regional van alfabéticamente en ese rango. Para que la comparación de rangos sea estricta, y no tenga en cuenta la ordenación regional sino el órden de caracteres del código ASCCI se debe establecer la variable de entorno LC_COLLATE=C. Como las variables, el entorno y esas zarandajas las veremos más adelante, sólo os indico como hacerlo, no os preocupéis si no lo entendéis de momento. Para consultar de acuerdo a que reglas se está ordenando, pondremos poner:

$ echo $LC_COLLATE
es_ES

En mi caso me indica que estoy usando el método de ordenación alfabética de es_ES que es español de España. Podría ser es_AR si estás en argentina, o es_MX para Mexico, o en_US para English – USA, o fr_FR para Francia o fr_BE para francés de Bélgica, fr_CA para francés de Candá, o en_CA para ingles de Canadá. Nuestros idiomas autonómicos se codifican normalmente como ‘gl’ para gallego ‘ca’ para catalan o ‘eu’ para euskera, sin variantes de país aunque es posible que en alguna distribución aparezcan como gl_ES, ca_ES, o eu_ES, lo cual no me parece muy correcto. Estos son unos códigos definidos por el estándar POSIX, que dice que: ‘Los códigos de idioma y país se deben definir juntos. El idioma sigue la norma ISO 639-1. Los códigos de países están definidos en la norma ISO 3166‘. El código de lenguaje ‘C’ así sin más indica que se seguirá el orden numérico del código ASCII (Bueno en realidad del código de caracteres que estemos usando, normalmente en una distribución linux moderna será el UTF-8). Para cambiar el código de lenguaje utilizado para la ordenación se usará el siguiente comando, poniendo después del igual el código que nos interese que se utilice para las reglas de ordenación. Este cambio tendrá efecto durante toda esa sesión de bash, o hasta que lo cambiemos de nuevo:

$ export LC_COLLATE=C

Vamos a experimentar un poco todo esto. Primero crearemos un directorio para hacer pruebas, luego lo poblaremos con unos cuantos ficheros (vacíos, que para el caso es lo mismo), y sobre ellos usaremos distintos patrones para que entiendas como funciona el asunto.


$ cd ~
$ mkdir ejercicios_curso_bash
$ cd ejercicios_curso_bash
$ touch fichero{1,8,9,0,_,a,c,C,F,Ñ,ñ}.txt
$ touch {123,345,685}{b,j,A,BcD,JJ}.{dat,dit,zip,jpg,Jpg,JPEG}
$ touch {A,B,C,a,b,f,á,é,í,ñ,Ñ}{ABC,abc,def}
$ touch {A,á,Z,z,Ñ.Ñ}' '{ABC,abc,def}
 
$ echo 1???.dat
123A.dat 123b.dat 123j.dat
$ echo 1*.dat
123A.dat 123BcD.dat 123b.dat 123j.dat 123JJ.dat
$ echo 1*.d?t
123A.dat 123A.dit 123BcD.dat 123BcD.dit 123b.dat 123b.dit 123j.dat 123j.dit 123JJ.dat 123JJ.dit
$ echo 1*
123A.dat 123A.dit 123A.JPEG 123A.jpg 123A.Jpg 123A.zip 123BcD.dat 123BcD.dit 123BcD.JPEG 123BcD.jpg 123BcD.Jpg 123BcD.zip 123b.dat 123b.dit 123b.JPEG 123b.jpg 123b.Jpg 123b.zip 123j.dat 123j.dit 123JJ.dat 123JJ.dit 123JJ.JPEG 123JJ.jpg 123J
J.Jpg 123j.JPEG 123j.jpg 123j.Jpg 123JJ.zip 123j.zip

$ echo f*
fabc fABC fdef fichero0.txt fichero1.txt fichero8.txt fichero9.txt ficheroa.txt ficheroc.txt ficheroC.txt ficheroF.txt fichero±.txt ficheroÐ.txt fichero_.txt
$ echo f*.*
fichero0.txt fichero1.txt fichero8.txt fichero9.txt ficheroa.txt ficheroc.txt ficheroC.txt ficheroF.txt fichero±.txt ficheroÐ.txt fichero_.txt
$ echo *.jpg
123A.jpg 123BcD.jpg 123b.jpg 123JJ.jpg 123j.jpg 345A.jpg 345BcD.jpg 345b.jpg 345JJ.jpg 345j.jpg 685A.jpg 685BcD.jpg 685b.jpg 685JJ.jpg 685j.jpg
$ echo 3*.[jJ]pg
345A.jpg 345A.Jpg 345BcD.jpg 345BcD.Jpg 345b.jpg 345b.Jpg 345JJ.jpg 345JJ.Jpg 345j.jpg 345j.Jpg
$ echo 3*.[jJ][pP]*[gG]
345A.JPEG 345A.jpg 345A.Jpg 345BcD.JPEG 345BcD.jpg 345BcD.Jpg 345b.JPEG 345b.jpg 345b.Jpg 345JJ.JPEG 345JJ.jpg 345JJ.Jpg 345j.JPEG 345j.jpg 345j.Jpg
$ export LC_COLLATE=C
$ echo fichero[a-z].txt
ficheroa.txt ficheroc.txt
$ echo fichero[A-Z].txt
ficheroC.txt ficheroF.txt
$ export LC_COLLATE=es_ES
$ echo fichero[a-z].txt
ficheroa.txt ficheroc.txt ficheroC.txt ficheroF.txt ficheroñ.txt ficheroÑ.txt
$ echo fichero[a-f].txt
ficheroa.txt ficheroc.txt ficheroC.txt
$ echo fichero[A-Z].txt
ficheroc.txt ficheroC.txt ficheroF.txt ficheroñ.txt ficheroÑ.txt

Con el método de ordenación, establecido como ‘C’ en el rango [a-z] sólo coincidirán las letras minúsculas del código ascii (descartando eñes, vocales acentuadas, etc), en cambio con el método de ordenación es_ES, parece que obtendremos coincidencias en todas las letras mayúsculas y minúsculas del alfabeto español. Si ponemos el rango en mayúsculas [A-Z] con el LC_COLLATE=C coincidirá con todas las letras mayúsculas del código ASCII, como era de esperar. En cambio con es_ES no tamos que no sale la ‘a’ minúscula. Esto tiene una sencilla explicación: Como hay que darle un órden a todos los caracteres, se decidió que primero irían las letras minúsculas, luego las mayusculas, luego (en el caso de las vocales) las acentuadas, primero en minúscula y luego en mayúscula, Más o menos así: aAábBcC…nNñÑ…uUúü…zZ Visto así está claro que en el rango [a-z] nos dejamos fuera la ‘Z’ mayúscula y en el rango A-Z nos dejamos fuera la ‘a’ minúscula. O en el rango [a-f], como podemos apreciar en el ejemplo, la ‘F’ mayúscula queda fuera. Parece algo trivial pero hay que tenerlo en cuenta si no queremos sorpresas trabajando con rangos

Además de listas de caracteres y rangos, bash reconoce los siguientes nombres especiales de clase, que irán siempre entre otros corchetes (además de los corchetes de rango) y símbolos de dos puntos.

  • [:alpha:] Coincide con cualquier letra.
  • [:digit:] Coincide con cualquier dígito numérico decimal.
  • [:xdigit:] Coincide con cualquier digito hexadecimal, es decir los numeros 0-9 y las letras A-F que pueden ser mayúsculas o minúsculas.
  • [:alnum:] Coincide con cualquier letra o número decimal.
  • [:punct:] Cualquier signo de puntuación.
  • [:lower:] Cualquier letra minúscula.
  • [:upper:] Cualquier letra mayúscula.
  • [:ascii:] Cualquier caracter del conjunto ascii de 7 Bits.
  • [:blank:] Cualquier caracter de espaciado, es decir los espacios y los tabuladores.
  • [:cntrl:] Cualquier caracter de control.
  • [:print:] Cualquier caracter imprimible incluyendo los espacios.
  • [:graph:] Cualquier caracter imprimible sin incluir los espacios.

Como los rangos, estas clases dependen del LC_COLLATE que tengamos seleccionado. Por ejemplo con es_ES, la letra Ñ o las vocales acentuadas o la u con diéresis pertenecerán a la clase :alpha:, en cambio con fr_FR la Ñ no estará en la clase :alpha: pero si estará la C con cedilla ‘Ç’ o las vocales con acento grave o circunflejo.

Por ejemplo:


$ echo fichero[[:digit:]aC]*

Mostrará todos los ficheros que comiencen con la palabra fichero, y contengan bien un
número o las letras ‘a’ minúscula o ‘C’ mayúscula a continuación.

$ ls -1 *[[:blank:]]*

Mostrará todos los ficheros o directorios que tengan algun espacio en su nombre. Este lo pongo con el comando ls -1 para que salga cada uno en una línea porque si los pongo con un simple echo saldrían todos segudos y no se distinguiría cuales son los espacios dentro del nombre de los que separan unos nombres de otros.

Patrones extendidos

Además de los patrones normales existen los denominados patrones extendidos, pero normalmente no funcionarán porque suelen estar desactivados por defecto en bash. Los comodines normales, que acabamos de ver en detalle, aunque potentes, se quedaban algo escasos para algunas cosas, así que en la versión 2 de bash se implementó esta nueva expansión de nombres de ficheros. Pero para mantener compatibilidad hacia atrás, esta expansión solo funciona si se activa previamente, de no hacerlo así los nuevos caracteres podrían hacer que un script que funcionaba correctamente en bash 1.x tuviese un extraño comportamiento en el bash 2.x. Aclarado esto, para que funcionen los patrones extendidos de expansión de nombres de ficheros hay que activarlos previamente (en cada sesión) con la orden:

$ shopt -s extglob

Después de esa orden:

  • ?(patrón) concordará con una o ninguna ocurrencia del patrón dado. Por ejemplo texto?([0-9]).txt concordará con texto.txt, texto1.txt, texto3.txt, pero no con texto12.txt
  • *(patrón) concordará con ninguna o más ocurrencias del patrón dado. Por ejemplo texto*([0-9]).txt concordará con texto.txt, texto1.txt, texto3.txt, texto12.txt y texto12345.txt pero no con texto1a.txt
  • +(patrón) concordará con una o más ocurrencias del patrón dado. Por ejemplo texto+([0-9]).txt concuerda con texto1.txt, texto3.txt, texto12.txt y texto12345.txt pero no con texto.txt
  • @(patrón) concuerda solo con una ocurrencia del patrón. Por ejemplo texto@([0-9]).txt concuerda con texto1.txt, pero no con texto12.txt ni con texto.txt
  • !(patrón) Concuerda con cualquier cosa que no concuerde con el patrón. Por ejemplo texto!([0-9]).txt concordará con textoa.txt, textob.txt, texto19.txt o texto.txt, pero no con texto4.txt

En la evaluación de patrones extendidos, entre los paréntesis podemos poner un patrón simple o una lista de ellos separados por el caracter ‘|’, en caso de poner una lista coincidirá con cualquier cosa que coincida con cualquiera de los patrones de la lista.

Para volver a desactivar los patrones extendidos hay que usar

$ shopt -u extglob

¿Serías capaz de escribir un patrón que concordase con todos los ficheros que comenzaran con ‘img-’ seguidos de una serie de números, y después de un punto las extensiones jpg o jpeg teniendo en cuenta que tanto el img inicial como la extensión al final pueden aparecer en minúsculas, mayúsculas o cualquier combinación de mayúsculas y/o minúsculas? Puedes usar patrones extendidos.