Patricio Poblete (ppoblete@dcc.uchile.cl) |
El shell es el programa que interpreta los comandos que el usuario tipea interactivamente. También puede procesar comandos pre-almacenados en un archivo y tiene instrucciones que lo transforaman en un lenguaje de programación.
En primer lugar analizaremos diversos ejemplos de uso interactivo del shell:
$ who $ date; who $ (date; who) | wc $ date; who | wc $ (sleep 10; echo Ya pasaron 10 segundos) & $ echo hola >x $ >x echo hola $ echo * # * no calza con nombres que empiezan por "." $ echo \* $ echo '*' $ echo x*y # nombres de la forma x...y
> redirecciones < >> | pipe < "here doc" ... FIN * secuencia de 0 o más caracteres cualquiera (en nombres de archivos) ? un caracter cualquiera [ccc] clase de caracteres, también 0-9, a-z; terminador & como ";" pero no espera `cmd` ejecuta cmd y reemplaza el output en este lugar (sin newlines) (...) ejecuta en un sub-shell $0, ..., $9 argumentos del Shell ($0 es el nombre del comando) $var, ${var} valor de una variable \c tomar caracter c literalmente '...' tomar literalmente "..." similar, pero interpreta $, `` y \ # comentario var=valor asignación cmd1 && cmd2 ejecuta cmd2 sólo si cmd1 terminó exitosamente cmd1 || cmd2 ejecuta cmd2 sólo si cmd1 falló
nu.sh |
who | wc -l
Uso:
$ sh <nu.sh $ sh nu.sh $ chmod +x nu.sh $ ./nu.sh
Para poder ejecutarlo como comando (sin ./) deberíamos ponerlo en ~/bin.
Argumentos:
cx1.sh |
chmod +x $1
Uso: ./cx1.sh archivo
cx2.sh |
chmod +x $*
Uso: ./cx2.sh archivo1 archivo2 ...
Uso de `...`
$ mail `cat amigos`
Variables
$ echo $PATH $ PATH=$PATH:/otro/directorio
Variables creadas por el usuario
$ pwd $ dir=`pwd` $ cd /tmp $ more $dir/x $ set # muestra todas las variables $ export var # para que sea visible en los sub-shells
Existe un comando "." que lee y ejecuta un archivo de comandos como si hubiese sido tipeado (no como sub-shell).
ejemplo.sh |
cd /tmp echo hola
Usando ese archivo:
$ cat ejemplo.sh $ . ejemplo.sh $ pwd
Redirecciones
>archivo >>archivo <archivo cmd1 | cmd2 2>archivo redirige fd 2 2>>archivo 2>&1 mezcla fd 2 con fd 1 4<&0 < "here doc" ... FIN <<'FIN' "here doc" sin sustituciones ... FIN
for var in lista de palabras do comandos done
Ejemplo:
$ for i in * > do > echo $i > done
$ cal october 2001 # error $ cal 10 2001 # OK
Hagamos nuestro propio comando cal más flexible:
cal |
# cal - interfaz más agradable para /usr/bin/cal case $# in # $# es el número de argumentos # Thu Oct 11 09:59:36 CLT 2001 0) set `date`; m=$2; y=$6;; # set define $1, $2, etc. 1) m=$1; set `date`; y=$6;; # un argumento ==> año *) m=$1; y=$2;; # dos args ==> mes y año esac case $m in jan*|Jan*) m=1;; feb*|Feb*) m=2;; mar*|Mar*) m=3;; apr*|Apr*) m=4;; may|May) m=5;; jun*|Jun*) m=6;; jul*|Jul*) m=7;; aug*|Aug*) m=8;; sep*|Sep*) m=9;; oct*|Oct*) m=10;; nov*|Nov*) m=11;; dec*|Dec*) m=12;; [1-9]|10|11|12) ;; *) y=$m; m="";; # sólo año esac /usr/bin/cal $m $y # ejecutamos el verdadero cal
Variables built-in del shell
$# número de argumentos $* todos los argumentos $- opciones $? valor retornado por el último comando $$ pid del shell $! pid del último comando iniciado con & $HOME default para cd $IFS separador de argumentos $PATH lista de directorios $PS1 prompt 1 $PS2 prompt 2
if cmd the cmds else cmds fi
En el if es frecuente usar el comando test:
saluda |
if test "$1" = hola then echo "Hola!" else echo "???" fi
Ejemplo: Comando which
mywhich |
# which - busca comando en PATH case $# in 0) echo "Use: which comando"; exit 2 esac for i in `echo $PATH|sed 's/:/ /g'` do if test -f $i/$1 then echo $i/$1 exit 0 # lo encontramos fi done exit 1 # no se encontró
¡Ojo! Este comando puede ser saboteado instalando un falso comando test en el PATH. Esto se puede prevenir haciendo:
mywhich2 |
# which - busca comando en PATH oldpath=$PATH PATH=/bin:/usr/bin case $# in 0) echo "Use: which comando"; exit 2 esac for i in `echo $oldpath|sed 's/:/ /g'` do if test -f $i/$1 then echo $i/$1 exit 0 # lo encontramos fi done exit 1 # no se encontró
Dos maneras de esperar a un usuario:
while sleep 60 # siempre retorna OK (0) do who | grep juan done
until who|grep juan # retorna 0 (verdadero) si encuentra algo do sleep 60 done
Empaquetaremos la segunda versión como un comando watchfor:
watchfor |
# watchfor - vigila si un usuario se conecta PATH=/bin/:/usr/bin case $# in 0) echo "Use: watchfor persona"; exit 1 esac until who|grep "$1" do sleep 60 done
Nota: Se puede decir "watchfor 'juan|maria'" y funciona.
watchwho |
# watchwho - vigila usuarios que llegan y se van PATH=/bin:/usr/bin new=/tmp/ww1.$$ old=/tmp/ww2.$$ >$old # crea archivo vacío while true do who >$new diff $old $new mv $new $old sleep 60 done
La salida se puede mejorar un poco agregando un post-proceso:
watchwho2 |
# watchwho - vigila usuarios que llegan y se van PATH=/bin:/usr/bin new=/tmp/ww1.$$ old=/tmp/ww2.$$ >$old # crea archivo vacío while true do who >$new diff $old $new mv $new $old sleep 60 done | awk '/>/{$1="login: "; print} /</{$1="logout: "; print}'
trap 'comandos' señales
0 shell exit 1 hangup 2 interrupt (^C) 3 quit (^\) 9 kill 15 terminate
Ejemplo: limpiar archivos temporales de whatchwho
trap 'rm -f $new $old; exit 1' 1 2 15
Para proteger a un programa del hangup:
(trap '' 1; comando) &
Esto se puede empaquetar en un comando nohup:
nohup |
trap '' 1 15 exec nice -5 $*