Ir a contenido principal

Bienvenido a My developerworks. Si no tiene un ID de IBM y un password, regístrese aquí.

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. Este perfil incluye el nombre, apellido y nombre de usuario que poporcinó cuando se registró en developerWorks. Cierta información de su perfil será mostrada públicamente, pero usted puede editar la información en cualquier momento. Su nombre, apellido (a menos que usted elija ocultarlo), y nombre de usuario acompañarán el contenido que usted publica.

Toda la información enviada es segura.

La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

Toda la información enviada es segura.

Preparación para el examen 102 de LPI, Tema 109: Shells, scripting, programación y compilación

Tema 109 del examen de Administración Nivel Junior (LPIC-1)

Ian Shields, Senior Programmer, IBM
Ian Shields
Ian Shields trabaja en múltiples proyectos Linux para la zona Linux dev developerWorks. Es Senior Programmer de IBM en el Research Triangle Park (RTP), Carolina del Norte. Ingresó a IBM en Canberra, Australia, como Systems Engineer en 1973, y desde entonces se dedica a sistemas de comunicaciones y computación ubicua en Montreal, Canadá, y en el RTP de Carolina del Norte. Es propietario de numerosas patentes y publicó diversos trabajos. Tiene una diplomatura en Matemática Pura y Filosofía de la Universidad Nacional de Australia. Es Máster y Doctor en Ciencias de la Computación de la Universidad Estatal de Carolina del Norte.

Resumen:  En este tutorial, Ian Shields continúa preparándolo para rendir el® Examen 102 de la Administración Nivel Junior de Linux Professional Institute (LPIC-1). En éste, el quinto de una serie de nueve tutoriales, Ian lo introduce en el mundo del shell Bash, y le presenta los scripts y la programación del shell Bash. Al finalizar este tutorial, usted sabrá cómo personalizar el entorno de su shell, cómo usar estructuras de programación de shell para crear funciones y scripts, cómo activar y desactivar variables de entorno, y cómo usar los diversos scripts de inicio de sesión.

Ver más contenido de esta serie

Fecha:  23-11-2009
Nivel:  Intermediaria

Comentario:  

Personalización de shells

Esta sección se ocupa del material del tema 1.109.1 del examen 102 de la Administración Nivel Junior (LPIC-1). El tema tiene un valor de 5.

En esta sección, aprenda a:

  • Activar y desactivar variables de entorno
  • Usar perfiles para configurar variables de entorno al inicio de la sesión o cuando se genera un nuevo shell
  • Escribir l funciones de shell para las secuencias de comandos frecuentemente utilizadas
  • Uso de listas de comandos

Shells y entornos

Antes del nacimiento de las interfaces gráficas, los programadores usaban una terminal de impresión o una terminal de visualización para ASCII para conectarse a un sistema UNIX® . La terminal de impresión les permitía escribir los comandos, y los datos de salida generalmente se imprimían en papel continuo. La mayoría de las terminals de visualización para ASCII contenían 80 caracteres por línea y alrededor de 25 líneas en pantalla, si bien existían terminales de mayor y menor tamaño. Cuando los programadores escribían un comando y presionaban Enter, el sistema interpretaba y luego ejecutaba dicho comando.

Si bien esto puede parecer algo primitivo en nuestra era de interfaces geográficas de arrastrar y soltar, fue un enorme adelanto respecto de tener que escribir un programa, perforar las tarjetas, compilar el conjunto de tarjetas y ejecutar el programa. Con el nacimiento de los editores, los programadores pudieron incluso crear programas como imágenes de tarjeta y compilarlas en una sesión de terminal.

El flujo de caracteres escritos en una terminal brindaba un flujo de datos estándar de entrada al shell, y el flujo de caracteres que devolvía el shell, ya sea en papel o en el monitor, representaba los datos de salida estándar.

El programa que acepta y ejecuta los comandos se denomina shell. Brinda una capa entre usted y las complejidades de un sistema operativo. Los shells de UNIX y Linux son extremadamente poderosos en el sentido que usted puede elaborar operaciones bastante complejas al combinar funciones básicas. Con el uso de construcciones de programación, usted podrá entonces elaborar funciones para su ejecución directa en el shell o guardar funciones como scripts de shell para poder reutilizarlas una y otra vez.

En ocasiones, usted deberá ejecutar comandos antes de que el sistema haya arrancado lo suficiente como para permitir las conexiones de la terminal, y en otras, deberá ejecutar los comandos de manera periódica, esté o no conectado. Cierto tipo de shell también puede realizar estas tareas por usted. Los datos de entrada y salida estándar no deben necesariamente provenir o dirigirse a un usuario real de la terminal.

En esta sección, usted aprenderá más acerca de los shells. En particular, aprenderá sobre el shell bash o Bourne-again, el cual es una versión mejorada del shell Bourne original, junto con algunas funciones de otros shells y algunos cambios respecto del shell Bourne que lo hacen más adaptable a POSIX.

POSIX significa Portable Operating System Interface for uniX (Interfaz portátil de sistema operativo para Unix), y es una serie de estándares IEEE conocidos colectivamente como IEEE 1003. El primero de ellos fue el Estándar IEEE 1003.1-1988, emitido en 1988. Otros shells famosos incluyen el shell Korn (ksh), el shell C (csh) y su derivado tcsh, el shell Almquist (ash) y su derivado para Debian (dash). Usted deberá tener cierto conocimiento sobre muchos de estos shells, aunque más no sea para reconocer cuando un script determinado requiere características de alguno de ellos.

Muchos aspectos de su interacción con la computadora serán iguales de una sesión a otra. Recuerde que en el tutorial " Preparación para el examen 101 de LPI (tema 103): Comandos GNU y UNIX " decíamos que cuando usted se encuentra funcionando en un shell Bash, tiene un entorno de shell, que define cosas como la forma de su prompt, su directorio principal, su directorio en funcionamiento, el nombre de su shell, los archivos que ha abierto, las funciones que ha definido, etc. El entorno se vuelve disponible a todos los procesos del shell. Los shells, incluyendo el bash, le permiten crear y modificar shellvariables (variables de shell), que usted podrá exportar a su entorno para que sean utilizadas por otros procesos que se ejecutan en el shell o por otros shells que usted haya generado a partir del shell actual.

Tanto las variables de entorno como las variables de shell tienen un nombre. Usted hace referencia al valor de una variable colocando un prefijo a su nombre con el signo '$'. Algunas de las variables de entorno bash más comunes que se encuentran establecidas para usted se muestran en la Tabla 3.

Tabla 3. Variables comunes de entorno bash
NombreFunción
USERNombre del usuario conectado
UIDID numérica de usuario del usuario conectado
HOMEDirectorio principal del usuario
PWDDirectorio actual de trabajo
SHELLNombre del shell
$La ID del proceso (o PID del shell Bash en ejecución (u otro proceso)
PPIDID de proceso del proceso que inició este proceso (es decir, id del proceso principal)
?Código de salida del último comando

Definición de variables

En el shell Bash, usted deberá crear o definir una variable de shell escribiendo un nombre inmediatamente seguido de un signo igual (=). Los nombres de variables (o identificadores) son palabras formadas solamente por caracteres alfanumérico y guiones bajos, que comienzan con un carácter alfabético o un guión bajo. Las variables distinguen entre mayúsculas y minúsculas, por lo tanto var1 y VAR1 son variables diferentes. Por convención, las variables, especialmente las variables exportadas, se escriben en mayúscula, aunque esto no representa un requerimiento. Técnicamente, $$ y $? son parámetros de shell más que variables. Sólo es posible hacer referencia a los mismos; no se les puede asignar un valor.

Cuando usted crea una variable de shell, a menudo será conveniente exportarla al entorno para que quede disponible a otros procesos que usted pueda iniciar desde este shell. Las variables que usted exporta no están disponibles para un shell primario. Usted deberá usar el comando exportar para exportar un nombre de variable. Bash permite el atajo de asignar y exportar en un solo paso.

Para ilustrar la asignación y la exportación, ejecutemos el comando bash mientras estamos en el shell Bash, y luego ejecutemos el shell Korn (ksh) del nuevo shell Bash. Usaremos el comando ps para visualizar la información sobre el comando que se está ejecutando.


Listado 1. Definición y exportación de variables de shell
[ian@echidna ian]$ ps -p $$ -o "pid ppid cmd"
  PID  PPID CMD
30576 30575 -bash
[ian@echidna ian]$ bash
[ian@echidna ian]$ ps -p $$ -o "pid ppid cmd"
  PID  PPID CMD
16353 30576 bash
[ian@echidna ian]$ VAR1=var1
[ian@echidna ian]$ VAR2=var2
[ian@echidna ian]$ export VAR2
[ian@echidna ian]$ export VAR3=var3
[ian@echidna ian]$ echo $VAR1 $VAR2 $VAR3
var1 var2 var3
[ian@echidna ian]$ echo $VAR1 $VAR2 $VAR3 $SHELL
var1 var2 var3 /bin/bash
[ian@echidna ian]$ ksh
$ ps -p $$ -o "pid ppid cmd"
  PID  PPID CMD
16448 16353 ksh
$ export VAR4=var4
$ echo $VAR1 $VAR2 $VAR3 $VAR4 $SHELL
var2 var3 var4 /bin/bash
$ exit
$ [ian@echidna ian]$ echo $VAR1 $VAR2 $VAR3 $VAR4 $SHELL
var1 var2 var3 /bin/bash
[ian@echidna ian]$ ps -p $$ -o "pid ppid cmd"
  PID  PPID CMD
16353 30576 bash
[ian@echidna ian]$ exit
[ian@echidna ian]$ ps -p $$ -o "pid ppid cmd"
  PID  PPID CMD
30576 30575 -bash
[ian@echidna ian]$ echo $VAR1 $VAR2 $VAR3 $VAR4 $SHELL
/bin/bash
					

Notas:

  1. En el inicio de la secuencia, el shell Bash tenía la PID 30576.

  2. El segundo shell Bash tiene la PID 16353, y su shell primario es PID 30576, el shell Bash original.

  3. Creamos VAR1, VAR2, y VAR3 en el segundo shell Bash, pero sólo exportamos VAR2 y VAR3.

  4. En el shell Korn, creamos VAR4. El comando echo mostró valores sólo para VAR2, VAR3, y VAR4, confirmando que VAR1 no fue exportada. ¿Le sorprendió descubrir que el valor de la variable SHELL no se había modificado, incluso cuando había cambiado el prompt? Usted no siempre podrá confiar en que SHELL le diga en qué shell usted está funcionando, pero el comando ps sí le dice el comando real. Observe que ps coloca un guión (-) delante del primer shell Bash para indicar que éste es el shell de inicio de sesión.

  5. Nuevamente, en el segundo shell Bash, podemos ver a VAR1, VAR2 yVAR3.

  6. Finalmente, cuando volvemos al shell original, veremos que ya no existe ninguna de nuestras variables.

El Listado 2 muestra lo que probablemente usted vea en algunas de estas variables bash.


Listado 2. Entorno y variables de shell
[ian@echidna ian]$ echo $USER $UID
ian 500
[ian@echidna ian]$ echo $SHELL $HOME $PWD
/bin/bash /home/ian /home/ian
[ian@echidna ian]$ (exit 0);echo $?;(exit 4);echo $?
0
4
[ian@echidna ian]$ echo $$ $PPID
30576 30575
					

Entornos y el shell C

En los shells tales como C y tcsh, usted deberá usar el comando set para definir las variables de su shell, y el comando setenv para definir y exportar variables. La sintaxis difiere levemente de las del comando export como se muestra en el Listado 3. Observe los signos igual (=) cuando se usa set.


Listado 3. Definición de variables de entorno en el shell C
ian@attic4:~$ echo $VAR1 $VAR2

ian@attic4:~$ csh
% set VAR1=var1
% setenv VAR2 var2
% echo $VAR1 $VAR2
var1 var2
% bash
ian@attic4:~$ echo $VAR1 $VAR2
var2
					


Cómo desactivar variables

Usted puede quitar una variable del shell Bash mediante el comando unset. Puede usar la opción -v para asegurarse de que está eliminando una definición de la variable. Es posible que las funciones tengan el mismo nombre que las variables, de manera que usted deberá usar -f si desea eliminar una definición de función. Sin -f o -v, el comando unset de bash elimina una definición de variable existente; de otro modo, elimina una definición de función existente. (Las funciones serán tratadas en más detalle en la sección Shell functions. )


Listado 4. Comando unset de bash
ian@attic4:~$ VAR1=var1
ian@attic4:~$ VAR2=var2
ian@attic4:~$ echo $VAR1 $VAR2
var1 var2
ian@attic4:~$ unset VAR1
ian@attic4:~$ echo $VAR1 $VAR2
var2
ian@attic4:~$ unset -v VAR2
ian@attic4:~$ echo $VAR1 $VAR2
					

Bash está predeterminado para tratar las variables desactivadas como si tuvieran un valor vacío, por lo tanto es posible que usted se pregunte por qué debería desactivar una variable en lugar de simplemente asignarle un valor vacío. Bash y muchos otros shells le permiten generar un error si se hace referencia a una variable no definida. Use el comando set -u para generar un error para variables no definidas, y set +u para desactivar la advertencia. El Listado 5 ilustra estos puntos.


Listado 5. Generación de errores con variables desactivadas
ian@attic4:~$ set -u
ian@attic4:~$ VAR1=var1
ian@attic4:~$ echo $VAR1
var1
ian@attic4:~$ unset VAR1
ian@attic4:~$ echo $VAR1
-bash: VAR1: unbound variable
ian@attic4:~$ VAR1=
ian@attic4:~$ echo $VAR1

ian@attic4:~$ unset VAR1
ian@attic4:~$ echo $VAR1
-bash: VAR1: unbound variable
ian@attic4:~$ unset -v VAR1
ian@attic4:~$ set +u
ian@attic4:~$ echo $VAR1

ian@attic4:~$
					

Observe que no es un error desactivar una variable que no existe, incluso cuando se ha especificado set -u.


Perfiles

Cuando usted inicia una sesión en un sistema Linux, su id cuenta con un shell predeterminado, que es su shell de inicio de sesión. Si este shell es bash, ejecutará muchos scripts de perfil antes de brindarle el control. Si existe /etc/profile, se ejecutará en primer lugar. Según la distribución que usted tenga, se podrán ejecutar otros scripts del árbol /etc, por ejemplo, /etc/bash.bashrc o /etc/bashrc. Una vez que se han ejecutado los scripts del sistema, se ejecutará un script de su directorio principal si existiese. Bash busca los archivos ~/.bash_profile, ~/.bash_login, y ~/.profile en ese orden. Se ejecuta el primero que se encuentra.

Cuando usted termina la sesión, bash ejecuta el script ~/.bash_logout de su directorio principal si el mismo existiese.

Una vez que usted ha iniciado sesión y se encuentra usando bash, podrá iniciar otro shell, denominado shell interactivo para ejecutar un comando, por ejemplo, para ejecutar un comando en el segundo plano. En este caso, bash ejecuta solamente el script the ~/.bashrc, suponiendo que el mismo exista. Es común buscar este script en su ~/.bash_profile, de manera que usted sólo podrá ejecutarlo al inicio de la sesión y cuando se inicia un shell interactivo, con comandos tales como los que se muestran en el Listado 6.


Listado 6. Búsqueda de ~/.bashrc
# include .bashrc if it exists if [ -f ~/.bashrc ]; then
                    . ~/.bashrc fi

Usted puede obligar a bash a leer perfiles como si fuera un shell de inicio de sesión con la opción --login. Si no desea ejecutar los perfiles para un shell de inicio de sesión, deberá especificar la opción --noprofile. De manera similar, si desea desactivar la ejecución del archivo ~/.bashrc para un shell interactivo, inicie bash con la opción --norc. También puede obligar a bash a usar un archivo que no sea ~/.bashrc si especifica la opción --rcfile con el nombre del archivo que desea usar. El Listado 8 ilustra la creación de un archivo simple denominado testrc y cómo se usa con la opción --rcfile. Observe que la variable VAR1 no no está definida en el shell exterior, sino que ha sido definida para el shell interior por el testrc.


Listado 7. Uso de la opción --rcfile
ian@attic4:~$ echo VAR1=var1>testrc ian@attic4:~$
                    echo $VAR1 ian@attic4:~$ bash --rcfile testrc ian@attic4:~$ echo $VAR1
                    var1

Inicio de bash de otras maneras

Además de los modos estándar que existen para ejecutar bash desde una terminal como se mencionó anteriormente, también se puede usar bash de otras maneras.

A menos que usted origine un script para que se ejecute en el shell actual, bash se ejecutará en su propio shell no interactivo, y no se leerán los perfiles anteriormente mencionados. No obstante, si se define la variable BASH_ENV, bash expande el valor y lo toma como nombre de un archivo. Si el archivo existe, entonces bash ejecuta el archivo antes independientemente de cualquier script o comando que se esté ejecutando en el shell no interactivo. El Listado 8 usa dos archivos simples para ilustrar este caso.


Listado 8. Uso de BASH_ENV
ian@attic4:~$ cat testenv.sh
#!/bin/bash
echo "Testing the environment"
ian@attic4:~$ cat somescript.sh
#!/bin/bash
echo "Doing nothing"
ian@attic4:~$ export BASH_ENV="~/testenv.sh"
ian@attic4:~$ ./somescript.sh
Testing the environment
Doing nothing
					

Los shells no interactivos pueden también iniciarse con la opción --login para forzar la ejecución de los archivos de perfil.

Bash también puede iniciarse en modo POSIX con la opción --posix. Este modo es similar al shell no interactivo, excepto en que el archivo a ejecutar está determinado en la variable de entorno ENV.

En los sistemas Linux, es común ejecutar bash como /bin/sh usando un vínculo simbólico. Cuando bash detecta que está siendo ejecutado con el nombre sh, intenta seguir el comportamiento de inicio del antiguo shell Bourne al tiempo que se adapta a los estándares POSIX. Cuando se ejecuta como un shell de inicio de sesión, bash intenta leer y ejecutar /etc/profile y ~/.profile. Cuando se ejecuta como un shell interactivo con el comando sh, bash intenta ejecutar el archivo especificado por la variable ENV como lo hace cuando es invocado en modo POSIX. Cuando se ejecuta de manera interactiva como sh, solamente usa un perfil especificado por la variable ENV; siempre se ignorará la opción --rcfile.

Si bash es invocado por el daemon remoto del shell, se comportará como un shell interactivo, y usará el archivo ~/.bashrc si existiera.


Alias de los shell

El shell Bash le permite definir alias para los comandos. Los motivos más comunes para la creación de alias son brindar un nombre alternativo para el comando, o proporcionar algunos parámetros predeterminados para el comando. El editor vi ha sido durante años un elemento básico de UNIX y los sistemas Linux. El editor vim (Vi IMproved) es similar a vi, pero presenta muchas mejoras. De manera que si usted está escribiendo "vi" cuando desea un editor, pero en realidad prefiere usar vim, entonces necesitará un alias. El Listado 9 muestra cómo usar el comando alias para lograr esta tarea.


Listado 9. Uso de vi como alias de vim
[ian@pinguino ~]$ alias vi='vim'
[ian@pinguino ~]$ which vi
alias vi='vim'
   /usr/bin/vim
[ian@pinguino ~]$ /usr/bin/which vi
/bin/vi
					

Observe en este ejemplo que si usted usa el comando which para ver dónde reside el programa vi, obtendrá dos líneas de datos de salida: la primera le muestra el alias, y la segunda, la ubicación de vim ( /usr/bin/vim). Sin embargo, si usa el comando which con su ruta completa ( /usr/bin/which), obtendrá la ubicación del comando vi. Si usted dedujo que esto significa que el comando which propiamente dicho posee un alias en este sistema, estará en lo correcto.

También puede usar el comando alias para mostrar todos los alias si lo usa sin opciones o sólo con la opción -p, y podrá mostrar todos los alias para uno o más nombres proporcionando los nombres como argumentos sin asignaciones. El Listado 10 muestra los alias para which y vi.


Listado 10. Alias para which y vi
[ian@pinguino ~]$ alias which vi alias which='alias |
                    /usr/bin/which --tty-only --read-alias --show-dot --show-tilde' alias
                    vi='vim'

El alias para el comando which resulta bastante curioso. ¿Por qué canalizar los datos de salida del comando del alias (sin argumentos) a /usr/bin/which ? Si usted consulta las páginas man sobre el comando which, descubrirá que la opción --read-alias le indica a which que lea una lista de alias desde stdin y que informe las correspondencias en stdout. Esto permite que el comando which informe los alias además de los comandos de su PATH, por lo cual es probable que su distribución lo haya configurado como predeterminado para usted. Esto es bueno, debido a que el shell ejecutará un alias antes de un comando con el mismo nombre. Entonces, ahora que usted sabe esto, podrá verificarlo usando alias which. Además, podrá saber si este tipo de alias ha sido configurado para which ejecutando which which.

Otro uso común para los alias es agregar parámetros a los comandos de manera automática, como vio anteriormente para --read-alias y muchos otros parámetros del comando which. Esta técnica se aplica usualmente para el usuario root con los comandos cp, mv, y rm de manera que se emita un prompt antes de que se detecten o sobrescriban los archivos. Esto puede verse en el Listado 11.


Listado 11. Agregado de parámetros de seguridad
[root@pinguino ~]# alias cp mv rm alias
                    cp='cp -i' alias mv='mv -i' alias rm='rm -i'


Listas de comandos

En el anterior tutorial " Preparación para el examen 101 de LPI (tema 103): Comandos GNU y UNIX," usted aprendió sobre las secuencias o listas de los comandos. Acaba de ver al operador de canalización (|) usado con un alias, y puede también usar listas de comandos. Imagine, a modo de ejemplo, que usted desea que un comando elabore una lista de los contenidos del directorio actual y además, la cantidad de espacio usado por él y todos sus subdirectorios. Lo llamaremos comando lsdu. Entonces, usted simplemente asignará una secuencia de comandos ls y du al lsdu del alias . El Listado 12 muestra un modo erróneo de hacerlo y también el modo correcto. Obsérvelo cuidadosamente antes de leer, y piense en por qué fracasó el primer intento.


Listado 12. Alias para secuencias de comandos
[ian@pinguino developerworks]$ alias lsdu=ls;du -sh # Wrong way
2.9M    .
[ian@pinguino developerworks]$ lsdu
a tutorial  new-article.sh   new-tutorial.sh   readme  tools  xsl
my-article  new-article.vbs  new-tutorial.vbs  schema  web
[ian@pinguino developerworks]$ alias 'lsdu=ls;du -sh' # Right way way
[ian@pinguino developerworks]$ lsdu
a tutorial  new-article.sh   new-tutorial.sh   readme  tools  xsl
my-article  new-article.vbs  new-tutorial.vbs  schema  web
2.9M    .
					

Usted deberá prestar mucha atención al citar la secuencia completa que conformará el alias. También deberá ser cuidadoso acerca del uso de comillas simples o dobles si cuenta con variables de shell como parte del alias. ¿Desea que el shell expanda las variables cuando se defina o cuando se ejecute el alias? El Listado 13 muestra el modo erróneo de crear un comando personalizado denominado mywd cuya intención es imprimir el nombre de su directorio en funcionamiento


Listado 13. pwd personalizado: intento 1
[ian@pinguino developerworks]$ alias mywd="echo \"My working directory is $PWD\""
[ian@pinguino developerworks]$ mywd
My working directory is /home/ian/developerworks
[ian@pinguino developerworks]$ cd ..
[ian@pinguino ~]$ mywd
My working directory is /home/ian/developerworks
					

Recuerde que las comillas dobles hacen que bash expanda las variables antes de ejecutar un comando. El Listado 14 usa el comando alias para mostrar cuál es en realidad el alias resultante, por lo cual nuestro error es evidente. El Listado 14 muestra además un modo correcto de definir este alias.


Listado 14. pwd personalizado: intento 2
[ian@pinguino developerworks]$ alias mywd
alias mywd='echo \"My working directory is $PWD\"'
[ian@pinguino developerworks]$ mywd
"My working directory is /home/ian/developerworks"
[ian@pinguino developerworks]$ cd ..
[ian@pinguino ~]$ mywd
"My working directory is /home/ian"
					

Por fin el éxito.


Funciones de los shells

Los alias le permiten usar una abreviatura o un nombre alternativo para un comando o una lista de comandos. Es posible que haya notado que puede agregar otros elementos, como por ejemplo el nombre del programa que está buscando con el comando which. Cuando se ejecutan los datos que usted ha ingresado, el alias se expande, y cualquier otra cosa que usted escriba posteriormente se agrega a la expansión antes de que se ejecute el comando o la lista final. Esto significa que usted sólo podrá agregar parámetros al final del comando o la lista, y que podrá usarlos sólo con el comando final. Las funciones brindan capacidad adicional, incluyendo la capacidad de procesar los parámetros. Las funciones forman parte de la definición del shell por parte de POSIX. Se encuentran disponibles en shells tales como bash, dash, y ksh, pero no se encuentran en csh o tcsh.

En los próximos párrafos, usted elaborará un comando complejo pieza por pieza, a partir de bloques de construcción menores, y lo refinará en cada paso para convertirlo en una función que más adelante volverá a refinar.

Problema hipotético

Usted puede usar el comando ls para hacer una lista de una variedad de información sobre los directorios y los archivos de su sistema de archivos. Imagine que desea un comando, llamémoslo ldirs, que enumere los nombres de los directorios con datos de salida como los del Listado 15.


Listado 15. Datos de salida del comando ldirs
[ian@pinguino developerworks]$ ldirs *[st]* tools/*a*
                    my dw article schema tools tools/java xsl

Para hacer que las cosas sigan siendo relativamente simples, los ejemplos de esta sección usan los directorios y los archivos del paquete de autor de developerWorks (ver Recursos), el cual usted puede usar, si lo desea, para escribir artículos o tutoriales para developerWorks. En estos ejemplos, usamos el script new-article.sh del paquete para crear una plantilla para un nuevo artículo que denominamos "my dw article".

Al momento de escribir este artículo, la versión del paquete de autor de developerWorks es la 5.6, de manera que si usted usa una versión posterior, es posible que encuentre diferencias. También puede usar sus propios archivos y directorios. El comando ldirs también los manejará. Usted podrá encontrar otros ejemplos de funciones de bash en las herramientas incluidas en el paquete de autor de developerWorks.

Cómo encontrar las entradas de directorios

Ignorando por el momento *[st]* tools/*a*, si usted usa el comando ls con las opciones de color que se muestran en los alias anteriores, verá datos de salida similares a los que muestran en la Figura 1.


Figura 1. Cómo distinguir los archivos y directorios con el comando ls
Distinguishing files and directories with the ls command

En este ejemplo, los directorios se muestran en azul oscuro, si bien esto es un poco difícil de descodificar con las aptitudes que usted ha desarrollado en esta serie de tutoriales. El uso de la opción -l, sin embargo, le dará una pauta de cómo proceder: las listas de los directorios tienen una 'd' al final. De manera que su primer paso puede ser simplemente filtrarlas de las listas largas con grep como se muestra en el Listado 16.


Listado 16. Uso de grep para buscar entradas de directorios
[ian@pinguino developerworks]$ ls -l | grep "^d"
drwxrwxr-x 2 ian ian 4096 Jan 24 17:06 my dw article
drwxrwxr-x 2 ian ian 4096 Jan 18 16:23 readme
drwxrwxr-x 3 ian ian 4096 Jan 19 07:41 schema
drwxrwxr-x 3 ian ian 4096 Jan 19 15:08 tools
drwxrwxr-x 3 ian ian 4096 Jan 17 16:03 web
drwxrwxr-x 3 ian ian 4096 Jan 19 10:59 xsl
					

Recorte de las entradas de directorios

Quizás le convenga usar awk en lugar de grep para poder en una pasada, filtrar la lista y quitar la última parte de cada línea, que es el nombre del directorio, como se muestra en el Listado 17.


Listado 17. Uso de awk em lugar de grep
[ian@pinguino developerworks]$ ls -l  | awk '/^d/ { print $NF } '
article
readme
schema
tools
web
xsl
					

El problema que surge con el enfoque del Listado 17 es que no se ocupa del directorio con espacios en su nombre, como por ejemplo "my dw article". Como casi siempre sucede en Linux y en la vida, a menudo existen diversas maneras de resolver un problema. Pero como el objetivo aquí consiste en aprender acerca de las funciones, volveremos a usar grep. Otra de las herramientas que usted vio anteriormente en esta serie es cut, que corta campos de un archivo, incluyendo stdin. Si volvemos a ver el Listado 16, observará que hay ocho campos delimitados vacíos antes del nombre del archivo. El agregado de cut al comando anterior generará datos de salida como los que se muestran en el Listado 18. Observe que la opción -f9- le dice a cut que imprima los campos 9 y superiores.


Listado 18. Uso de cut para recortar nombres
[ian@pinguino developerworks]$ ls -l | grep "^d" |
                    cut -d" " -f9- my dw article readme schema tools web xsl

Hay un pequeño problema en este enfoque que se plantea de manera obvia cuando probamos el comando en el directorio de herramientas en lugar de en el directorio actual como se muestra en el Listado 19.


Listado 19. Problema con cut
[ian@pinguino developerworks]$ ls -l tools | grep "^d" | cut -d" " -f9-
11:25 java
[ian@pinguino developerworks]$ ls -ld tools/[fjt]*
-rw-rw-r-- 1 ian ian  4798 Jan  8 14:38 tools/figure1.gif
drwxrwxr-x 2 ian ian  4096 Oct 31 11:25 tools/java
-rw-rw-r-- 1 ian ian 39431 Jan 18 23:31 tools/template-dw-article-5.6.xml
-rw-rw-r-- 1 ian ian 39407 Jan 18 23:32 tools/template-dw-tutorial-5.6.xml
					

¿Cómo llegó aquí la marca de tiempo? Los dos archivos de plantillas tienen tamaños de 5 dígitos, mientras el directorio java sólo posee un tamaño de 4 dígitos. Por ende, cut interpretó que el espacio adicional era otro separador de campo.

Use seq para encontrar un punto de corte

El comando cut puede también realizar cortes usando posiciones de caracteres en lugar de campos. En lugar de contar caracteres, el shell Bash posee muchísimos utilitarios que usted podrá emplear. Entonces, podrá probar con los comandos seq y printf para imprimir una regla por encima de su extensa lista de directorios, para encontrar fácilmente dónde debe cortar las líneas de datos de salida. El comando seq toma hasta tres argumentos, que le permiten imprimir todos los números hasta un valor dado, todos los números entre un valor y otro, o todos los números desde un valor, siguiendo la ejecución del comando en función de un valor dado, hasta un tercer valor. En las páginas man encontrará todas las demás maravillas que puede hacer con seq, incluyendo la impresión de números octales y hexadecimales. Por ahora, usaremos seq y printf para imprimir una regla con posiciones marcadas cada 10 caracteres, como se muestra en el Listado 20.


Listado 20. Impresión de una regla con seq y printf
[ian@pinguino developerworks]$ printf "....+...%2.d" `seq 10 10 60`;printf "\n";ls -l
....+...10....+...20....+...30....+...40....+...50....+...60
total 88
drwxrwxr-x 2 ian ian 4096 Jan 24 17:06 my dw article
-rwxr--r-- 1 ian ian  215 Sep 27 16:34 new-article.sh
-rwxr--r-- 1 ian ian 1078 Sep 27 16:34 new-article.vbs
-rwxr--r-- 1 ian ian  216 Sep 27 16:34 new-tutorial.sh
-rwxr--r-- 1 ian ian 1079 Sep 27 16:34 new-tutorial.vbs
drwxrwxr-x 2 ian ian 4096 Jan 18 16:23 readme
drwxrwxr-x 3 ian ian 4096 Jan 19 07:41 schema
drwxrwxr-x 3 ian ian 4096 Jan 19 15:08 tools
drwxrwxr-x 3 ian ian 4096 Jan 17 16:03 web
drwxrwxr-x 3 ian ian 4096 Jan 19 10:59 xsl
					

¡Aha! Ahora usted puede usar el comando ls -l | grep "^d" | cut -c40- para cortar líneas desde la posición 40. Si reflexionamos un momento, veremos que esto en realidad tampoco resuelve el problema, debido a que los archivos más extensos moverán la posición de corte correcta as la derecha. Compruébelo usted mismo.

Sed al rescate

A veces denominado "la navaja suiza" del kit de herramientas de UNIX y Linux, sed es un extremadamente poderosos filtro de edición que utiliza expresiones regulares. Resulta claro ahora que el desafío consiste en quitar las primeras 8 palabras y los espacios en blanco que siguen de cada línea de datos de salida que comience con 'd'. Usted puede hacer todo esto con sed : seleccione sólo aquellas líneas que le interesan usando la expresión de correspondencia de patrones /^d/, sustituyendo una cadena nula con las primeras ocho palabras con el comando sustituto s/^d\([^ ]* *\)\(8\}//. Use la opción -n para imprimir sólo las líneas que usted especifica con el comando p como se muestra en el Listado 21.


Listado 21. Recorte de nombres de directorios con sed
[ian@pinguino developerworks]$ ls -l | sed -ne 's/^d\([^ ]* *\)\{8\}//p' 
my dw article
readme
schema
tools
web
xsl
[ian@pinguino developerworks]$ ls -l tools | sed -ne 's/^d\([^ ]* *\)\{8\}//p'
java
					

Para conocer más acerca de sed, consulte la sección Recursos.

Por fin una función

Ahora que usted tiene el comando complejo que necesita para su función ldirs, es hora de aprender acerca de cómo se lo convierte en una función. Una función consiste en un nombre seguido por () y luego un comando compuesto. Por ahora, consideraremos que un comando compuesto es cualquier comando o lista de comandos, finalizada con un punto y coma y encerrada por llaves (que deben estar separadas de otros tokens mediante un espacio en blanco). Usted aprenderá acerca de otros comandos compuestos en la sección Scripts del shell.

Nota: En el shell Bash, el nombre de una función puede estar precedido por la palabra 'function', pero esto no forma parte de la especificación POSIX y no se encuentra soportado por shells más minimalistas como por ejemplo dash. En la sección Scripts del shell, usted aprenderá a asegurarse que un script se encuentra interpretado por un shell determinado, incluso si usted normalmente utiliza un shell diferente.

Dentro de la función, usted puede referirse a los parámetros usando las variables especiales de bash que aparecen en la Tabla 4. Debe colocarles un símbolo $ como prefijo para referirse a ellos, al igual que con las demás variables del shell.

Tabla 4. Parámetros del shell para las funciones
ParámetroPropósito
0, 1, 2, ...Parámetros posicionales que comienzan desde el parámetro 0. El parámetro 0 se refiere al nombre del programa que inició bash, o al nombre del script del shell si la función se ejecuta dentro de un script del shell. Consulte las páginas man para obtener información sobre otras posibilidades, como por ejemplo cuando bash se inicia con el parámetro -c. Una cadena encerrada dentro de comillas simples o dobles pasará como un parámetro único, sin las comillas. En el caso de las comillas dobles, toda variable del shell, como por ejemplo $HOME, se expandirá antes de que la función sea llamada. <usted deberá usar comillas simples o dobles para pasar parámetros que contengan espacios en blanco incrustados u otros caracteres que puedan tener un significado especial para el shell.
*Parámetros posicionales que comienzan desde el parámetro 1. Si la expansión se realiza con comillas dobles, entonces la expansión será una única palabra en donde el primer carácter de la variable especial del separador de campos (IFS) separa los parámetros o no interviene espacios si el IFS es nulo. El valor predeterminado es un espacio en blanco, una pestaña y una nueva línea. Si el IFS no está definido, entonces el separador utilizado es un espacio en blanco, al igual que para el IFS predeterminado.
@Parámetros posicionales que comienzan desde el parámetro 1. Si la expansión se realiza con comillas dobles, entonces cada parámetro se convierte en una única palabra, de manera que "$@" es equivalente a "$1" "$2" .... Si es probable que sus parámetros contengan espacios en blanco incrustados, será conveniente que use esta forma.
#Cantidad de parámetros, sin incluir el parámetro 0.

Nota: Si usted tiene más de 9 parámetros, no puede usar $10 para referirse al décimo. Usted deberá en primer lugar, o bien procesar o guardar el primer parámetro ($1), y luego usar el comando shift para dejar el parámetro 1 y mover los parámetros restantes 1 lugar hacia abajo, de manera que $10 se convierta en $9 y así sucesivamente. El valor de $# quedará actualizado para reflejar la cantidad restante de parámetros.

Ahora usted puede definir una función simple para que haga nada menos que decirle cuántos parámetros posee y se los muestre como se observa en el Listado 22.


Listado 22. Parámetros de función
[ian@pinguino developerworks]$ testfunc () { echo "$# parameters"; echo "$@"; }
[ian@pinguino developerworks]$ testfunc
0 parameters

[ian@pinguino developerworks]$ testfunc a b c
3 parameters
a b c
[ian@pinguino developerworks]$ testfunc a "b c"
2 parameters
a b c
					

Ya sea que usted use $*, "$*", $@, o "$@", no verá demasiada diferencia en los datos de salida de la función anterior; sin embargo, esté seguro de que cuando las cosas se vuelvan más complejas, las distinciones que existen tendrán mucha importancia.

Ahora tome el comando complejo que probamos hasta ahora y cree una función ldirs con él, usando "$@" para representar los parámetros. Usted puede ingresar la totalidad de la función en una única línea como lo hizo en el ejemplo anterior; también, bash le permitirá ingresar comandos en múltiples líneas, en cuyo caso se agregará automáticamente un punto y coma como se muestra en el Listado 23. El Listado 23 también ilustra el uso del comando type para visualizar la definición de la función. Observe a partir de los datos de salida de type que el comando ls ha sido reemplazado por el valor expandido de su alias. Usted podría usar /bin/ls en lugar del simple ls si fuera necesario evitarlo.


Listado 23. Su primera función ldirs
[ian@pinguino developerworks]$ # Enter the function on a single line            
[ian@pinguino developerworks]$ ldirs () { ls -l "$@"|sed -ne 's/^d\([^ ]* *\)\{8\}//p'; }
[ian@pinguino developerworks]$ # Enter the function on multiple lines           
[ian@pinguino developerworks]$ ldirs ()
> {
> ls -l "$@"|sed -ne 's/^d\([^ ]* *\)\{8\}//p'
> }
[ian@pinguino developerworks]$ type ldirs
ldirs is a function
ldirs ()
{
    ls --color=tty -l "$@" | sed -ne 's/^d\([^ ]* *\)\{8\}//p'
}
[ian@pinguino developerworks]$ ldirs
my dw article
readme
schema
tools
web
xsl
[ian@pinguino developerworks]$  ldirs tools
java
					

De manera que ahora su función parece funcionar. Pero ¿Qué sucede si usted ejecuta ldirs * como se muestra en el Listado 24?


Listado 24. Ejecución de ldirs *
[ian@pinguino developerworks]$ ldirs * 5.6 java www.ibm.com
                    5.6

¿Sorprendido? En el directorio actual, usted no encontró directorios, sino subdirectorios de segundo nivel. Revise la página man sobre el comando ls o nuestros tutoriales anteriores y comprenderá por qué. De lo contrario, ejecute el comando find como se muestra en el Listado 25 para imprimir los nombres de los subdirectorios de segundo nivel.


Listado 25. Cómo encontrar subdirectorios de segundo nivel
[ian@pinguino developerworks]$ find . -mindepth 2 -maxdepth 2 -type d
./tools/java
./web/www.ibm.com
./xsl/5.6
./schema/5.6
					

Agregado de algunas pruebas

El uso de comodines ha expuesto un problema con la lógica de este enfoque. Alegremente ignoramos el hecho de que ldirs sin parámetros mostró los subdirectorios en el directorio actual, mientras que ldirs tools mostró el subdirectorio java del directorio de herramientas más que el directorio de herramientas propiamente dicho como se esperaría usando ls con archivos más que con directorios. Lo ideal sería usar ls -l si no se proveen parámetros y ls -ld si se proveen algunos parámetros. Usted puede usar el comando test para comprobar la cantidad de parámetros, y luego usar && y || para construir una lista de comandos que ejecute el comando adecuado. Al usar la forma test expression ] de test, su expresión será algo similar a { [ $# -gt 0 ] &&/bin/ls -ld "$@" || /bin/ls -l } | sed -ne ....

No obstante, hay un pequeño problema con este código, en el sentido de que si el comando ls -ld no encuentra archivos o directorios correspondientes, emitirá un mensaje de error y devolverá un código de salida sin cero, haciendo que el comando ls -l también se ejecute. Quizás esto no sea lo que usted desea. Una respuesta consiste en construir un comando compuesto para el primer comando ls de manera que el número de parámetros se verifique nuevamente si falla el comando. Expanda la función para incluir esto, con lo cual su función se verá como la que aparece en el Listado 26. Intente usarla con algunos de los parámetros del Listado 26, o experimente con sus propios parámetros para ver su comportamiento.


Listado 26. Manejo de comodines con ldirs
[ian@pinguino ~]$ type ldirs
ldirs is a function
ldirs ()
{
    {
        [ $# -gt 0 ] && {
            /bin/ls -ld "$@" || [ $# -gt 0 ]
        } || /bin/ls -l
    } | sed -ne 's/^d\([^ ]* *\)\{8\}//p'
}
[ian@pinguino developerworks]$ ldirs *
my dw article
readme
schema
tools
web
xsl
[ian@pinguino developerworks]$ ldirs tools/*
tools/java
[ian@pinguino developerworks]$ ldirs *xxx*
/bin/ls: *xxx*: No such file or directory
[ian@pinguino developerworks]$ ldirs *a* *s*
my dw article
readme
schema
schema
tools
xsl
					

Retoque final

En este momento, usted podrá obtener un directorio que aparezca dos veces como en el último ejemplo del Listado 26. Usted podría ampliar la canalización canalizando los datos de salida de sed mediante sort | uniq si lo desea.

A partir de algunos bloques de construcción pequeños, usted ha construido una función de shell bastante compleja.


Personalización de teclas

Las teclas que usted presiona en una sesión de terminal, y aquellas usadas en programas tales como FTP, son procesadas por la biblioteca de líneas de lectura y pueden ser configuradas. El archivo de personalización predeterminado es .inputrc y se encuentra en su directorio principal. El mismo será leído durante el inicio de bash, si existiera. Usted puede configurar un archivo diferente estableciendo la variable INPUTRC. Si no se la define, se usará .inputrc en su directorio principal. Muchos sistemas cuentan con un mapeo de teclas predeterminado en /etc/inputrc, por lo cual será usualmente conveniente que las incluya usando la directiva $include.

El Listado 27 ilustra de qué manera usted puede enlazar su función ldirs a la combinación de teclas Ctrl-t (presionar y mantener Ctrl, luego presionar t). Si desea que el comando se ejecute sin parámetros, agregue \n al final de la línea de configuración.


Listado 27. Ejemplo de archivo .inputrc
# My custom key mappings $include /etc/inputrc

Usted puede obligar al archivo INPUTRC a leer nuevamente si presiona Ctrl-x y luego Ctrl-r. Observe que algunas distribuciones fijarán INPUTRC=/etc/inputrc si usted no posee su propio .inputrc, por lo tanto si usted crea uno en un sistema así, deberá desconectarse y volver a iniciar sesión para levantar sus nuevas definiciones. Si sólo resetea INPUTRC a un valor nulo o para que se dirija a su nuevo archivo, el sistema volverá a leer el archivo original y no la nueva especificación.

El archivo INPUTRC puede incluir especificaciones condicionales. Por ejemplo, el comportamiento de su teclado deberá ser distinto según usted use el modo de edición emacs (predeterminado por bash) o el modo de edición vi. Consulte las páginas man para ver más detalles sobre cómo personalizar su teclado.


Cómo guardar alias y funciones

Usted probablemente agregará sus propios alias y funciones al archivo ~/.bashrc, si bien puede guardarlos en cualquier archivo que desee. No importa lo que haga, recuerde, recuerde originar el/los archivo/s con el comando source o . de manera que los contenidos de su archivo se lean y ejecuten en el entorno actual. Si usted crea un script y solamente lo ejecuta, el mismo será ejecutado en un subshell y se perderá la valiosa personalización que usted ha hecho cuando se produzca la salida del subshell y el mismo le devuelva el control.

En la próxima sección, usted aprenderá cómo ir más allá de las funciones simples. Aprenderá a agregar construcciones de programación tales como las verificaciones condicionales y construcciones de bucle, y a combinarlas con múltiples funciones para crear o modificar scripts en el shell Bash.

2 de 5 | Anterior | Siguiente

Comentario



static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=Linux
ArticleID=449222
TutorialTitle=Preparación para el examen 102 de LPI, Tema 109: Shells, scripting, programación y compilación
publish-date=11232009
author1-email=ishields@us.ibm.com
author1-email-cc=

Tabla de contenidos

Etiquétalo Etiquetas

Help
Utilice el campo de búsqueda para encontrar todo tipo de contenido en My developerWorks con esa etiqueta.

Utilice el deslizador para controlar cuántas etiquetas deben mostrarse.

Las etiquetas populares muestran las etiquetas más difundidas en esta zona particular de contenido (por ejemplo: Java, Linux, WebSphere).

Mis Etiquetas muestra sus etiquetas en esta zona particular de contenido (por ejemplo: Java, Linux, WebSphere).

Utilice el campo de búsqueda para encontrar todo tipo de contenido en My developerWorks con esa etiqueta. Las etiquetas populares muestran las etiquetas más difundidas en esta zona particular de contenido (por ejemplo: Java, Linux, WebSphere). Mis Etiquetas muestra sus etiquetas en esta zona particular de contenido (por ejemplo: Java, Linux, WebSphere).