Muchos tutoriales e introducciones sobre la bash hablan acerca de como utilizar los alias. Desafortunadamente la mayoria de ellos no cubren las funciones. Esto es una verdadera perdida, pués las funciones ofrecen muchas funcionalidades que los alias no proporcionan.
Los alias son simples substituciones de cadenas de caractéres. El intérprete de comandos ( shell ) toma la primera palabra de un comando y la compara con la lista actual de alias. Además, si el último caracter de un alias es un espacio en blanco, mira, también, en la siguiente palabra. Por ejemplo :
$ alias 1='echo '
$ alias 2='Esto es un alias'
$ 1 2
Esto es un alias
$
Los alias no permiten control de flujo, argumentos de la linea de comandos, u opciones adicionales que hacen la linea de comandos tan util. De forma adicional, las reglas relativas a la expansión de los alias son un poco espinosas, tanto, que la página de manual de bash(1) recomienda: "tome precauciones, ponga siempre las definiciones de alias en lineas separadas, y no utilice alias en comandos compuestos"
Las funciones, en si mismas, son, en realidad, guiones completos que proporcionan toda la flexibilidad y capacidad que esto implica.
Se pueden crear funciones de diferentes maneras. Puede, simplemente, introducirla en un archivo y referencie el archivo con el comando '.' ( bien desde la linea de comandos, bien desde tus propios guiones ). Puede, incluso, ejecutar simplemente la función desde la linea de comandos. Una función es accesible exclusivamente desde la sesión en la que se ha hecho disponible mediante alguno de estos dos métodos ( o si ha sido heredada de un intérprete de comandos padre ).
Para crear una función desde la linea de comandos tendríamos que hacer algo como esto:
$ gla() {
> ls -la | grep $1
> }
Esta función es muy sencilla, y podría ser implementada también como un alias. ( Hay varias razones por las que no te apetecerá hacer esto, lo veremos más adelante. ) Lo escrito, hace un largo listado del directorio local y busca cualquier coincidencia con el primer argumento. Podría ser más interesante pasarselo a awk para encontrar cualquier archivo coincidente cuyo tamaño fuese mayor de 1024 octetos. Sería algo asi:
$ gla() {
> ls -la | grep $1 | awk ' { if ( $5 > 1024 ) print $0 } '
> }
Esto último no puede hacerse con un alias, no se puede reemplazar, simplemente, gla por 'ls -la | grep'. Al estar escrito como función, no es un problema utilizar $1 ( el primer argumento de gla ) en cualquier parte del cuerpo de tus comandos.
Para un ejemplo mayor ( bien, de acuerdo es bastante más largo ), suponga que esta trabajando en dos proyectos con dos repositorios CVS diferentes. Tal vez quiera ser capaz de escribir una función que le permita establecer las variables CVSROOT y CVS_ROOT apropiadas, o limpiar cualquier valor de estas variables al pasarle el argumento 'unset'. Sería interesante, también, si ejecutase 'cvs update', por usted, al pasarle el argumento 'update'. Con alias, se puede aproximar a esto, pero solo ejecutando multiples alias desde la linea de comandos. Mientras que con las funciones, podría crear un archivo de texto que incluyese lo siguiente: ( versión en texto )
setcvs() {
export done="No"
if [ "$1" = "unset" ]
# Queremos limpiar todas las variables
then
echo -n "Limpiando las variables relacionadas con cvs: "
export CVSROOT=""
export CVS_RSH=""
export done="Si"
echo "Hecho"
fi
if ( pwd | grep projects/reporting > /dev/null && \
[ "$done" != "Si" ] )
# Si estamos en el area de información, y aún no lo hemos hecho
then
echo -n "Estableciendo cvs para el proyecto de información: "
export CVSROOT="issdata:/usr/local/cvs/"
export CVS_RSH="ssh"
export done="Si"
echo "Hecho"
fi
if ( pwd | grep projects/nightly > /dev/null && \
[ "$done" != "si" ] )
# Si estamos en el area nocturna, y aún no lo hemos hecho
then
echo -n "Estableciendo cvs para el proyecto nocturno: "
export CVSROOT="/home/cvs/"
export done="si"
echo "Hecho"
fi
if [ "$1" = "update" ]
# Queremos actualizar el arbol actual desde el servidor cvs despues de
# establecer las variables apropiadas
then
if [ -z "$CVSROOT" ]
# Si $CVSROOT tiene longitud cero ( ya ha sido limpiada
# o aún no ha sido establecida ) emite un error y no hace nada
then
echo "Variables cvs no establecidas ... compruebe su cwd e intentelo de nuevo"
elif [ -n "$CVSROOT" ]
# Si no existe $CVSROOT intenta y realiza la actualización
then
echo "Actualizando el arbol local: "
cvs -q update
echo "Hecho"
fi
fi
}
Despues, se puede habilitar y utilizar la función de esta manera:
$ . ~/scripts/setcvs
$ cd
$ pwd
/home/a257455
$ setcvs unset
Limpiando las variables relacionadas con cvs: Hecho
$ echo $CVSROOT
$ echo $CVS_RSH
$ cd projects/reporting/htdocs/
$ setcvs
Estableciendo cvs para el proyecto de información: Hecho
$ echo $CVSROOT
issdata:/usr/local/cvs/
$ echo $CVS_RSH
ssh
$ cd ../../nightly/
$ setcvs
Estableciendo cvs para el proyecto nocturno: Hecho
$ setcvs update
Setting up cvs for nightly project: Hecho
Actualizando el arbol local: Hecho
$ cd
$ setcvs unset
Clearing cvs related variables: Hecho
$ setcvs update
Variables cvs no establecidas ... compruebe su cwd e intentelo de nuevo
$
Las funciones pueden hacer mucho más que los alias, la función que acabamos de ver muestra un poco de control de flujo, algo de control de errores, y la posibilidad de utilizar variables. Otra gran ventaja es que las funciones pueden reutilizarse en los guiones, mientras que los alias no. Por ejemplo, se puede escribir un guión como el siguiente al tener la función anterior guardada en el archivo '~/scripts/setcvs':
#!/bin/bash
# Guión de ejemplo
# Referenciamos la función
. ~/scripts/setcvs
# Vamos a los directorios de proyecto y los actualizamos desde el cvs
cd ~/projects/reporting/htdocs
setcvs update
cd -
cd ~/projects/nightly
setcvs update
# Volvemos a donde estabamos y limpiamos las variables relacionadas con el cvs.
cd -
setcvs unset
Los alias son muy utiles, pero espero que despues de esta introducción, encuentre las funciones al menos igual de interesantes ( e incluso probablemente más útiles ) Una advertencia final sobre los alias y las funciones, es que nunca debería reemplazar un comando estandar por un alias o una función. Es demasiado facil hacerse daño intentando ejecutar tus alias cuando no existen. Imagine la diferencia entre:
$ alias rm='rm -i'
$ cd ~/scratch
$ rm * # aqui el alias rm interactivamente te permite
# eliminar el contenido del directorio actual
$ su -
# cd /tmp
# rm # aqui el alias rm no existe, y lanzas
# un montón de cosas fuera de /tmp
Feliz hacking!
-pate