Tutorial del Shell

Hablando con GNU/Linux a través del shell

El shell o intérprete de comandos, como antes habíamos dicho, es una interfase con nuestro sistema operativo. Gracias a él podremos dar las órdenes y mandatos necesarios para que nuestro sistema informático realice las tareas que necesitamos. No debemos confundir al intérprete de comandos con el sistema operativo. Este es solo un programa que hará de mediador entre nosotros y el kernel del sistema. El shell puede ser tanto gráfico (Ej. La interfase X-Window), como de texto (Ej. El bash).

En este capítulo trataremos el tema de shell al nivel de solo texto y en especial veremos un intérprete de comando que por su utilidad y su gran aceptación, es él mas usado tanto en Un*x como en GNU/Linux. Como veremos luego, el intérprete de comandos no solo tiene la labor de interpretar nuestros mandatos, también es un potente lenguaje de programación que nos será de gran utilidad a la hora de elaborar guiones (script) para poder automatizar nuestras tareas. Los usuarios de DOS estarán acostumbrados a la utilización de los archivos de procesamiento por lotes (.bat) o más bien denomina "batch".

Existen varios tipos de intérpretes de comandos en UNIX de los cuales los más famosos e importantes son el "Bourne Shell" (sh) y el "C Shell" (csh). El intérprete de comandos Bourne, usa una sintaxis de comandos usada en los primeros sistemas UNIX, como el System III. El nombre de intérprete Bourne en la mayoría de los Un*x es /bin/sh (sh por shell). El C Shell posee una sintaxis muy parecida al lenguaje de programación C y se lo encontrara como /bin/csh. El más usado en el mundo GNU/Linux, como antes dijimos, es el bash por "Bourne Again Shell y se lo encontrará en /bin/bash. Bash posee toda la funcionalidad del sh con características avanzadas de C Shell, por esto cualquier guión escrito para un intérprete de comandos sh correrá perfectamente en él. Si lo que se prefiere es el uso del intérprete de comandos basado en el lenguaje C, en GNU/Linux se podrá encontrar el Tcsh en /bin/tcsh, que es una versión extendida del C Shell. Los gustos de los usuarios son los que deciden que intérprete de comandos se usará, por esto es posible configurar para cada usuario un intérprete de comandos distintos y no afectar el funcionamiento de programas como el ls, cat o cp. Solo se vera afectada la funcionalidad de algún guión que esté preparado para ejecutar con un intérprete de comandos determinado, lo que veremos como se puede corregir al ver programación de script de shell.

Para más información respecto a cada uno de estos intérpretes de comandos se recomienda leer las páginas de manual de cada uno de ellos, lo que dará información muy detallada de estos.

Características

Comodines

Hablaremos de algunas característica de mucha utilidad y que harán mucho más fácil el trabajo, tanto en sh como en bash. Una de las características que poseen los intérpretes de comandos es el uso de comodines para reemplazar partes de archivos. Con esto, podría referirse a un archivo usando caracteres especiales como el "*" o "?". Supongamos que se desea listar todos los archivos que contengan la letra "ñ" en nuestro directorio. Para ello usaremos el comando ls

[shrek@pantano:~]$ ls *ñ*

Esto el intérprete de comandos lo vera como si quisiéramos listas todos los archivos que empezaran con ninguno, uno o varios caracteres, tuviesen una letra "ñ" y terminaran con ninguno, uno o varios caracteres.

[shrek@pantano:~]$ ls
hola  heart texto.form  manti.form
[shrek@pantano:~]$ ls h*
hola  heart

Como puede observarse también nos es útil si se sabe como comienza el archivo pero no como termina. Lo que nuestro intérprete de comando esta haciendo es sustituir el "*" con cualquier combinación posible que coincida con los archivos que tenemos en el directorio donde estamos buscando. Si se utiliza solamente el "*" con el comando ls, este interpretará que se esta buscando cualquier combinación posible y listará todo como si se estuviese tratando de ejecutar el comando ls solo

[shrek@pantano:~]$ ls *
hola  heart texto.form  manti.form

Este proceso es llamado "expansión de comodines" y lo efectúa el intérprete de comandos. Esto es muy importante ya que los comandos como el ls nunca ven el "*" en su lista de parámetros, es el intérprete de comandos quien expande los comodines para incluir todos los nombres de archivos que se adapten. Es decir que luego que se da la orden

[shrek@pantano:~]$ ls h*

es expandida para obtener

[shrek@pantano:~]$ ls hola heart

Otro comodín es el "?". Este carácter comodín solo expande un único carácter. Luego ls ? mostrará todos los nombres de archivos con un carácter de longitud. En cambio la orden ls hol? nos mostrará el archivo hola.

[shrek@pantano:~]$ ls ho?a
hola
[shrek@pantano:~]$ ls h????
heart

Estos caracteres comodín nos permiten referirnos a más de un archivo al mismo tiempo y en conjunto con los comandos ya aprendidos, podrán ser de gran utilidad para realizar nuestras tareas de forma más fácil.

Ejecución de comandos uno detrás del otro

Supongamos que se necesita ejecutar un comando e inmediatamente después otro. En la forma común lo que haríamos seria ejecutar el primer comando y una vez que este haya finalizado, ejecutaríamos el segundo. Existe una segunda forma de hacerlo y es utilizando el carácter ";" inmediatamente después del primer comando y a continuación poner el segundo comando. Con esto lograríamos que se ejecutara el primer comando y al terminar se ejecutará el segundo.

[shrek@pantano:~]$ ls
hola  heart texto.form  manti.form
[shrek@pantano:~]$ date
mié jul 14 15:15:25 ART 2004

Utilizando el carácter ";" haremos

[shrek@pantano:~]$ ls;date
hola  heart texto.form  manti.form
mié jul 14 15:15:25 ART 2004

Comillas

Este punto viene bien aclararlo antes de empezar a ver características de alias y script para no tener complicaciones y confusiones. En GNU/Linux existen tres tipos de comillas: las dobles ("), comillas sencillas (') y comillas inversas o tics inversos (`). Empezaremos con las comillas inversas. Estas indican al shell que tendrá que remplazar lo que esta encerrado entre ellas con su resultado. Supongamos que quisiéramos ver solamente el día de hoy. Entonces teclearíamos el comando

[shrek@pantano:~]$ date +%d
15

Ahora bien, cada vez que quisiéramos ver el día de hoy tendríamos que ingresar este comando nuevamente. Si le asignamos a una variable de entorno el resultado, solo tendríamos que mostrar esta variable.

[shrek@pantano:~]$ DIAHOY=`date +%d`
[shrek@pantano:~]$ echo $DIAHOY
15

Hay que tener en cuenta que para asignarle un valor a una variable tenemos que solo poner un nombre seguido del signo "=" y luego lo que queramos ponerle dentro. En cambio para mostrarla tendremos que anteponer el signo de dólar "$". Esto seria igual a poner

[shrek@pantano:~]$ DIAHOY=15

Lo único que el shell hace es expandir el comando para que solo se guarde el resultado de éste.

El otro tipo de comillas, la sencilla ('), le dice al sistema que no haga ninguna expansión.

[shrek@pantano:~]$ DIAHOY='date +%d'
[shrek@pantano:~]$ echo $DIAHOY
date +%d

Lo que se obtiene es exactamente lo que le asignamos a la variable ya que el shell no realizo ninguna expansión y lo único que asigno fue la cadena 'date +%d' a la variable DIAHOY.

El ultimo tipo de comillas son las dobles, este tiene casi la misma funcionalidad que las comillas simples pero con la salvedad de que lo que se incluya dentro de estas estará sujeto a expansión por parte del shell, siendo los caracteres con significado especial los tics inversos (`) el signo de dólar ($), la diagonal (\) y las mismas comillas dobles ("). Por ejemplo podríamos ingresar el siguiente comando

[shrek@pantano:~]$ echo "`date`"
Sat Apr 15 14:17:01 ART 2000

Alias

La utilización de alias nos da la capacidad de poder referenciar a un comando con otro nombre. La diferencia sustancial que podemos encontrar con los script de shell, es que los script para ejecutarse primero lanzaran a un subshell como un proceso hijo del shell que esta ejecutando en ese momento. En cambio un alias funciona en el proceso del shell siendo ejecutado en forma interna con la subsiguiente velocidad que esto otorga al no tener que crearse un nuevo proceso o de buscar en el disco rígido. Por supuesto que pueden usarse un conjunto de comandos para crear otro más complejos. Con los scripts se puede realizar lo mismo y lo veremos más adelante, pero para comandos simples y cortos no son necesarios. Los alias pertenecen a cada usuario, pudiendo éste configurarlos como más le convenga.

Supongamos que quisiéramos ver solo la hora cada vez que se lo pidamos al sistema y no quisiéramos poner el comando date con su modificador cada vez que lo queramos hacer. Lo que haremos será crear un "alias" al comando "date" con el modificador correspondiente. Para ello contamos con el comando alias, que seguido de argumentos nos permitirá crear uno, sin argumentos nos mostrará los alias que tenemos configurados.

[shrek@pantano:~]$ alias hora='date +%r'
[shrek@pantano:~]$ hora
02:45:04 PM

Ahora bien, esto funcionará mientras estemos en el sistema, ya que estos datos son cargados en memoria y al salir del sistema se perderán. Para que cada vez que entremos al sistema los alias sigan funcionando deberemos agregarlos a un archivo, que aparece en forma oculta en el directorio de cada usuario, denominado .bashrc o en caso de que se requiera que el alias funcione para todos los usuarios en el /etc/bashrc. Este archivo es leído por el /bin/bash cada vez que se entra al sistema. En él se tendrá que poner la línea alias en igual forma que lo haríamos en la interfaz de comandos.

Completado de línea

Se puede hacer que el shell complete la línea de comandos cuando se introduzca la primeras letras y se presione la tecla Tab. Esta propiedad también es útil para expandir la ruta a un directorio determinado. Supongamos que dentro del directorio /home existe un subdirectorio llamado /shrek

[shrek@pantano:~]$ cd /home/shr

Si presionamos la tecla Tab se completara la línea en /home/shrek. Si existiera más de un directorio que comenzará con "shr", el shell anunciaría con una señal audible, "bip", que existen más de una coincidencia, y al presionar nuevamente la tecla Tab mostraría una lista de todos los directorios que poseen "shr" al comienzo. Si dicha lista excediera la catidad de lineas a motrar en la consola, nos preguntará primero si es que queremos ver la cantidad "X" de posibilidades. Como ejemplo de esto podríamos citar el autocompletado de comandos. Si se oprime la tecla Tab sin haber introducido nada antes, el shell nos mostraría primero que existe un número "X" de posibilidades y al presionar nuevamente nos mostraría un listado de todos los comandos que son alcanzables.

PATH

Muchas veces ocurre que al tratar de introducir un comando nos damos cuenta de que no tiene efecto y nos da un error en él intérprete de comandos. Tal vez se haya ingresado mal, pero tal vez no se posea el directorio que contiene dicho comando en la "ruta de búsqueda" o PATH. EL PATH es una variable de entorno que contiene un grupo de directorios predefinidos en los cuales el shell buscará el comando o programa a ejecutar. Esto ahorra tiempo ya que el sistema no tendrá que buscar en todos los directorios el programa a ejecutar. Por esto el sistema, en caso de que el directorio no figure en el PATH, no podrá ejecutar el programa hasta que le demos la ruta exacta en donde se encuentre.

Esta variable de entorno llamada PATH, es inicializada con un valor predeterminado en el archivo /etc/profile que es sólo modificable por el "root" y funciona como una referencia para todos los usuarios. Además cada usuario posee en un archivo oculto denominado .bash_profile o .profile, donde se le asigna además del PATH inicial, cualquier otra modificación exclusiva para él. También puede modificarse totalmente esta variable.

Volviendo a la especificación de la ruta completa, anteriormente dijimos que los archivos se referenciaban de esta forma, pero los programas también podrán referenciarse de la misma forma. Si le damos la ruta completa podremos ejecutar el programa, si es que se cuenta con los permisos adecuados, independientemente del valor de la variable PATH. Como ejemplo, si quisiéramos ejecutar el programa date, referenciándolo con una ruta completa, la sintaxis es

[shrek@pantano:~]$ /bin/date
mié jul 14 15:15:25 ART 2004

Seguramente el directorio /bin estará en nuestra ruta, esto sirve solo como un ejemplo.

Existe otra forma de ejecutar el programa, y es haciendo referencia a su ruta relativa, que es la ruta con relación al directorio donde estamos parados actualmente. Para esto debemos saber que la forma de referenciar al directorio actual es mediante "." y a su directorio padre con ".." (Mencionándose comúnmente como "punto" y "punto - punto"). Dado que los directorios están divididos por una "/", se podría hacer referencia a un archivo en el directorio padre con ../archivo. De esta forma, si se quisiera hacer referencia a un archivo que se encuentra dos niveles encima nuestro, su referencia relativa seria ../../archivo.

Algo que difiere en la forma de ejecutar los programas en GNU/Linux y DOS, es que este último busca primero el archivo a ejecutar en el directorio actual y luego en la ruta de búsqueda. En cambio en GNU/Linux, solo se buscará en la ruta.

No es problema ejecutar un archivo que esta en el directorio actual ya que este está incluíido en la ruta, pero por seguridad, deberá estar agregado como el último directorio en la ruta, por lo menos en la cuenta "root" ya que si algún usuario creara un archivo malicioso que estuviera en el directorio donde esta parado el usuario root en ese momento, y lo nombrara "more", cuando el root ejecute este comando tendría resultados catastróficos. En cambio, si el directorio actual esta a lo último de la ruta, primero se ejecutara el que se encontré en el /bin en lugar que el que esté en el directorio actual. Igualmente para poder ejecutar directamente un programa que se encuentra en el directorio en que estamos parados actualmente podríamos ingresar la ruta relativa a este, anteponiendo la referencia al directorio actual "./" al programa. Para ejecutar el programa hola que se encuentra en el directorio donde estamos parados actualmente

[shrek@pantano:~]$ ./hola
Hola amigos !!!

No busquen el programa "hola" en su sistema, no lo he distribuido aún ya que esta en desarrollo :^).

Variables de entorno

Independientemente de shell que estemos usando, se contará con lo que se denomina "variable de entorno" o "variable de ambiente". Estas variables son como las que encontraremos en cualquier lenguaje de programación. Esto es tan esta forma que también podría ser accedidas desde los scripts que creemos. Para distinguir de los comando, la variables se ponen en letra mayúscula. Cuando uno entra al sistema, existen ciertas de estas variables ya están asignadas. Para ver estas variables y su contenidos se cuenta con el comando "set", y para ver el contenido de una de estas variables solo tenemos que usar el comando "echo" seguido del nombre de la variable con el signo "$" antepuesto. Ej. echo $LOGNAME, con lo obtendremos el nombre del usuario.

Siguiendo con la ruta de búsqueda o PATH, esta es guardada en una variable llamada "$PATH", con lo que si tecleamos "echo $PATH" veremos algo como esto

[shrek@pantano:~]$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11/bin:/home/shrek:/:.

Nótese que el ultimo exponente de la ruta es el directorio actual que habíamos indicado en el punto anterior sobre "ruta de búsqueda". Como se puede observar cada uno de los directorios esta separado por ":" y en último lugar está la referencia al directorio actual que habíamos hablado anteriormente. Otra variable muy importante es el "prompt" que no es ni más ni menos que la secuencia de caracteres que aparecen justo antes de lo que ingresamos en la interfase de comandos. El entorno bash dispone de 4 prompt's, "PS1", "PS2", "PS3" y "PS4". Estas variables tienen un porqué. La denominación "PS1" es el prompt principal y es el que vemos al iniciar el sistema. El "PS2" es el que aparece si al introducir algo que sobrepasa la línea y se va a la siguiente. Su valor por defecto es ">". Se puede comprobar ingresando el carácter "\" en la línea que estemos ingresando, con lo que pasaremos a la siguiente línea. El "PS3" aparece cuando se utiliza el comando "select" de la interfase de comandos. El "PS4" aparece cuando se efectúa el seguimiento de un comando. El prompt y la mayoría de las variables de entorno pueden ser modificados con el comando "export", con el podremos modificar el contenido de las variables.

[shrek@pantano:~]$ export SECONDS=0
[shrek@pantano:~]$ echo $SECONDS
0

La variable $SECONDS cuenta la cantidad de segundos desde que entramos al sistema; con este mandato la seteamos a "0". Existen variables que no pueden ser cambiadas, como UID, EUID o PPID, (se muestra un detalle de las variables más útiles en la tabla 2). Volviendo al prompt, este puede ser cambiado para que, por ejemplo, nos muestre el nombre de usuario seguido de una "@" y el nombre del sistema, con la ayuda de caracteres especiales.

[shrek@pantano:~]$ export PS1='[\u@\h:\w]\$ '

Se muestra en la tabla los caracteres especiales del prompt.

Tabla 1. Caracteres especiales de la variable PROMPT

CaracterQue representa
\tLa Hora actual en formato HH:MM:SS.
\dLa fecha en formato "Día de la Semana Mes Fecha", como por ejemplo, "Sun Feb 4".
\nSalto de línea.
\sNombre delshell.
\wEl directorio actual.
\WEl nombre base del directorio actual. Por ejemplo, del directorio /home/shrek, el nombre base es shrek.
\uNombre de usuario.
\hNombre de máquina.
\#Número de comando del comando actual.
\!Posición en el historial de comandos del comando actual.
\$Si la EUID es 0 (root), el carácter '#'. En cualquier otro caso el carácter es '$'.
\\La barra inclinada hacia atrás.
\nnnEl carácter correspondiente al numero octal nnn.
\[Comienza una secuencia de caracteres no imprimibles, como los caracteres de escape o las secuencias de control. Estos caracteres pueden ser usados para definir colores.
\]Fin de la secuencia de caracteres no imprimibles.

Tabla 2. Variables más comunes

VariableDefinición
PPIDNúmero de identificación del proceso padre de la interfaz de comandos.
PWDDirectorio de trabajo actual, establecido por el comando cd.
OLDPWDAnterior directorio de trabajo, establecido por el comando cd.
REPLYCuando se usa el comando read de bash sin argumentos,esta variable recoge el contenido de la línea leída.
UIDNúmero identificativo (ID) del usuario, establecida en el arranque del shell.
EUIDID eficaz de usuario, inicializada en el arranque del shell.
BASHNombre completo, incluida la ruta, del archivo que se ejecutó para lanzar el shell.
BASH_VERSIONNúmero de versión de Bash.
SHLVLNivel del shell. Incrementa su valor en una unidad cada vez que se lanza una nueva interfaz de comandos.
RANDOMCada vez que se invoca a esta variable se obtiene un número entero aleatorio. La secuencia de números aleatorios proporcionadas por RAMDOM se puede inicializar simplemente asignándole un nuevo valor a esta variable.
SECONDSMantiene el número de segundos que han transcurrido desde que se ejecutó el shell. Si asignamos un valor a esta variable, la cuenta indicará los segundos transcurridos desde dicha asignación, mas el valor asignado.
LINENOCuando se hace referencia a esta variable desde un script, indica la línea dentro del script en la que se le esta haciendo referencia, considerando que la primera línea se numera como 1. Si se invoca desde la propia interfaz de comandos, el valor que devuelve es el número de línea que hace la orden ejecutada desde que se inició la interfaz de comandos.
HISTCMDContiene la posición dentro del archivo de historia de comandos que ocupa el comando actual.
HOSTTYPESe trata de una cadena de texto describiendo el tipo de máquina en la que esta ejecutando el bash.
HOSTNAMENombre de la máquina.
OSTYPESe trata de una cadena de texto describiendo el sistema operativo en el que sé esta ejecutando el bash.
PATHLa ruta de búsqueda de comandos. Se trata de una secuencia de directorios en los que localizar los programas, separados entre sí por un signo de dos puntos (:). La interfaz de comandos recorrerá esta lista en el orden dado buscando los comandos que queramos ejecutar.
HOMEDirectorio raíz del usuario actual. Es el argumento usado por defecto cuando ejecutamos el comando cd sin especificar ninguno.
CDPATHSe trata de la ruta de búsqueda para el cd. Tiene una estructura similar a la variable PATH. Lo que indica es donde se deben buscar los directorios especificados como argumentos al comando. Como ejemplo habitual, podría contener :~:/usr.
MAILCuando contiene el nombre de un archivo, bash comprueba en él la llegada de correo nuevo y avisa en caso que se produzca.
MAILCHECKDetermina el intervalo de tiempo en segundos que bash tomará para revisar si hay correos nuevos.
MAILPATHAl igual que PATH y CDPATH, esta variable contiene una lista, en este caso, de archivos que deberán ser comprobados para la posible llegada de correo. Se puede indicar un mensaje especifico para la llegada de correo en diferentes buzones usando el carácter '?' como separador entre el archivo y el mensaje. En ese caso, la variable $_ obtendrá el nombre del buzón. Un ejemplo: MAILPATH='/var/spool/mail/nlucas?"Tienes correo":~/mail/buzon?"Ha llegado algo al $_"'
PS1Valor del prompt principal. En la tabla 1 se muestran los valores con los que puede expandirse.
PS2Valor del prompt secundario. Este prompt es el que aparece cuando partimos una línea en la interfaz de comandos para continuar introduciendo en la siguiente línea de la pantalla.
PS3Valor del tercer prompt. Este prompt es usado solamente por el comando select.
PS4Valor del cuarto prompt y último. Tan solo se usa cuando se esta realizando un seguimiento de los comandos mostrándose para indicar los pasos por los que se va ejecutando el comando. Para entrar en el modo de seguimiento, basta con ejecutar set -x. Entonces veremos como cada comando que ejecutemos se expande, mostrando las sustituciones que se hacen con los alias, las variables, etc.
HISTSIZEContiene él número de comandos a guardar en el historial de comandos.
HISTFILEContiene el nombre del archivo en el que se almacena el historial de comandos. Por defecto se trata del archivo ~/.bash_history.
HISTFILESIZENúmero máximo de líneas que puede contener el archivo de historial. Tengamos en cuenta que un comando puede ocupar varias líneas.
PROMPT_COMMANDSi esta definido, contiene el comando a ejecutar antes de presentar el prompt.
IGNOREEOFIndica cuantas veces ha de recibir el carácter EOF (End Of File, o la pulsación de la tecla Crtl-D) antes de salir de bash. Si el valor indicado no es numérico, se toma por defecto el 10. Si no esta seleccionado, una única pulsación basta.
TMOUTSi contiene un valor superior a cero, indica el número de segundos que se puede estar sin introducir un comando a el shell. Pasado este tiempo, la interfaz de comandos se cerrará.
FCEDITRuta y nombre del editor usado por defecto para el comando fc. Por defecto se usa Vi .
FIGNORELista de sufijos de archivos que se ignoraran al intentar completar un nombre de archivo desde bash. La lista estará formada por los sufijos ignorados, separados por un signo de dos puntos (:). Por ejemplo .0:.tmp
EDITOREn esta variable muchos programas buscaran la ruta y el nombre del editor a usar. Por defecto el editor usado es el Vi. Algunos de los programas que hacen uso de esta variable son crontab (con la opción -e), edquota y otros muchos.
TERMContiene el tipo de terminal. Esta variable es importante, pues algunos tipos de terminales no son compatibles con algunos programas.

Expresiones regulares y uso de metacaracteres

Empezaremos ampliando el uso de los comodines que antesmencionamos (* y ?). Es muy frecuente el uso de esta clase de caracterespara que el shell los expanda a fin de que coincidan con cualquiercarácter, en el caso de "?", y con cualquier cadena de caracteres, en elcaso de "*". La utilización de estos ya la definimos anteriormente ysolo nos queda mostrar su utilización en función de las expresiones regulares.

La expresiones regulares son una serie de reglas que de cumplirsese expanden para poder ser utilizadas. Para esto se utiliza también otrotipo de metacaracteres "[]" donde los caracteres que se introduzcandentro serán reemplazados para completar el comando. Supongamos quequeremos editar con el programa vi los archivos carta.1, carta.2, carta.3, carta.4 y carta.5. Podríamos utilizar tanto el carácter ? como el * paraeditarlo. Pero también puede ser utilizado la serie de números del 1 al 5 encerrados dentro de los corchetes ([]).

[shrek@pantano:~]$ vi carta.*

Con esto editaríamos todos los archivos desde carta.1 a carta.5.

[shrek@pantano:~]$ vi carta.[12345]

Con esta expresión, el intérprete de comandos expandirá a cadauno de los caracteres encerrados entre los corchetes para que tome ellugar que corresponde en el nombre del archivo a editar. ¿Pero que pasaría si solo se quisiera editar el archivo carta.1, carta.2 y carta.5? No podríamos utilizar ni el carácter "*" ni el "?" para hacerlo de un solo paso, pero si podremos usar la expresión regular de esta forma

[shrek@pantano:~]$ vi carta.[125]

Con esto, y dado que el intérprete de comandos solo interpreta deun carácter por vez, se editaran solamente los archivos carta.1, carta.2y carta.5. Los números serán tomados dentro de los corchetes comocaracteres individuales.

La utilidad de las expresiones regulares es muy grande, y existencasos que es solamente con su utilización que puede realizarse untrabajo. Las expresiones regulares podrán ser usadas, junto con otroscomandos, también para identificar grupos de caracteres en un texto yrepresentarlos según se pida. Por ejemplo, utilizando el comando grep,que casualmente significa Global Regular Expresión Print, se podrábuscar dentro de un texto una cadena que se requiera y mostrarla por pantalla. Supongamos que necesitamos mostrar todas las líneas que posean la palabra carta en el archivo documentos

[shrek@pantano:~]$ grep carta documentos

Lo que nos devolverá las líneas que posean la palabra carta, aun aquellas que tengan entre sus letras un grupo de caracteres que sea igual al buscado, por ejemplo "cartas" o "pancarta". Ahora bien, que pasaría si la palabra carta estuviese escrita con el primer carácter en mayúsculas. Entonces el comando grep no la mostraría, para esto podríamos usar una expresión regular de esta forma

[shrek@pantano:~]$ grep [Cc]arta documentos

Si lo que necesitáramos es buscar las líneas que comiencen con la palabra carta, tanto con la "C" en mayúsculas o minúsculas usaríamos el símbolo circunflejo (^) y el comando es

[shrek@pantano:~]$ grep ^[Cc]arta documentos

Con esto indicaríamos que mostrará todas las líneas que tuviesen como primer palabra a "carta" tanto con la primera letra en mayúscula o minúscula. Si lo que quisiéramos es mostrar todas las líneas de documentos que terminaran con la palabra "carta", tendríamos que usar la función que cumple el símbolo "$" (ya habíamos visto que era usado para referenciar a una variable de entorno) que en las expresiónes regulares, colocado al final, indica que esta tendrá que ser buscada al final de la línea. Primero le asignaremos la expresión a una variable para que el mandato sea más legible y se vea la utilización de las dos funciones del símbolo $

[shrek@pantano:~]$ VAR=^[Cc]arta
[shrek@pantano:~]$ grep $VAR$ documentos

Explicamos el uso de expresiones regulares solo con el comando grep, pero existe un sin número de oportunidades para usarlas con los comandos. Se verá más adelante que pueden ser muy utilizadas en el editor Vi y nos harán muy fácil el trabajo con éste.

Interpretación de comandos

Hemos podido apreciar la utilización de una serie de comandosjunto con aplicaciones comunes de estos, pero es de necesidad poderentender el método por el cual estos comandos son interpretados por elshell, para tener un poco más de entendimientos a la hora de elaborarnuestros propios script y programas. Lo primero que se nos puede ocurrir averiguar, es el orden en que son interpretados los comandos. Partiremos de la base que se pueden ejecutar más de un comando en una línea. Esto se logra separándolos con un punto y coma (;).

Lo primero que el shell realiza es dividir estos comandos en lo que se denomina "tokens". Cada token es un comando, junto con sus argumentos, a ejecutar. Una vez que el shell puede saber cuantos tokens hay, determina su sintaxis. Si existe un error en este punto, ninguno del os tokens se ejecutará y el shell nos advertirá de un posible error de sintaxis. Si la sintaxis es correcta, comienza la interpretación de los comandos. A partir de aquí el shell trata de ver si alguno de los tokens es en realidad una función del propio shell. Si existe, esta será expandida antes de ser ejecutada. Luego, se expanden los alias. Si alguno de los tokens es en realidad un alias, ya sea creado por el usuario o no, este se expandirá antes de ejecutarse. Si el alias llama a otro alias, este también se expandirá antes de ejecutarse. Una vez que se han expandido los alias y funciones el shell comenzará a evaluar las variables y las sustituirá con su valor. Al terminar esto, se expandirá cualquier comodín utilizado expandiéndolo al nombre de archivo o comando que concuerde de acuerdo a las reglas antedichas. Ahora, aunque está la mayor parte del comando evaluado, el shell tendrá todavía que ver si el primero de los tokens corresponde a un comando del shell o a un programa externo. Si esto ocurriera, se tendrá que consultar la ruta de búsqueda. Luego evaluará si existe algún tipo de redirección o canalización, tem que veremos más en detalle en el próximo capítulo, por el cual la entrada o salida de un comando este redirigida a otro lugar. Una vez que se ha podido analizar todo el comando o conjunto de comandos a su mínima expresión, el shell efectuará una copia de si mismo, a través de la llamada a sistema fork() para poder ejecutar el script. Esta copia es un hijo del shell y que depende del shell que lo originó. Luego de esto, el shell hijo, usará una llamada a sistema denominada exec() para poder ejecutar el binario correspondiente. Aunque quien lo ejecuta es en realidad el shell hijo, hay que recordar que el padre todavía se encuentra en memoria esperando la finalización del proceso hijo.

Puede darse el caso que se desee que quien ejecute el comando o script sea el propio shell y no un shell hijo. Esto se logra con la utilización del comando "." punto. Al anteponer un punto al comando o script y dejando un espacio entre ellos, el shell entenderá que es el mismo quién debe interpretar este comando o script.

[shrek@pantano:~]$ . myscript

Este script será interpretado sin que se genere un sub-shell hijo.

Esta es una simplificación de la ejecución de un comando tipo, no quiere ser una explicación total de lo que puede ocurrir dado la grancantidad de variantes que intervienen, pero servirá como base de entendimiento de lo que el shell realiza una vez que se ha presionado la tecla Enter.