|
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 $*