Bash es un intérprete de comandos o
lanza programas, que
funciona como idioma de
programación y está integrado con los sistemas Linux por
defecto como parte indisoluble.
Tiene la ventaja de que su uso es mas simple que otros sistemas de
programación y cualquiera persona con
un poco de interés y algunos rudimentos de Bash puede escribir
guiones que harán obedecer la PC en muchos campos. Su modo
de
operar es como "humanoide" es decir casi puede "conversarse" con la
máquina tal y como si se hiciera con otra persona al escribir el
guión, lo que lo convierte en el idioma de programación
de los "iniciados" en este mundo, aunque este mismo modo de
comunicación se utiliza muy parecido en otros idiomas de
programación populares.
Su desventaja principal es que funciona a baja velocidad comparado con
otros idiomas como C por ejemplo, por lo que para programas complejos
no es apropiado.
Historia
Bash tiene su origen en la Shell de Unix, o Bourne Shell pero como esta
Shell de Unix,
era software propietario no podía utilizarse libremente.
Un día el movimiento mundial de software
libre (free
software)
lo adoptó como propia y comenzó a reescribirla y a
incorporarle nuevas funciones y capacidades en lo que se conoce como
Bash (Bourne again shell) que traducido al
Español es algo así como "de nuevo la shell de Bourne".
El Bash sigue siendo completamente compatible con sus predecesores y se
desarrolla cada día mas, hasta ser un excelente aliado para el
que quiere elaborar sus propios guiones.
Elementos de Bash
Antes de empezar
Antes de empezar a tratar de aprender algo de Bash hay que saber algo
de Linux, no pueden elaborarse
guiones sin tener un conocimiento
esencial integral del sistema
operativo Linux, así es que
si va a intentar hacer sus primeros "experimentos" como "guionero"
primero necesita entender como es el sistema y su construcción
modular de multi-programas integrados que se interrelacionan formando
una potente herramienta de trabajo. Algunas "horas de vuelo" sentado en
una PC con Linux le ayudarán mucho, especialmente si está
migrando de Windows,
cuyas posibilidades reales de interacción
son ridículas.
En este articulo se utiliza mucho los términos proceso y
programa,
por eso es bueno aclarar brevemente sus significados.
Un programa es un archivo o conjunto de archivos que contienen
código
ejecutable y datos, el programa normalmente esta ubicado en un disco.
Se denomina proceso a un programa en ejecución, normalmente
esta ubicado en memoria.
Puede suceder que el mismo programa se ejecute varias veces
simultáneamente,
produciendo varios procesos.
Por ejemplo supongamos dos usuarios en un sistema Linux , user1
y user2, ambos están utilizando el programa ls, user1
esta
listando sus archivos (ls /home/user1) y el user2 también (ls
/home/user2).
Ambos utilizan el mismo programa pero generan dos procesos distintos.
Cada proceso tiene necesariamente un número de identidad con el
cual el Kernel trabaja.
La consola
En Linux todo puede hacerse usando una consola de texto, incluyendo por
supuesto el lanzamiento de programas, muchos de los cuales no tienen
ninguna interfaz gráfica, no obstante en el escritorio (desktop)
todas las distribuciones de Linux tiene una o mas consolas con interfaz
gráfica que emulan la consola principal y permiten al usuario
normal utilizar este servicio de manera mas fácil y amistosa. La
utilización de la consola para obtener información sobre
la disponibilidad de programas o su lanzamiento, sus opciones
así como la prueba
del guión mismo que se escribe serán en este mundo "el
pan de cada día". esta consola representa la shell.
El comando
Un comando es comúnmente un texto escrito en la consola o en un
guión que el sistema operativo buscará como ejecutable
dentro de los directorios apropiados ($PATH)
y lo
ejecutará, si el comando es verdadero (corresponde a un
ejecutable) la consola mostrará la salida de la
ejecución, de lo contrario imprimirá el texto "comand not
found" (no encuentro el comando).
La linea de comandos.
La linea de comandos es una linea de texto escrita de forma
determinada en la consola o el guión, que puede
ser interpretada por uno o mas programas en particular para ejecutar un
trabajo
determinado de acuerdo al contenido de la linea. Las lineas de comandos
generalmente comienzan con el nombre del programa que ejecutará
la primera acción solicitada. Casi siempre el programa se invoca
utilizando el nombre del programa y puede ser seguido por una o mas
opciones propias del programa que varían de acuerdo al
diseño seguido por el
programador, esto puede ser variable, no obstante hay una receta
bastante generalizada que se ha
convertido en una suerte de estándar en los programas de Linux,
esta es:
"nombre del
programa" "opciones" "archivo de entrada"
"archivo de salida"
El primer argumento del comando invoca el programa en cuestión
que ejecutará la acción, el segundo le indica al programa
alguna forma de ejecución particular de la acción y los
dos últimos le indican al programa cual archivo utilizará
como "materia prima" y donde debe colocar el "trabajo terminado".
Repetimos que no es una receta única pero puede servir de
ejemplo bastante usual.
La linea de comandos puede ser mas compleja e involucrar varios
programas en una acción final solicitada.
En una misma linea de comandos pueden ejecutarse varios comandos si se
separan con el carácter (;)
y su ejecución será consecutiva, tal y cual se hubieran
escrito en lineas diferentes.
Por lo general las opciones van precedidas por uno o dos guiónes
(-) y una buena parte de los programas aceptan dos variantes de la
opción, la corta (una letra) y la larga (una palabra),
el clásico ejemplo es el de como indicarle e un programa que nos
muestre la ayuda, casi siempre se obtiene el mismo efecto cuando se
escribe como opción -h, o --help.
Así tenemos que si usted en la consola escribe:
"sox -h", obtendrá el mismo efecto que si escribe "sox --help" .
Siempre hay que dejar un
espacio entre cada una de las partes que conforman la linea de
comandos. Bash interpreta este espacio vacío como el fin de una
cosa y el comienzo de otra por defecto.
Esta ayuda es lo primero que debe usted buscar para saber como invocar
el programa y que opciones tiene a su disposición. Una ayuda mas
completa casi siempre se obtiene si escribe en la consola "man
nombre del programa", se imprimirá a la pantalla el
manual. También algunos programas tienen la opción
--longhelp que imprime una ayuda mas elaborada.
En Linux existen una gran variedad de programas que se instalan por
defecto y otros que pueden instalarse adicionalmente que conforman un
amplio arsenal para casi cualquier cosa que necesite hacer en su
guión.
Un vistazo al comando "info coreutils" corrido en la consola le
dará una muestra de algunos.
El guión
Un guión (script) para bash es un archivo de texto que
contiene
una sucesión de comandos de Shell que pueden ejecutar diversas
tareas de acuerdo al contenido del texto del guión. De esta
forma pueden automatizarse muchas acciones para alguna
necesidad particular o para la administración de sistemas. El
guión debe escribirse en un orden lógico pues Bash
ejecutará el guión en el orden en que se escriben las
lineas, de la misma forma que cuando se realiza una tarea cualquiera
por una persona, por ejemplo; primero hay que poner la escalera y luego
subirse.
Los guiones de Bash deben tener siempre como primera linea del
guión el texto:
#!/bin/bash
Esto indicará al sistema operativo que debe ejecutar la
acción usando el programa Bash.
Una vez escrito el guión y guardado en el disco en alguno de los
directorios "bin" con el nombre y permiso de
ejecución apropiados, se invoca, escribiendo en la consola
el
nombre del guión.
Si el guión tiene una interfaz gráfica se invoca como
otro programa cualquiera, uno o dos clic sobre el guión o su
icono.
El guión puede escribirse en cualquiera de los editores de texto
de Linux, por ejemplo Kwrite y será ya un guión funcional
cuando se salve a alguno de los "bin". Es buena práctica cuando
se escribe un guión salvarlo apenas se hayan escrito las
primeras línea para ir comprobando su funcionamiento e ir
corrigiendo los problemas.
Variables
Es impensable elaborar guiones de Bash sin el uso de las
variables. Una variable es una estructura de texto (una letra, un
número o sucesiones de ellos) que representa
alguno de los elementos que varían en valor y/o significado en
el entorno de la Shell, sirviendo como elemento básico de
entrada/salida de valores a y desde los comandos en su ejecución
consecutiva. Para invocar una variable se utiliza el carácter
especial $ precediendo al
nombre de la variable.
Hay dos tipos de variables:
1.- Variables intrínsecas de Bash.
2.- Variables creadas por el programador.
Las variables intrínsecas son elaboradas por defecto por el
propio Bash
y son:
$0----------- Nombre del
guión
$1....$n ----Variables
que almacenan los n argumentos (opciones)
proporcionados al comando.
$# ----------Variable que
contiene el total de los argumentos
proporcionados.
$* -----------Conjunto
de los argumentos.
$? -----------Valor de
ejecución del comando anterior, si es
cero es que el comando anterior se
ejecutó sin errores, de lo contrario hubo algún error.
$$----------- Identifica
el proceso del guión.
$!------------ Identifica
el último proceso arrancado en el
trasfondo (background).
Las variables pueden ser creadas en cualquier momento, pero siempre
antes de su utilización de manera muy simple, se escribe:
nombre_variable=valor_variable
en cualquier momento posterior a la creación si se coloca $nombre_variable dentro del entorno
de la Shell el sistema colocará allí valor_variable,
ejemplo.
Variable
SALUDO=Bienvenido
En cualquier momento posterior si se pone $SALUDO, Bash
colocará
ahí Bienvenido.
Una variable también puede ser la salida de un comando si
envolvemos este en el carácter (`)
así por ejemplo el texto:
SALIDA=`comando`
le indicará al sistema que donde se escriba $SALIDA
debe poner la salida de
ese comando.
Es práctica común utilizar mayúsculas para las
variables a fin de identificarlas fácilmente dentro del
guión.
Cuando se ejecutan guiones que pueden ser "hijos" de otro guión
en ocasiones es necesario exportar las variables, esto se hace
escribiendo;
export
nombre_variable
Caracteres especiales
Existe un grupo de caracteres especiales (también llamados meta
caracteres) que tienen significado propio
para Bash. Algunos son:
\ ------ Le
indica
a Bash que ignore el carácter especial que
viene después.
" " -----Cuando se
encierra entre comillas dobles un texto o una
variables si esta es una
frase (cadena de palabras) Bash lo interpretará como una cadena
única.
$
------Identifica que
lo que le sigue es una variable.
' ' ----- Las
comillas
simples se usan para desactivar todos los caracteres
especiales
encerrados dentro de ellas, así tenemos
que si
escribe '$VARIABLE' Bash interpreta literalmente lo escrito y no como
variable.
# ----- Cuando se
coloca
este carácter dentro de una linea del guión, Bash
ignora el resto de la linea. Muy útil para hacer comentarios y
anotaciones o para inhabilitar una linea de comandos al hacer pruebas.
; ------
Este carácter se usa para separar la ejecución de
distintos comandos en una misma linea de comandos.
`` ----- Se
utiliza como se explicó en el punto anterior, para convertir la
salida de un comando en una variable. El comando en cuestión se
ejecuta en una sub shell.
También están |, (), !,
>, <, cuyo
significado se verá mas adelante. El espacio es otro
carácter especial y se interpreta por bash como el separador del
nombre del programa y las opciones dentro de la linea de comandos, por
esta razón es importante encerrar entre comillas dobles el texto
o las propias variables cuando son una frase de varias palabras.
Otro carácter que debe evitarse en lo
posible su uso es el guión (-) ya que para la mayoría de
los programas se usa para indicarle al propio programa que lo que sigue
es una de sus opciones, de manera tal por ejemplo, si usted crea un
archivo con nombre -archivo (en caso que pueda) después
será difícil borrarlo ya que rm (programa que borra)
tratará el archivo como una de sus opciones (al "ver" el
guión) y dará de error algo así,
"Opción -archivo no se reconoce".
Palabras especiales
Hay un grupo de palabras que tienen significado especial para bash y
que siempre que se pueda deben evitarse cuando se escriben lineas de
comandos para no crearle "confusiones" algunas son: exit, break,
continue, true, false, return etc... cuyo significado es mas o menos
así:
exit ---------------Se sale del guión
break -------------Se manda explícitamente a salir de un ciclo
continue ---------Se manda explícitamente a retornar en un ciclo
return ------------Como exit pero solo se sale del comando u
operación sin cerrar el guión
true ---------------Indica que una condición es verdadera
false --------------Indica que una condición es falsa
Argumentos propios
de bash*
Bash como programa tiene algunos argumentos útiles y propios que
se usan con frecuencia en la elaboración de guiones en los
condicionales vinculados a la
determinación de elementos sobre los archivos, variables,
cadenas de palabras o cadenas de pruebas, los
mas comunes son:
Argumentos
de Archivos
|
Cierto
si.... (salida 0)
|
-d
|
Archivo
existe y es un directorio
|
-c
|
Archivo
existe y es de caracteres
|
-e
|
Archivo
existe
|
-h
|
Archivo
existe y es un vínculo simbólico
|
-s
|
Archivo
existe y no está vacío
|
-f
|
Archivo
existe y es normal |
-r
|
Tienes
permiso de lectura del archivo |
-w
|
Tienes
permiso de escritura en el archivo |
-x
|
Tienes
permiso de ejecución del archivo |
-O
|
Eres
propietario del archivo |
-G
|
Perteneces
al grupo que tiene acceso al archivo |
-n
|
Variable
existe y no es nula
|
Archivo1
nt Archivo2
|
Archivo1
es mas nuevo que Archivo2
|
Archivo1
-ot Archivo2
|
Archivo1
es mas viejo que Archivo2
|
Agumentos
de cadenas
|
Cierto
si
|
-z
|
La
cadena está vacía
|
-n
|
La
cadena no está vacía
|
cadena1
= cadena2
|
Si las
cadenas son iguales
|
cadena1
!= cadena2
|
Si las
cadenas son diferentes
|
cadena1
< cadena2
|
Si la
cadena 1 va antes en el orden lexicográfico
|
cadena1
>cadena2
|
Si la
cadena 1 va despues en el orden lexicográfico
|
* En realidad estos argumentos cuando se encuentran entre [ ],
son un atajo al argumento correspondiente del programa test, que es un
programa independiente pero que ha sido incorporado al propio Bash.
Entrada/salida
En algunas ocasiones será necesario leer ciertas variables desde
el teclado o imprimirlas a la pantalla, para imprimir a la pantalla se
pueden invocar dos programas en la linea de comandos:
echo
printf
(que es un echo mejorado)
y para leer desde el teclado se usa:
read
si hacemos un read
sin asignar variable, el dato de almacena
en $REPLY una variable del sistema. Tanto el comando echo como read
tienen sus propias opciones.
Ejemplos:
1.-Si creamos en una linea del guión una variable como un
comando y queremos imprimir la variable a la pantalla podemos hacer
algo así
VARIABLE=`comando`
echo "$VARIABLE"
La palabra $VARIABLE está
puesta entre comillas dobles
para que se imprima todo el texto ignorando los espacios entre palabras.
2.-
Si escribimos en una linea del guión
read PREGUNTA
habremos creado una variable de nombre PREGUNTA así
es que si luego ponemos
echo
"$PREGUNTA"
se imprimirá a la pantalla lo que se escribió en el
teclado al presionar la tecla Enter.
Con los elementos tratados hasta aquí ya podemos escribir
nuestros primeros guiónes
Guión 1
#!/bin/bash
echo Hola
mundo
Cuando se corre este guión se imprimirá a la
pantalla Hola mundo
Guión 2
Lo mismo usando una variable
#!/bin/bash
VARIABLE=Hola
mundo
echo
"$VARIABLE"
Nótese la variable entre comillas dobles para que
imprima todo el texto.
Guión 3
Cuando se usan mas de una variable
#!/bin/bash
VARIABLE=Hola
SALUDO=mundo
echo
"$VARIABLE""$SALUDO"
En los tres casos se imprimirá a la pantalla Hola
mundo
Guión 4
Si se usan caracteres especiales la cosa puede cambiar
#!/bin/bash
VAR=auto
echo "Me
compré un $VAR"---------------Imprimirá Me compré
un auto
echo 'Me
compré un $VAR' ---------------Imprimirá Me compré
un $VAR
echo "Me
compré un \$VAR"--------------Imprimirá Me
compré un $VAR
Note como las comillas simples y el carácter \ hacen que Bash
ignore la función del carácter especial $. Siempre las
comillas simples harán que se ignore todos los meta caracteres
encerrados entre ellas y \ solo el que sigue después.
Condicionales
Los condicionales son claves para "explicarle" a la máquina como
debe proceder en una tarea cualquiera, esto se hace casi como si
se estuviera explicando una tarea a
ejecutar a otra persona.
El condicional por excelencia tiene seis palabras claves que son if, elif, else, then y fi.
Donde las palabras tienen un significado comunicativo (en
Inglés) casi literal, tal y cual se tratara con otra persona y
que Bash por defecto las entienda con ese significado.
if
---si
condicional (de si
esto o lo otro)
elif
---también si
(contracción de else if)
else
---De cualquier otra manera
then
---Entonces
fi -------if
invertido, indica que se acabó la condicional abierta con if
Solo son imprescindibles en la estructura del guión
if then y fi.
Supongamos ahora que es usted el jefe de una oficina
y tiene una secretaria y que por alguna razón le han
pedido que envíe una copia de cualquier documento que lo
identifique; normalmente le diría a la secretaria algo
así:
"Maria, por favor, busca en el archivo alguna
identificación" (condición a evaluar)
if "si es una copia del
pasaporte" (primer resultado de la condición); then (entonces)
"
envíala por fax a...." (equivalente al comando a ejecutar)
elif
"si es de la licencia de conducción" (segundo resultado
de la condición); then
"envíala por correo" (otro comando a ejecutar)
elif "
si es del carnet de identidad" (tercer resultado de
la condición); then
"envíala con un mensajero " (otro comando diferente)
else
"de cualquier otra manera"
"pasa un fax diciendo que la enviaré mañana" (otro
comando)
fi
Observe que la acción a ejecutar (equivalente al comando) se
hace si la condición se evalúa como verdadera de lo
contrario se ignora y se pasa a la próxima, si ninguna es
verdadera se ejecuta finalmente la acción después del else.
La sintaxis de bash se debe tener en cuenta a
la hora de escribir el guión o de lo contrario Bash no
entenderá lo que usted quiso decirle,
Pongamos ejemplos de guiones reales
Guión 5
#!/bin/bash
VAR1=Pablo
VAR2=Pedro
if [ "$VAR1" = "$VAR2"
]; then
echo Son iguales
else
echo Son diferentes
fi
Los corchetes son parte de la sintaxis de Bash y en
realidad son un atajo (shortcut) al programa test que es el que ejecuta
la acción de comparación.
Observe siempre los espacios vacíos entre los elementos que
conforman la linea de comandos (excepto entre el último corchete
y el ;), recuerde que ese
espacio vacío por defecto Bash lo interpreta como final de un
elemento y comienzo de otro.
Si corre este
guión siempre se imprimirá a
pantalla Son diferentes, ya que la condición es falsa.
Pero si cambia el valor de VAR2=Pablo entonces se imprime Son iguales.
Guión 6
Un guión que verifica si existe un directorio y si no existe lo
crea e imprime mensajes a pantalla comunicando la acción
ejecutada.
#!/bin/bash
DIR=~/fotos (crea como variable el directorio /home/fotos)
if [ ! -d "$DIR" ]; then (verifica si no
existe el directorio)
mkdir "$DIR" (si la condición es cierta, no existe el
directorio, lo crea)
if [ $? -eq 0 ]; then (verifica si la acción
se ejecutó sin errores, de serlo imprime lo que sigue)
echo "$DIR" ha sido creado..."
else (de lo
contrario imprime)
echo "Se produce un error al crear "$DIR"
fi (Se cierra la
condición abierta en la realización del directorio
segundo if)
else ( de lo contrario,
relativo al primer if)
echo "Se usará "$DIR" existente"
fi
En este guión pueden verse varias cosas nuevas:
1.- El carácter ! niega la acción, si se hubiera escrito
if [ -d "$DIR" ] lo que se estaba evaluando era la condición
¿existe el directorio"$DIR"? pero al colocar ! se evalúa
lo contrario.
2.- El carácter ~ significa el /home del usuario.
3.- La expresión -eq se utiliza cuando quieren compararse
valores numéricos, y significa =
4.- Se usa una de las variables del sistema "$?" explicada mas arriba.
5.- Pueden utilizarse unos condicionales dentro de otros siempre que se
cierren apropiadamente.
Guión 7
#!/bin/bash
echo "Diga si o no:"
read VAR
if [ "$VAR" = si ];
then
echo "Escribiste -si-"
elif [ "$VAR" = no ]; then
echo "Escribiste -no-"
elif [ "$VAR" = "" ]; then
echo "No puede dejarlo en blanco"
else
echo "Lo que escribió no se acepta"
fi
Observe que se está evaluando varias opciones de la
misma condición por lo que lo apropiado es incorporar los
respectivos elif dentro de la misma
condicional.
Un elemento nuevo que se incorpora aquí es la condición "
" que quiere decir "la variable está vacía", en este
caso, cuando no se escribió nada.
Condicional case-in
esac
Cuando una variable puede puede adquirir varios valores o significados
diferentes, ya hemos visto como puede usarse la palabra elif para hacer diferentes
ejecuciones de comandos dentro de una misma condicional if-then-fi de acuerdo al valor de
la
variable. Una forma de
realizar la misma acción sin escribir tantas lineas de
condicionales elif y con ello
disminuir el tamaño del guión es la utilización de
la sentencia case-in-esac.
Esta sentencia permite vincular patrones de texto con conjuntos de
comandos; cuando la variable de la sentencia coincide con alguno de los
patrones, se ejecuta el conjunto de comandos asociados.
La sintaxis de la sentencia case-in
esac es como sigue
case "nombre_variable" in
posibilidad 1) "uno o mas
comandos" ;;
posibilidad 2) "uno o mas
comandos" ;;
posibilidad n) "uno o mas
comandos" ;;
esac
Ejemplo
Guión 8
#!/bin/bash
echo "Diga si o no:"
read VAR
case "$VAR" in
si) echo "Escribiste -si-" ;;
no) echo "Escribiste -no-" ;;
*) echo "Lo que escribió no se
acepta" ;;
esac
Este guión es el mismo guión 7 pero
utilizando la sentencia case-in-esac
Observe que el carácter (*) utilizado en la
última opción significa "patrón no contemplado" en
este caso.
Funciones
Como mecanismo de estructuración en la codificación de
guiones, existe la posibilidad de crear funciones. Su definición
exige la definición de un nombre y un cuerpo. El nombre que debe
ser representativo , es seguido de apertura y cierre de
paréntesis, mientras que el cuerpo se delimita con llaves. La
sintaxis es la siguiente.
nombre_función ()
{
uno o mas comandos
}
Una vez definida la función se utiliza como si de
un comando se tratase, invocándolo con el nombre de la
función. Hay que hacer una invocación de la
función ya definida para que se ejecute el código en su
interior y se convierta en operativa.
Las funciones son muy útiles cuando segmentos del código
de guión son repetitivos, de tal forma solo se escriben una vez
y se invocan todas las veces que haga falta, practicando el divino arte
de la recursión.
Ejemplos
Creando una función simple
ayuda () (se
define la función ayuda)
{
echo "Las opciones son si o no, luego apriete Enter"
}
Después de creada y activada la función, cada vez que
necesitemos la "ayuda" dentro del guión solo colocamos la
palabra ayuda como si se tratase de un comando mas y Bash
ejecutará el código incluido dentro de la función,
es decir imprimirá el texto "Las opciones son si o no, luego
apriete Enter".
Las funciones pueden ser definidas en cualquier orden, pueden ser
tantas como haga falta y pueden contener un paquete relativamente
complejo de comandos. Un programador que ha pensado la estructura del
guión antes de empezarlo puede y de hecho se hace, crear todas
las funciones que necesitará al empezar el guión.
Pruebe lo siguiente
Guión 9
#!/bin/bash
salir () #(Se crea la
función salir)
{
exit #(comando)
}
hola() #(Se crea la función Hola)
{
echo Hola #(comando)
}
hola # (Se invoca la función Hola)
salir # ( Se invoca la función salir)
echo "Esto no se imprime nunca"
Verá que el último echo no se imprime ya que primero se
invoca la función hola y luego la función salir que
cierra el
guión (exit). Trate ahora poniendo un comentario (#) a la linea
que invoca la función salir (linea 11) y note la diferencia,
vera como se imprime el último echo.
Observe también como se han comentado aquellas cosas que no son
parte integrante del guión pero que se pueden escribir para
hacer aclaraciones o anotaciones de interés.
Ciclos, lazos o bucles
Indistintamente llamados (loop) en Inglés, los ciclos permiten
repetir una secuencia de acciones un cierto número de veces, ya
sea fijo o determinado por el cumplimiento de una condición y
pueden ser de tres tipos.
While-do-done
La sentencia while-do-done
se utiliza para ejecutar un grupo de
comandos en forma repetida mientras una condición sea verdadera.
Su sintaxis es:
while
lista de comandos 1
do
lista de comandos 2
done
Mientras la condición de control (lista de
comandos1) sea verdadera,
se ejecutaran los comandos comprendidos entre do y done
en forma repetida, si la condición da falsa (o encuentra una
interrupción explícita dentro del código) el
programa sale del
bucle (se para) y continua la ejecución por debajo del while.
Un ejemplo de la utilidad de este lazo es la posibilidad
de poder escoger varias opciones de un menú sin tener que correr
el guión para cada opción, es decir se escoge y evalua
una opción y el programa no se cierra, vuelve al menú
principal y se puede escoger otra opción, tantas veces
como sea necesario.
Veamos un ejemplo de como elaborar un menú de opciones.
Guión 10
#!/bin/bash
while [ "$OPCION" != 5 ]
do
echo "[1] Listar archivos"
echo "[2] Ver directorio de trabajo"
echo "[3] Crear directorio"
echo "[4] Crear usuario"
echo "[5] Salir"
read -p "Ingrese una opción: " OPCION
case $OPCION in
1) ls;;
2) pwd;;
3) read -p "Nombre del
directorio: " DIRECTORIO
mkdir $DIRECTORIO;;
4) if id | grep uid=0
then
read -p "Nombre del usuario: " NOMBREUSUARIO
useradd $NOMBREUSUARIO
else
echo "Se necesitan permisos de root"
fi;;
5);;
*) echo "Opción ingresada invalida, intente de nuevo";;
esac
done
exit 0
Descripción del guión
1.-En la primera linea condicionamos el lazo a que la opción escogida
sea diferente de 5.
2.-Luego se hace una lista de echos
de las opciones desde 1 hasta 5 con su descripción para que sean
imprimidas a la pantalla y así poder escoger alguna.
3.- Le sigue el comando read
para que lea del teclado la opción escogida (variable OPCION), a read se le ha agregado -p que hace que imprima un mensaje,
en este caso imprime Ingrese una
opción.
4.-Para ahorrar lineas del guión se elabora un case con los comandos que deben
ejecutarse en cada caso ls
para listar los archivos [1], pwd
(present work directory) para ver directorio
de trabajo [2], otro read para
escribir el nombre del directorio que quiere crear [3] y hacer la
variable DIRECTORIO seguido por mkdir
que crea el directorio, luego se crea una condicional if-fi para chequear si el usuario
tiene permisos de root, necesario para la opción [4] de crear un
usuario rechazándolo de lo contrario, despues viene la
opción [5] vacía que ejecuta el comando exit 0,
finalmente se incluye "cualquier otra cosa" con el carácter *
Este guión resulta interesante porque se usan las dos formas de
compactar el guión vistas hasta ahora, la sentencia case-in-esac y la while-do-done. Además
empiezan a aparecer incluidos en los comandos algunos de los programas
muy usados de Linux al escribir guiones.
until-do-done
La sentencia until-do-done es
lo contrario de while-do-done
es decir el lazo se cierra o para, cuando la condición sea
falsa. Si le parece que ambas son muy parecidas está en lo
cierto.
En ambos casos se pueden elaborar bucles o ciclos infinitos si la
condición de control es siempre verdadera o falsa según
el caso, veamos
Lazos infinitos
Bucles infinitos son aquellos donde la ejecución continua
dentro del bucle indefinidamente, veamos como hacer un bucle infinito
mediante while:
while true
do
comando 1
comando 2
comando n
done
La condición siempre es verdadera y se ejecutara el
bucle indefinidamente, mediante until sería
así:
until false
do
comando 1
comando 2
comando n
done
Existe la posibilidad de salir de un bucle, independientemente del
estado de la condición, el comando break produce el
abandono del bucle inmediatamente.
Veamos el guión anterior sobre la creación de un
menú utilizando un lazo infinito y el comando break
Guión 11
while true
do
echo "[1] Listar archivos"
echo "[2] Ver directorio de trabajo"
echo "[3] Crear directorio"
echo "[4] Crear usuario"
echo "[5] Salir"
read -p "Ingrese una opción: " OPCION
case $OPCION in
1) ls;;
2) pwd;;
3) read -p "Nombre del
directorio: " DIRECTORIO
mkdir $DIRECTORIO;;
4) if id | grep uid=0
then
read -p "Nombre del usuario: " NOMBREUSUARIO
useradd $NOMBREUSUARIO
else
echo "Se necesitan permisos de root"
fi;;
5)
echo "Abandonando el programa..."
break;;
*)
echo "Opción ingresada invalida, intente de nuevo";;
esac
done
exit 0
for-in-done
Es otro tipo de ciclo o lazo disponible, la diferencia con los
anteriores es que
no se basa en una condición, sino que ejecuta el bucle una
cantidad
determinada de veces, su sintaxis es la siguiente:
for variable in arg 1 arg 2 ......arg n
do
comando 1
comando 2
comando n
done
Ejemplos
Guión 11
for LETRA in a b c d e f
do
echo $LETRA
done
En este guión el comando echo se ejecutara tantas veces
como argumentos se hayan puesto después del in, por lo tanto imprimirá
seis lineas cada una con una letra de la a a la f.
Guión 12
for ARCHIVO in *
if [ -d $ARCHIVO ]; then
cd $ARCHIVO
rm *.tmp
cd ..
fi
done
Este es un guión entra en todos los subdirectorios del
directorio actual de trabajo y borrará todos los archivos .tmp
(temporales).
En este caso el carácter * se usa en la primera linea con el
significado "tantas veces como sea necesario" y en la penúltima
linea como "cualquier cosa".
Redireccionamiento
Es frecuente la necesidad de redirigir resultados de la
ejecución de un comando a diferentes lugares, que pueden ser los
descriptores de ficheros stdin,
stdout y stderr, a la entrada de otro
comando o a un archivo en el disco duro, esto se llama
redirección y es muy útil en la
escritura de guiones.
Los descriptores de archivos.
En Bash al igual que en cualquier otro programa de consola de Linux
tenemos tres flujos o descriptores de archivos abiertos por defecto:
La entrada estándar (STDIN)
La salida estándar (STDOUT)
El error estándar (STDERR)
El primero puede ser utilizado para leer de él, y los otros dos
para enviar datos hacia ellos. Normalmente STDIN viene del teclado de
la terminal en uso, y tanto STDOUT como STDERR van hacia la pantalla.
STDOUT muestra los datos normales o esperados durante la
ejecución, y STDERR se utiliza para enviar datos de
depuración o errores. Cualquier programa iniciado desde el
shell, a menos que se le indique explícitamente, hereda estos
tres descriptores de archivo permitiendole interactuar con el usuario.
Enviar STDOUT a un archivo
En ocasiones necesitamos enviar la salida estándar a un archivo
y no a la pantalla, ya sea porque es muy grande para "manejar a ojo" o
porque nos interesa guardarla a disco duro. Para enviar la salida
estándar a un archivo usamos > con lo que se sobreescribe el
archivo si ya existe, o >> que solo agrega los datos de salida al
final del archivo ya existente.
Ejemplo
Guión 13
#!/bin/bash
ls -R /home/mis_fotos > /tmp/indice
Creará un archivo llamado /tmp/indice donde estará
el listado de los archivos bajo /home/mis_fotos.
Tomar STDIN de un archivo
Si queremos que un proceso tome su entrada estándar de un
archivo existente usamos < como es el caso por ejemplo de los
procesos que piden datos de confirmación (nombre de usuario,
contraseña etc). Para esto creamos un archivo con los datos de
confirmación y agregamos un < antes del comando que
ejecutará el proceso.
Enviar STDERR a un archivo
Si queremos enviar la salida de errores a un archivo se procede igual
que lo que se mencionaba con respecto a la salida estándar pero
se usa &> o &>> segun el caso.
Enviar STDERR a STDOUT
Para esto se escribe al final de la linea de comandos 2>&1.
Enviar STDOUT a STDERR
En este caso se escribe al final de la linea de comandos 1>&2
Entubado
Las tuberías se utilizan para enviar la salida de un comando o
proceso a la entrada de otro, esto es con frecuencia necesario para
completar una acción iniciada con un comando que debe ser
completada con otro. Es simple el modo de operar, solo se coloca el
carácter | en la linea de comandos entre un programa y otro.
Este carácter (|) se conoce como tubo (pipe)
Ejemplo
Guión 14
#!/bin/bash
file -b "$1" | grep -i "vorbis" >/dev/null 2>&1
if [ $? -eq 0 ]; then
oggdec "$1"
echo "Hecho"
else
echo "Archivo no soportado"
exit
fi
Este guión convierte a wav cualquier archivo de audio ogg.
Primero se invoca a file para que analice el tipo de archivo
correspondiente a la variable $1 que como ya se sabe es el primer
argumento introducido en la linea de comandos (por ejemplo la
ruta hasta un archivo). Luego la salida de file se entuba al programa
grep que determina si dentro del archivo aparece la palabra vorbis
(caso de los archivos de audio ogg).
El condiciomal if- then-fi
chequea que sea cierto (es decir la palabra vorbis si existía,
por lo que es un archivo ogg de audio), entonces se decodifica a wav
con el comando oggdec, de lo contrario se imprime que es un
archivo no soportado.
Tanto la salida estándar como la de errores se envía a
/dev/null, un dispositivo que "desaparece" la información
suprimiendo la salida por pantalla. Esto es conveniente y saludable en
muchas lineas
de comandos cuando la salida puede generar gran cantidad de
información tanto de salida estándar como de errores y
estos no nos interesan. Solo se escribe >/dev/null 2>&1.
Aliases
Podemos indicarle a Bash que cada ves que le demos una estructura de
texto la sustituya por otra, eso se hace con el comando alias.
Cuando bash ejecuta cualquier proceso, verifica si la
primera palabra que tengan todos los comandos aparecen en su lista de
aliases y la sustituye antes de seguir ejecutando la linea. La sintaxis
para crear un alias es como sigue: alias algo='comando', por ejemplo:
alias lista='ls -l'.
Cada vez que pongamos la palabra lista como un comando Bash
ejecutará ls -l.
Ahora, si escribimos echo lista, como no es la primera palabra
imprimirá lista a secas, pero si ecribimos echo `lista` nos
imprimirá el resultado de ls -l ya que lo encerrado entre
comillas invertidas se ejecuta en un sub shell para la cual la primera
palabre es lista.
Pueden definirse tantos aliases como necesitemos para trabajar con
comodidad y ahorrarnos teclazos. Para consultar la lista de aliases se
ecribe en la consola alias sin argumentos.
Para eliminar los aliases se escribe unalias y el nombre. Las
distribuciones y los propios programas instalados ya vienen por defecto
con varios aliases, haga la prueba en el suyo.
Globales y expansiones.
Globales
Estos son aliados cuando uno quiere ahorrarse teclazos y funcionan como
"generalizadores" de cosas, los globales mas comunes son:
1. ~ Le dice a Bash que es el directorio home del usuario.
2. * Significa "todo lo que puedas incluir ahí" de
forma tal que si ponemos el comando ls ~/*.wav listará todos los
archivos .wav que están en el directorio home del usuario. Ahora
si escribimos ls ~/m* nos listará todos los archivos de home que
empiecen con m.
3.- . Un punto en el entorno de la shell significa "el directorio
donde estamos trabajando" Ejemplo:
Guión 15
#!/bin/bash
DIR=.
mkdir "$DIR"
echo "$?"
Si escribimos este guión y lo corremos dará un
error. Por supuesto, le estamos mandando a hacer el directorio
donde estamos. Habrá notado usted que es muy común a la
hora de compilar programas desde el binario utilizar ./configure,
con esto le estamos diciendo a Bash "corre el archivo configure que
está en este mismo directorio".
Expansiones
Las expanciones son mas configurables y trabajan con argumentos mucho
mas definidos, está claramente hecha para hacer mas inteligente
la shell.
Cuando especificamos una lista de valores o argumentos separados por
comas entre llaves, Bash la expande convirtiéndola en la
cadena expandida con cada uno de los argumentos, por ejemplo:
el comando
echo
este/directorio/{algo,muy,demasiado}/largo
dará como resultado la impresión a pantalla de:
este/directorio/algo/largo este/directorio/muy/largo
este/directorio/demasiado/largo
Hay que tener en cuenta que:
1.- La expansión funciona sobre una sola palabra sin espacios si
escribimos:
echo esto {es,parece}
difícil
escribirá;
esto es parece difícil
2.- La expansión no se realiza entre comillas simples ni dobles
por lo que no sirve para corregir el ejemplo anterior:
echo "esto {es,parece}
difícil"
dará:
esto {es,parece}
difícil
3.- Lo que debe hacerse es ignorar o escapar los espacios y escribir
echo esto\ {es,parece}\
confuso
así obtendremos lo que queríamos:
esto es difícil esto
parece confuso.
Pueden ponerse múltiples expansiones en una sola
linea y se obtendrán todas las combinaciones posibles.
echo {una,otra}\
combinación\ { bastante,muy}\ difícil.
Responde
una combinación bastante
difícil. otra combinación bastante difícil. una
combinación muy difícil. otra combinación muy
difícil.
Aritmética de Bash
Se pueden ejecutar en Bash las principales acciones aritméticas
entre las variables utilizando los signos:
+ -----suma
- -----resta
* -----multiplicación
/ ------división
Las operaciones tienen su sintaxis que debe ser respetada para que Bash
lo haga adecuadamente.
1.- Pruebe esto en la shell o la linea de comandos (consola).
echo 1+1, la respuesta
será 1+1 porque bash lo interpreta como caracteres simples, para
que realice la operación de suma hay que escribir:
echo $((1+1)) o
echo $[1+1]
Bash no maneja números fraccionarios solo números enteros
por lo tanto si usted escribe:
echo $[3/4] la
respuesta será cero, sin embargo si escribe:
echo $[4/2] la
respuesta será correcta 2
2.- También podrá utilizar a expr para las operaciones de la
forma siguiente:
expr argumento1 signo argumento2
pruebe en la consola
expr 2+2 la
respuesta será 4 o
expr 4 / 2 la
respuesta será 2
Cuando se use es signo * para
la multiplicación debe anteponerle una barra invertida para que
Bash no lo interprete como un global, sería:
expr 10 \* 10 la
respuesta será 100
El programa expr da sus resultados directamente a la
salida estándar pero tampoco maneja números
fraccionarios. Hay que observar siempre un espacio entre los argumentos.
3.-Para operar con fraccionarios debe entubar la expresión al
programa bc de la forma
siguiente:
echo operación | bc
-l
por ejemplo;
echo 3/4 | bc -l
el resultado será 0.75 o
echo 2+2.5 | bc -l
devolverá 4.5
En algunas distribuciones
el programa bc no se instala por defecto.
Hay otras expresiones que Bash interpreta aritméticamente;
-lt
|
Menor que
|
-le
|
Menor o igual que
|
-eq
|
Igual que
|
-ge
|
Mayor o igual que
|
-gt
|
Mayor que
|
-ne
|
Distinto que
|
Lógica de Bash.
Para la shell los caracteres que tienen un significado lógico en
la comparación o evaluación de archivos son:
>
|
Mayor que
|
<
|
Menor que
|
>=
|
Mayor o igual que
|
<=
|
Menor o igual que
|
!
|
Diferente que
|
||
|
OR (ó)
|
&&
|
AND (y)
|
Ejemplo:
Guión 16
#!/bin/bash
ARCHIVO=$1
file -b "$1" | grep -i 'JPEG' || file -b "$1" | grep -i 'GIF' || file -b "$1" | grep -i 'PNG' || file -b "$1" | grep -i 'BITMAP' >/dev/null
2>&1
if [ $? -eq 0 ]; then
echo "Es una imagen"
else "No es una
imágen"
fi
En este guión hemos supuesto que un archivo
cualquiera se convierte en la variable $1 y queremos averiguar si el
archivo es una imágen en alguno de los formatos mas
comunes, primero acudimos a file para que "lea" el texto que contiene
el archivo y lo entubamos a grep que buscará patrones de texto
de lo que le entrega file. Como necesitamos averiguar si alguno de los
patrones JPEG, GIF, PNG o BITMAP aparece dentro del archivo utilizamos
varias instancias de file y grep separadas con OR (||), de esta forma
le estamos diciendo en el comando "busca si aparece JPEG o GIF o PNG o
BITMAP, si lo encuentras entonces imprime"
"Es una imágen" de cualquier otra forma imprime "No es una
imágen"
Consideraciones finales
No existe forma alguna de que se aprenda Bash que no sea
usándolo, son tantas y tan variadas las herramientas a su
disposición así como las vías para lograr un
objetivo que solo la experiencia puede ayudarle a aprenderlo. Al
principio le parecerá inalcanzable pero si persevera se
dará cuenta de que es posible y hasta fácil,
después que uno le encuentra "las cosquillas" dirá con
orgullo "esto es pan comido" cuando quiera automatizar una tarea o
quiere hacer un "programita" para cumplir un objetivo. Haga lo mismo
que yo, empecé jugando y ya he aprendido algo. Le deseo suerte.
|