Site hosted by Angelfire.com: Build your free website today!

Shell Avanzado

El shell de Unix es el programa que interactua con el usuario. Su funcion es de interpretar comandos que le provee el usuario.

El shell es tanto un interprete de comandos como un lenguaje de programacion; para sacarle el maximo provecho conviene aprenderse algo de ese lenguaje. El proposito de esta leccion es de facilitar este aprendizaje.

Como hemos dicho, hay dos versiones de shell: el original sh de ATT, y todos sus sucesores (bash, ksh); segundo el csh y sus derivados (tcsh). En esta leccion concentraremos en el sh, dejando csh marginado.

Las cosas a aprenderse pueden resumirse en la siguiente lista; recomendamos que la primera vez que la use lo haga en el orden dado; despues puede hacerlo en orden arbitrario.


Variables

Puntos a tratar:

Asignacion y uso de variables

Como cualquier lenguaje de programacion, el shell permite definir y usar variables para recordar cosas importantes. Hay dos aspectos del uso de variables: asignacion y uso. Para asignar una variable, usamos la sintaxis:

 var=valor
Aqui var puede ser cualquier combinacion de letras que empieze por lafabeticas, distinguiendo minusculas y mayusculas. Por ejemplo

 Xabc=23
 nombrelargo=abc
Para usar una variable, se coloca su nombre precedido del signo "$". Esto causa que el valor (mano derecha) de la variable se sustituya donde aparece el "$". Por ejemplo:
 abulafia% Xabc=23
 abulafia% echo valor $Xabc
     valor 23
Aqui vemos como el shell sustituye el valor de la variable (Xabc) por su valor (23). Si una variable es indefinida, (no ha sido usada a mano izquierda de una asignatura) tiene valor nulo, es decir, no se sustituye nada. No se considera un error usar una variable nula.

Ejercicio
Repita el ejemplo anterior con una variable no asignada (nula). Que pasa?

Ejercicio
Investigue el uso de "pegar" variables. Por ejemplo:

  x1=23
  x2=ab
  echo $x1$x2
Como haria para que una variable x3 valiera la encadenacion de otras dos (en este caso, para que x3 == 23ab .

En csh, la sintaxis para asignar es

 abulafia% set Xabc 23
y el uso es exactamente igual a sh.

Ir a: Principio, Seccion.

Variables globales y locales

Tal como los lenguajes de programacion tienen variables globales (por ejemplo, FORTRAN tiene variables que estan en COMMON), el shell tiene dos tipos de variables: unas que sobreviven todas los forks y execs del shell y otras que no. En efecto, el shell mantiene una especie de ``guarderia'' para las variables globales, llamada el environment o ambiente; el ambiente esta disponible a cualquier programa invocado por el shell.

Primero veamos como hacer que una variable se incorpore a la guarderia; en sh esto se hace asi:

 abulafia% Xabc=23; export Xabc
En ksh o bash:
 abulafia% export Xabc=23
Para ver lo que tenemos en la guarderia, un nuevo comando:
 abulafia% env
En mi maquina, esto produce:

ignoreeof=0
LOGNAME=juanr
MINICOM=-c on
MAIL=/var/spool/mail/juanr
TERMCAP=xterm|vs100|xterm terminal emulator (X window system):---
TERM=xterm
HOSTTYPE=i386
PATH=:/bin:/usr/bin:/usr/local/bin:/u/juanr/bin:/usr/TeX/bin:---
HOME=/home/juanr
SHELL=/bin/bash
PS1=\w>
PS2=...
MANPATH=/usr/man:/usr/man/preformat:/usr/X11/man:
LESS=-MM
DISPLAY=:0.0
GSVGAMODE=G320x200x256
WINDOWID=16777229
SHLVL=4
BASH=/bin/bash
_=/usr/bin/env
Algunas lineas han sido recortadas (indicadas por ---). Como se puede ver, hay bastantes cosas en la guarderia. Las mas importantes son: La variable PWD contiene el nombre del directorio actual.

Ir a: Principio, Seccion.

Sustitucion o no de variables

A veces es necesario suprimir la sustitucion de variables. Por ejemplo, cuando queremos atribuirle al signo "$" su significado de todos los dias:
 abulafia% echo El precio es $25
     El precio es
Este resultado se debe a que el shell busca la variable 25-- y no la encuentra, por lo tanto es nula. Esto nos trae al uso de comillas, las cuales se usan para controlar la sustitucion. Hay tres tipos de comillas con diferente significado:
Comillas sencillas (')
Las comillas sencillas inhiben totalmente la sustitucion. Por lo tanto, podemos reescribir el ejemplo anterior:
 abulafia% echo 'El precio es $25'
     El precio es $25
Comillas dobles (")
Las comillas dobles inhiben sustitucion de todo menos $ y a veces \. Por ejemplo, no se interpreta el asterisco. Esto dice que dentro de comillas dobles se sustituyen variables pero mas nada. Probablemente esto sea la mejor forma de producir mensajes, y uno debe acostumbrarse a ponerle doble comillas a los mensajes:
 abulafia% echo "Ud esta en el directorio $PWD" 
     Ud esta en el directorio /u/juanr/Mosaic
Comillas graves (`)
Estas comillas, asi llamadas por parecerse al acento grave del frances, se usan para asignar el resultado de un comando a una variable. En efecto se ejecuta todo lo que esta entre las comillas graves, y el resultado de los comandos, que normalmente va a la pantalla, se puede usar como variable.

El siguiente ejemplo cuenta los archivos en un directorio (wc es un comando que cuenta palabras o lineas o bytes de un archivo)).

 abulafia%ls | wc -w  wc cuenta palabras
     22 
 abulafia%arch=`ls | wc -w` asignamos por grave
 abulafia%echo "Hay $arch en este directorio"
     Hay  22 en este directorio
Debo mencionar que por lo menos en bash vale esto ``en un solo saque'':
 abulafia% echo "Hay `ls | wc -w` archivos" 
Es decir, valen comillas graves dentro de comillas dobles. Tambien es mas dificil de leer.

Ejercicio
Que pasa con comillas graves dentro de comillas sencillas? Que pasa con comillas dobles dentro de comillas graves?

Ir a: Principio, Seccion.


Ejecucion condicional (if-then-else)

Hay veces que se quiere ejecutar uno o mas comandos solo bajo ciertas condiciones. Las condiciones pueden ser, por ejemplo, la existencia (o no) de un archivo, si tal variable es igual a tal valor, etc.

El if-then-else se usa para estos casos. La sintaxis es


 if [ condicion ] 
 then
   ...
 else
   ...
 fi
En este ejemplo, el punto suspensivo (...) representa cualquier cantidad de comandos. El else es opcional. Veamos ahora el problema de especificar condiciones.
Condiciones
Las condiciones estan documentadas en el manual bajo el programa test; de hecho el corchete izquierdo ([) es sencillamente un link al programa test. Hay una gran cantidad de condiciones que se pueden especificar (haga man test para verlas todas):

Advertencia
Hay que dejar espacio en blanco entre el if y su corchete; entre los corchetes y su contenido y entre los argumentos del contenido de los corchetes. No funcionan los siguientes ejemplos:

 if[ -x arch]
 if [ n1=n2 ]
 if [ n1 = n2]
En general, el shell es exigente con los espacios en blanco.
Detalle
Se puede poner el then en la misma linea que el if, separado por punto y coma:

 if [ -f archivito ] ; then
  ....
 fi

Ir al principio


Ejecucion iterada (for-do-done

La ejecucion iterada o for nos permite ejecutar repetidamente una serie de comandos, variando sistematicamente uno de los elementos de esa ejecucion.

La sintaxis es

 
  for i in lista-de-valores
  do
    ....
  done

La i se llama la variable de lazo (loop variable). EL punto suspensivo representa cualquier cantidad de comandos. Estos son ejecutados sucesivamente avriando los valores de i, tomados de la lista. Por supuesto la variable de lazo se puede llamar cualquier cosa.

La lista se puede especificar de varias maneras:

 
 for k in 1 2 3 4 5  # especificacion directa de la lista
 for k in *.f        # como especificacion de archivos  
 for i in *.f code.c # combinacion

Por ejemplo,

code> 
 for i in 1 2 3 4 5 ; do 
    echo "i vale $i"
 done
Notese que hemos puesto el do en la misma linea que el for, separados por punto y coma. Esto produce:

 i vale 1
 i vale 2
 i vale 3
 i vale 4
 i vale 5
Notese que se puede redireccionar la salida del for completo:

  for i in 1 3 7 ; do
    ...
  done >archivo
Lo cual redirecciona todo a archivo.

Ir al principio


Ejecucion repetida condicionalmente (while)

El equivalente del conocido do while de varios lenguajes. Permite repetir comandos mientras valga una condicion.

La sintaxis del comando es:


 while [ condicion ]
 do
  ....
 done
Las condiciones son iguales que para el if (es decir, como en el caso de if, el corchete es el comando test.) Por ejemplo:

  p=2
  while [ p -lt 256 ] ; do 
    p=`expr $p + $p`
    echo $p
  done
Esto produce sucesivamente 4, 8, 16, 32, 64, 128, 256.

Ir al principio


Shell Scripts y Argumentos Posicionales

La anteriores figuras pueden ser emitidas "sobre la marcha", sencillamente tipeando comandos como lo hemos indicado. Por ejemplo:
 abulafia% for i in * ; do
  >
Notese en este punto que el prompt de abulafia es reemplazado por el signo >. Esto se llama el prompt secundario y quiere decir que hay una figura incompleta (es decir, falta algo). Para completar, hariamos por ejemplo
  > echo $i		# todavia incompleto
  > done		# listo
 (...)			# aqui va la salida
 abulafia%
Los dos prompts se controlan con las variables de shell PS1 y PS2. Por ejemplo, emitiendo

 PS1="abulafia>"
 PS2="..."
El porcentaje en el concido prompt de abulafia se transformaria en la "zanahoria" (PS1) y el prompt segundario seria el punto suspensivo.
Shell Scripts
Sin embargo, es a veces engorroso ingresar largas series de comandos. En este caso, las podemos ingresar a un archivo, y luego pedirle al shell que ejecute cada linea de ese archivo como si lo hubieramnos hecho desde el teclado. Estos archivos se llaman shell scripts o "guiones de shell", como guiones de teatro. (En este caso el "actor" que interpreta el "guion" es el shell".

El shell script se crea con cualquier editor y se le cambia luego el modo a "ejecutable" para indicarle al shell que es un guion. (Ver el comando chmod para cambiar el modo.

Un shell script puede tener argumentos como cualquier comando de Unix. Los argumentos se colocan en variables posicionales, llamadas $1, $2... $1. Por ejemplo, si miscript es un shell script que tiene este aspecto:


  echo "mis tres primeros argumentos son $1 $2 $3"
y ahora lo invocamos asi:
  abulafia% miscript alfa beta gamma
      mis tres primeros argumentos son alfa beta gamma 
Pueden usarse cualquier cantidad de argumentos, en grupos de 10. Es decir, inicialmente solo hay 10 argumentos. El comando shift causa que el onceavo argumento pase al primero, el doceavo al segundo.. etc. El argumento $0 siempre es el nombre del shell script. La variable $# nos da el numero de argumentos. Por ejemplo, creemos un shell script llamado scr2 que contiene estos comandos:
  echo "Este es el script $0"
  echo "Argumentos $1 $2 $3 $4 $5 $6 $7 $8 $9 $10"
  if [ $# -gt 10 ] ; then
    shift		# $1 <- $11 etc 
    echo "El onceavo argumento es $11"
  fi
Si invocamos a este script con mas de 10 argumentos por ejemplo:
 abulafia% scr2 1 2 3 4 5 6 7 8 9 10 11 12
   Este es el script scr2
   Argumentos 1 2 3 4 5 6 7 8 9 10
   El onceavo argumentos es 11
Funciones de shell
Las funciones de shell se parecen a los shell scripts, pero no residen en un archivo. Se definen una vez (por sesion login) y el shell se recuerda de ellos. La sintaxis es:

 nombre() { definicion; }
La definicion puede ser cualquier cantidad de comandos. Por ejemplo

 ver-tar(){ zcat $1 | tar tvf -; }
nos permite ver "tar comprimido" (lo cual veremos mas adelante **) (El uso seria por ejemplo "ver aaa.tar.Z").

La ventaja de una funcion de shell es que ahorra un archivo y algun tiempo de carga en comparacion con un shell script.

Ir al principio


La historia de comandos

El shell (ksh) guarda una historia de los N ultimos comandos ingresados por la consola. Estos se pueden recuperar, cambiar, y volver a ingresar. (Generalmente N = 128). Se reconocen dos opciones pertinentes:

  set -o emacs
  set -o vi
Estas dos opciones son mutuamente exclusivas. Para cambiar los comandos, se puede emular a vi o emular a emacs. Vease "man shell".

Ir al principio


Control de trabajo

Esto se refiere a poder correr multiples tareas concurrentemente. El ksh permite correr procesos en dos estados: foreground o interactivo y background o "en el fondo". (En realidad, lo que quiere decir esto es que el proceso no espera al terminal.) Cuando se inicia un trabajo con un comando, es interactivo. Un trabajo interactivo se puede pasr al fondo con la siguiente secuencia:
  abulafia%programa-largo    # arranque
    ^Z                                           # control-z
  [1] + Stopped
Es decir, ingresamos control-z (oprimir control sin soltarla oprime z; esto suspende al proceso programa-largo. Ahora, para que pueda seguir ejecutando,
  abulafia% bg
    [1] programa-largo & 
Esto manda el ultimo proceso suspendido al fondo, asignandole el numero de trabajo 1. Para ver que cosas tenemos corrriendo, usamos
  abulafia% jobs
    [1] Running programa-largo &
Por ultimo, podemos eliminar a cualquier "job" por medio del comando de shell kill (no es el mismo comando usado para matar procesos) en cuyo caso usamos el porcentaje para identificarlo:
  abulafia% kill  %1
    [1] Killed  programa-largo &
Si programa-largo termina por propia cuenta, el shell nos dara un mensaje con el identificador del job, que dice "Done".

Ir al principio


Ultima revisión: marzo 1998.
Autoría: Dr. Juan Rivero, Instituto Venezolano de Investigaciones Científicas (IVIC).
Se permite copia local, siempre y cuando se conserve esta autoría.