Programacin Shell en Linux
Programacin Shell en Linux
El turno ahora es para los intrpretes de comandos de Linux y su programacin, conocida como programacin shell. Aunque el intrprete de comandos es independiente del lenguaje de programacin utilizado, Linux permite crear programas utilizando caractersticas propias de cada un de los shell existentes. como, no hay solo un intrprete? Umm, nop; hay bastantes shells para Linux(o intrpretes de comandos o consolas o como lo llames [de ahora en adelante shell]). No como en windows que tan solo tiene el interprete de DOS (o cmd o command). Como habrs podido deducir la programacin en shell es interpretada, no compilada, lo que reduce el desempeo del sistema; pero la ventaja es la facilidad de creacin y mantencin. Los temas a ver sern los siguientes: Qu es un shell y Qu shells estn disponibles Principios de los shells (redireccin) Variables de entorno Procesamiento en segundo plano Completacin y sustitucin de comandos (sustitucin y creacin del alias) Secuencias de comandos de los shell
Qu es shell
Unos dicen que es el sistema operativo, otros dicen que es una pantalla negra sin sentido y anticuada, otros dicen que es la causa de que Linux no sea famosa entre gente inexperta. quien tiene la razn? Hagan sus apuestas!!! No, ahora en serio. NO es el sistema operativo (el sistema operativo es el software que dirige la computadora, habla con el hardware, carga y ejecuta programas, etc.); cuando se ve el indicador de la computadora y se escriben comandos para ejecutar, con lo que estamos tratando es con el shell. Una caracterstica interesante de Linux, es que los shells son completamente independientes. Como usuario tenemos la libertad de elegir entre uno u otro shell. Tambin es posible interactuar con un shell, y escribir comandos para otro.
uno con sus caractersticas propias, ventajas y desventajas. Los shells Bourne y el C fueron reescritos, como resultado ahora tenemos el Bourne again shell (Shell Bourne nuevamente) o bash, y el shell T (tcsh). Los tres shells estn disponibles en casi todas las distros de Linux. Bash es probablemente el shell ms utilizado actualmente, y es el que viene por defecto en la mayora de las distros.
Esta lnea se conoce como lnea de comandos; que consiste en un comando y una o ms opciones (y/o argumentos). Por lo general el espacio en blanco se ignora. En Linux los comandos son sensibles al uso de maysculas y minsculas, cosa que no pasa en Windows. El comando se termina pulsando la tecla Enter; aunque se puede continuar el comando en una nueva lnea usando backslash (\).
comandolargisisisisisisisimo opcion1 opcion2 opcion3 ... opcionN \ argumento1 argumento2 ... argumentoN
Adems es posible concatenar varios comandos, separndolos con punto y coma (;), por ejemplo:
clear; pwd; ls
Redireccin de E/S
Cuando se ejecuta un programa en Linux se abre automticamente tres archivos (flujos) de E/S para ellos. Estos son: la entrada estndar, la salida estndar y el error estndar. Aunque parezca confuso todos los sistemas UNIX utilizan este sistema, basado en el manejo de archivos. Por ejemplo, si deseas enviar datos a tu disco extraible debes enviar los datos al archivo asociado con dicho pendrive, por lo general /dev/sda1.
Por defecto la salida estndar est conectada a la pantalla, la entrada de estndar al teclado, y el error estndar a la pantalla. Es posible reasignar estos destinos antes de ejecutar el programa, en lo que se conoce como redireccin de E/S. Supongamos que queremos crear una lista de archivos del directorio /usr/include, pero que dichos archivos contengan la palabra #include. Una forma sencilla de hacer esto sera:
grep l #include /usr/include/*.h > ListaArchivos
grep comprobar los archivos de la carpeta /usr/lib cuya extensin sea *.h y determinar cuales de ellos contienen la palabra #include. El carcter > es el que indica la redireccin de salida; esto ocasiona que el shell redireccione la salida estndar a el archivo ListaArchivos. Los nombres que estn en el archivo se vern as:
/usr/include/GUI.h /usr/include/Float.h /usr/include/Redirect.h /usr/include/nvu.h /usr/include/bluefish.h
Para reemplazar, por ejemplo, la cadena /usr/include al principio de cada archivo puedes utilizar el comando sed:
sed 's#^/usr/include/##' < ListaArchivos > ListaArchivoss
El comando sed opera sobre los datos de entrada estndar, as que en este caso utilizamos el carcter < para redireccionar la entrada. En lugar de leer del teclado, esta vez leer de un archivo. Despus, la salida del comando sed se redirige hacia el archivo ListaArchivoss. Pero, cmo se redirige el error estndar? Para redirigir el error estndar se utiliza >&, por ejemplo:
sed 's#^/usr/include/##' < ListaArchivos >& ErrorSed > SalidaSed
Tuberas
Una forma relacionada con la redireccin se conoce como tubera. Partamos del ejemplo anterior; ya no solo quiero los archivos sino adems los quiero ordenados alfabticamente. Podriamos utilizar la redireccin as:
grep l #include /usr/include/*.h > ListaArchivos sort ListaArchivos > ListaOrdenada Importante!!! El comando sort ListaArchivos > ListaOrdenada en ningn momento utiliza redireccin de entrada. Algunos comandos como sort reciben directamente el nombre de un archivo como argumento, que es distinto a redireccionar la entrada; esto se conoce
como filtro.
Obviamente debe haber una manera ms eficiente de hacer sta operacin, es decir, sin usar dos comandos ni un archivo temporal. Por ejemplo:
grep l #include /usr/include/*.h | sort > ListaOrdenada
El carcter de tubera (|) encadena dos comandos y conecta (redirecciona) la salida estndar del primero, a la entrada del segundo. Una sola lnea de comandos puede tener cualquier nmero de tuberas:
grep l #include /usr/include/*.h | sort | sed 's#^/usr/include/##' > ListaModificadaOrdenada
Variables de entorno
Normalmente los programas utilizan variables para poder llevar a cabo determinadas acciones. Por ejemplo los editores como vi o emacs necesitan saber en que tipo de shell se etn ejecutando. Est informacin podra ser cargada mediante el uso de argumentos al momento de ejecutar un comando, pero dicha tarea sera ms que tediosa para el usuario, ya que se tendra que hacer cada vez que se ejecute el comando. Los shell solucionan estos problemas con las variables de entorno. Una variable de entorno es simplemente un par nombre/valor. El shell hace una lista de variables y las mantiene disponibles para cualquier programa que se ejecute sobre l. Existen dos tipos de variables: las variables normales de shell (variables locales), y las variables de entorno (variables globales). Para establecer una variable de entorno se utiliza el comando:
export NOMBRE=valor
Si el valor de la variable incluye espacios en blanco es posible encerrar dicho valor entre comillas dobles o sencillas, para evitar conflictos. Por ejemplo:
export JAVAPATH="/usr/lib/programas instalados/maquina virtual"
Tambin es posible agregar nuevos valores a una variable ya existente, para ello podemos utilizar la siguiente sintaxis:
export JAVAPATH="$JAVAPATH otro_valor"
Esto agrega la cadena "otro_valor" a la variable JAVAPATH. Puedes ver el valor de una variable con el comando echo, as:
echo $PATH
Tambin es posible ver las variables disponibles de una manera sencilla. Digitamos el
comando "echo $" y despus pulsamos dos veces la tacla TAB. Vers algo como esto:
ubuntu@ubuntu:~$ echo $ $_ $GDM_XSERVER_LOCATION $OSTYPE $BASH $GNOME_DESKTOP_SESSION_ID $PATH $bash205 $GNOME_KEYRING_SOCKET $PIPESTATUS $bash205b $GROUPS $PPID $bash3 $GTK_RC_FILES $PROMPT_COMMAND $BASH_ARGC $HISTCMD $PS1 $BASH_ARGV $HISTCONTROL $PS2 $BASH_COMMAND $HISTFILE $PS4 $BASH_COMPLETION $HISTFILESIZE $PWD $BASH_COMPLETION_DIR $HISTSIZE $RANDOM $BASH_LINENO $HOME $RUNNING_UNDER_GDM $BASH_SOURCE $HOSTNAME $SECONDS $BASH_SUBSHELL $HOSTTYPE $SESSION_MANAGER $BASH_VERSINFO $IFS $SHELL $BASH_VERSION $LANG $SHELLOPTS $COLORTERM $LESSCLOSE $SHLVL $COLUMNS $LESSOPEN $SSH_AGENT_PID $COMP_WORDBREAKS $LINENO $SSH_AUTH_SOCK $DBUS_SESSION_BUS_ADDRESS $LINES $TERM $DESKTOP_SESSION $LOGNAME $UID $DESKTOP_STARTUP_ID $LS_COLORS $USER $DIRSTACK $MACHTYPE $USERNAME $DISPLAY $MAILCHECK $WINDOWID $EUID $OPTERR $XAUTHORITY $GDMSESSION $OPTIND
actualizada constantemente por el shell y guarda el ltimo directorio referenciado por el comando cd.
El shell imprime "[1] 4647" y regresa inmediatamente, es decir, la shell queda lista para recibir comandos de nuevo. La salida indica que se est ejecutando una tarea en segundo plano, y que el PID (identificador de proceso) es 4647. En este caso el proceso es el nmero 1 en la cola de procesamiento en segundo plano. Si se ejecuta un proceso sin el comando & el shell espera a que dicha tarea termine antes de pedirte un nuevo comando. En ese caso se dice que la tarea se est ejecutando en primer plano. Si te vas a casar y en plena boda te das cuenta de que la ests cagando, lo ms probable es que ya no puedas hacer nada (condenado ests). Pero Linux es ms flexible que el matrimonio, si has ejecutado un comando en segundo plano y te arrepientes puedes traerlo (desde el mundo espectral) "al primer plano" con el comando fg. Si ya ests casado y quieres tomarte un descanso, conocer ms gente (mujeres) y dejar suspendido el matrimonio un rato, pues te jodes porque tu mujer de seguro no te deja. Linux piensa ms en ti que tu mujer, si ests ejecutando un comando en primer plano
puedes suspenderlo, sin eliminarlo completamente. Para ello debes oprimir "Ctrl+Z", con lo cual el proceso quedar suspendido:
debianita:/# find . name '*.so' print | sort > ListaOrdenada [1]+ Stopped find . name '*.so' print | sort >ListaOrdenada debianita:/#
Puedes "descongelar" el proceso y traerlo al primer plano con el comando fg, o descongelarlo pero dejarlo en segundo plano con el comando bg. Umm, si el matrimonio fuera como Linux... en fin. Ahora, por ltimo si quieres acabar con el matrimonio recurres al divorcio, pero despus viene la separacin de bienes, la demanda por alimentos, y tu mujer te deja en la calle. De nuevo, Linux piensa ms en ti y aunque lo utilices eres ms libre que "unas monjitas fujitivas". Si deseas eliminar un proceso completamente puedes utilizar el comando kill, indicando el PID del proceso (kill 4647), o por su lugar en la cola de procesamiento en segundo plano (kill %1). Si deseas saber que tareas se estn ejecutando en segundo plano puedes utilizar el comando jobs. La siguiente imagen muestra el comportamiento de una sesin de ejemplo, en la que se utilizan los comandos mencionados anteriormente.
El mismo mecanismo funciona si se est digitando el nombre de un archivo o directorio. Por ejemplo si escribes "ls /u" y presionas Tab, bash completar el comando y quedar as: "ls /usr/". El shell tambin permite otros mtodos para ahorrarse la escritura, por ejemplo los mecanismos de sustitucin. Se permite varios tipos de sustituciones.
Las letras a y z se combinan con cada una de las cadenas entre las llaves: primero con b, luego con c, luego con de y luego con fgh.
Este ejemplo ejecutar el comando find para localizar todos los archivos make que estn en el rbol de directorio /usr/src. La lista de archivos se presentar en la lnea de comandos a ls, el cual mostrar las entradas en el directorio de estos archivos.
Para invocar cualquier comando anterior, digita un signo de admiracin y el nmero del comando. Para repetir el comando echo $PATH (por ejemplo), escribe !7 as:
casidiablo@debianita:/media/documentos/archivos$ !7 echo $PATH /home/casidiablo/mono1.1.13.8/ bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11
Es posible repetir el ltimo comando con !!. Tambin puedes editar una lnea de comandos anterior antes de repetirla. Supongamos que hemos escrito el comando ls -l /USR/lib. Para corregir este comando y repetirlo, podriamos escribir ^USR^usr^. bash da por hecho que queremos editar el comando anterior y procede con la sustitucin, de ser posible.
Probablemente utilices con frecuencia ciertos comandos o secuencias de los mismos. Es posible crear nuestros propios comandos utilizando lo que se conoce como alias. El shell reemplazar el alias con su definicin. Por ejemplo, el comando ls lista los archivos y directorios de una carpeta. Con la opcin -a muestra tambin los archivos ocultos, y con la opcin -F aade un asteristo (*) a los archivos ejecutables y un slash (/) a los directorios. Puedes crear un alias para dicho comando de la siguiente manera:
casidiablo@debianita:~$ ls Desktop mono1.1.13.8 mono1.1.13.8_ 0installer. bin casidiablo@debianita:~$ ls a F ./ .gconf/ mono1.1.13.8/ ../ .gconfd/ mono1.1.13.8_ 0installer. bin* .bash_history .gimp2.2/ .mozilla/ .bash_logout .gksu.lock .nautilus/ .bash_profile .gnome/ .recentlyused .bashrc .gnome2/ .themes/ .bitrock/ .gnome2_private/ .thumbnails/ Desktop/ .gstreamer0.10/ .viminfo .dmrc .gtkrc1.2gnome2 .Xauthority .evolution/ .ICEauthority .xine/ .face .icons/ .xsessionerrors .fontconfig/ .metacity/ casidiablo@debianita:~$ alias lss="ls a F" casidiablo@debianita:~$ lss ./ .gconf/ mono1.1.13.8/ ../ .gconfd/ mono1.1.13.8_ 0installer. bin* .bash_history .gimp2.2/ .mozilla/ .bash_logout .gksu.lock .nautilus/ .bash_profile .gnome/ .recentlyused .bashrc .gnome2/ .themes/ .bitrock/ .gnome2_private/ .thumbnails/ Desktop/ .gstreamer0.10/ .viminfo .dmrc .gtkrc1.2gnome2 .Xauthority .evolution/ .ICEauthority .xine/
Ahora hemos creado un nuevo comando llamado lss que har la misma tarea de "ls -a -F". Tambin es posible sustituir un comando, por ejemplo, pudimos haber hecho: alias ls="ls -a -F", sin ningn problema.
Nota: Es necesario el uso de las comillas, ya que sin ellas el comando alias intentara usar el -a y el -F como una opcin de s mismo.
Variables
Ya hemos hablado sobre las variables del shell. Cuando se estn ejecutando una secuencia de comandos, ya estn definidas algunas variables tiles: $$: El identificador del proceso que se est ejecutando. $0: El nombre de la secuencia de comandos. $1 hasta $9: Los primeros 9 argumentos de lnea de comandos que se pasan a la secuencia de comandos. $#: El nmero de parmetros de lnea de comandos que se pasan a la secuencia de comandos.
Estructuras de control
bash soporta la mayora de las instrucciones de control utilizadas en los lenguajes de programacin comunes, aunque la sintaxis cambia un poquitn. Ahora vamos a ver un ejemplo de una secuencia de comandos en las que se demuestra el uso de casi todas las instrucciones de control disponibles en bash:
#!/bin/bash #el nombre de este archivo debe ser programa.sh MAX=9 #Uso de la instruccin if if [ $# gt $MAX ] then echo "$0: $MAX o menos argumentos requeridos" exit 1 fi #Imprimir los dos primeros argumentos echo "$0 : El argumento 1 es $1" echo "$0 : El argumento 2 es $2" echo "" #Uso del for for i in $1 $2 do ls l $i; done echo "" #Uso de la intruccin case echo "ejemplo case" for i do case "$i" in archivo1) echo "caso a";; archivo2) echo "caso b";; *) echo "Este es el famoso default: $i";; esac done echo "" #Uso de la instruccin whiledone echo "ejemplo whiledone" i=1; #mientras que $i sea menor al nmero de argumentos while [ $i le $# ] do echo $i; i=$[$i+1]; done echo ""; #Uso de la instruccin untildone echo "ejemplo until" i=1; #hasta que $i sea mayor al nmero de argumentos
until [ $i gt $# ] do echo "$i argumentos se balanceaban sobre la tela de una araa"; i=$[$i+1]; done echo "" exit 0
La lnea "MAX=9" establece una variable llamada MAX y le asigna el valor entero 9. Luego, el bloque if-fi comprueba si el nmero de argumentos es mayor de 9, en cuyo caso imprime un mensaje de error y aborta el programa. Las instrucciones echo se utilizan para imprimir en pantalla. El shell entiende que cuando se presenta algo como: echo "algo $1", no debe imprimir "algo $1" sino "algo primerargumento", es decir, reconoce las variables dentro de cadenas de texto (como perl o php). Las siguientes agrupaciones de comandos muestran el uso de las instrucciones de control disponibles en shell. La primera de ellas es el for-done (equivalente a for en C/C++ o Java). Es interesante ver que adems de imprimir valores con echo, es posible utilizar comandos del shell, en este caso se toman los dos primeros argumentos y se utilizan para completar el comando ls -l. Despus, en el uso del case (equivalente al switch-case de C o Java), podemos observar el uso de esta instruccin de seleccin. En el ejemplo de while, se imprime la cantidad de argumentos proporcionados al programa; igualmente actua la instruccin until. Fjate tambin que es posible utilizar comentarios con el signo numeral (#, almohadilla para los espaoletes). Un ejemplo completo para la utilizacin de este programa sera:
casidiablo# touch archivo1 archivo2 casidiablo# ls F archivo1 archivo2 programa.sh(*) casidiablo# ./programa.sh archivo1 archivo2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 ./programa.sh: 9 o menos argumentos requeridos casidiablo# ./programa.sh archivo1 archivo2 arg3 arg4 arg5 ./programa.sh : El argumento 1 es archivo1 ./programa.sh : El argumento 2 es archivo2 rwrr1 casidiablo casidiablo 0 20070210
14:16 archivo1 rwrr1 casidiablo casidiablo 0 20070210 14:16 archivo2 ejemplo case caso a caso b Este es el famoso default: arg3 Este es el famoso default: arg4 Este es el famoso default: arg5 ejemplo whiledone 1 2 3 4 5 ejemplo until 1 argumentos se balanceaban sobre la tela de una araa 2 argumentos se balanceaban sobre la tela de una araa 3 argumentos se balanceaban sobre la tela de una araa 4 argumentos se balanceaban sobre la tela de una araa 5 argumentos se balanceaban sobre la tela de una araa casidiablo#
Primero creamos dos archivos vacios con el comando "touch archivo1 archivo2". Con el comando ls -F comprobamos que existen dichos archivos ms el programa (programa.sh). Puedes probar pasarle ms de 9 argumentos, para comprobar que el if que colocamos el principio de verdad funciona. Luego se invoca el programa con 5 argumentos, los dos primeros son los nombres de los archivos creados antes.