Tutorial de Programacion en Bash

Bash es una shell, que ademas cuenta con un lenguaje de comandos interpretado incorporado. Esto te permite escribir guiones y correrlos directamente en tu entorno.

Si ya has completado el primer Tutorial del lenguaje de interprete de comandos y sabes como hacer guiones simples de programacion, sin dudas este tutorial te ensenara algunas de sus caracteristicas mas avanzadas de Bash.

Variables

Las variables de bash son las mismas que en otros lenguajes de programacion. No necesita especificarlas.

#!/usr/local/bin/bash

NAME="Tutoriales texto-plano.xyz"
echo $NAME

launchdate="Feb 28, 2023"
echo $launchdate

Varibles Globales y Locales

Las Variables Globales son accesibles desde cualquier lugar del guion. Las variables locales solo son accesibles dentro del mismo. Por ejemplo, una variable local utilizada unicamente en dentro de una funcion:

#!/usr/local/bin/bash

#Define una variable global de bash
GLOBAL_VAR="valor de variable global"

function hola {
    #Define variable local de bash
    local LOCAL_VAR="valor de variable local"
    echo $LOCAL_VAR
    echo $GLOBAL_VAR    ## Esto sera accesible aqui
}
echo $LOCAL_VAR     ## Esto no sera accesible aqui
echo $GLOBAL_VAR

Variable de Sistema

Las variables de sistema son responsables de definir aspectos del interprete de comando Bash. Estas variables son mantenidas por Bash en si. Podremos modificar tales variables para cambiar aspectos del Shell. Utiliza el comando env para presentar todas las variables disponibles de Bash.

env

Generalmente se suele definir estas variables en mayusculas, por ejemplo: PATH, SHELL, HOME, LANG, PWD y muchas mas.

Citas

En muchos lenguajes de programacion una practica estandar citar la cadena. Las citas se utilizan para referenciar texto, ficheros con caracteres de espacio.

Cita con cadena

No existe diferencia al utilizar apostrofo '...' o comillas "..." mientras operas con textos simples y cadenas.

El siguiente guion correra sin errores y presentara el mensaje indicado y creara ambos directorios:

#!/usr/local/bin/bash

echo 'Cadena apostrofada'
echo "Cadena entrecomillada"

mkdir 'Directorio 1'
mkdir "Directorio 2"

Citas con Variables

Recuerda que las expansion de variables de Bash unicamente operara con las comillas. Si defines cualquier variable en comillas simples, ¡no funcionaran!

Ejecuta el siguiente guion. La primer echo presentara el valor de la variable (ej. Bienvenid@ ~fulana). Pero la segunda variable solo presentara $NOMBRE porque esta entre comillas simples:

#!/usr/local/bin/bash

NOMBRE="Bienvenid@ ~$USER"

echo "$NOMBRE"
echo '$NOMBRE'

Depuracion en Bash

El guionado de Bash provee una funcionalidad de depuracion durante el tiempo de ejecucion. Emplea el comando set -xv dentro del guion o en la linea de comando cuando ejecutes el guion para depurarlo.

Depurado durante la ejecucion

Utiliza Bash con el argumento -xv para depurar al tiempo de ejecucion de un guion de bash:

$ bash -xv guion.sh

Depurado de un guion

Si deseas activar la depuracion dentro del mismo guion, podras depurar cierta parte del guion.

#!/usr/local/bin/bash

set -xv   # Esta linea activa el depurado

cd /var/log/
for i in "*.log"; do
 du -sh $i
done

Ahora corre el guion...

$ ./guion.sh

...y obtendras resultados como:

cd /var/log/
+ cd /var/log/
for i in "*.log"; do
 du -sh $i
done
+ for i in '"*.log"'
+ du -sh boot.log mysqld.log post111.log post1121.log yum.log
0       boot.log
32K     mysqld.log
0       post111.log
0       post1121.log
4.0K    yum.log

Codigo de Salida

Un codigo de salida en Bash es un valor en codigo que representa el resultado de la ejecucion apropiada de un programa, representado por un valor entre 0 y 255. Tal numero denota el status de salida del ultimo comando introducido, en otras palabras, es la indicacion que da - al cese de su ejecucion - el proceso hijo, al retornar al su proceso padre.

0 - Success Un valor cero (0) representa exito.
1-255 Failure Un valor no cero representa un fracaso.

Ejemplo

Escribe un programa para escribir una cadena de texto en /etc/fichero_prueba.txt y ejecutalo para ver si funciona correctamente:

#!/usr/local/bin/bash

echo "Viva Bash, mas rapido que un flash!" > /tmp/fichero_prueba.txt

if [ $? -eq 0 ]; then
  echo "Funciona! Viva el Software Libre!"
else
  echo "Lo siento, no puedo escribir en /tmp/fichero_prueba.txt"
fi

Este fichero buscara la cadena fulana en el fichero /etc/passwd.

#!/usr/local/bin/bash

CADENA="fulana"

if grep ${CADENA} /etc/passwd
then
  echo "Si! Encontre la cadena"
else
  echo "Buu!, no encontre la cadena"
fi

Entrada de Usuario

El comando read se utiliza realizar entrada de datos interactiva.

Para que el sistema aguarde tu entrada y la almacene en una variable al presionar la tecla Intro es la siguiente:

$ read mivariable

Tambien podras indicar algun texto con -p, que se presentara a modo de informacion a la hora de solicitar la entrada:

read -p "Dime tu nombre de usuari@: " tuusuario

Podras utilizar el argumento -sp para no mostrar lo que tecleas (util para guardar secreto en pantalla).

$ read -sp "Dime de que equipo eres fanatic@: " tuequipo

Utiliza en un guion de Bash la funcionalidad del comando read para solicitar el ingreso de datos al usuario:

#!/usr/local/bin/bash

read -p "Ingresa tu nombre de usuari@: " tuusuario
read -sp "Dime en secreto de que equipo eres fanatic@: " tuequipo

echo -e "\nTu nombre de usuari@ es $tuusuario y tu equipo favorito es $tuequipo"

Operadores

Los operadores en unix permiten realizar distinto tipo de operaciones en la shell. Bash cuenta con los mismos operadores, y extiende su utilizacion,

A continuacion encontraras ejemplos de su uso en Bash.

Operadores aritmeticos

En Bash podras encerrar entre parentesis dobles ((...)) para realizar las operaciones aritmeticas sobre variables constituidas por valores numericos, segun esta sintaxis:

((expresion))
((variable1+variable2))
((variable1-variable2))
((variable1*variable2))
((variable1/variable2))
Ejemplo de operadores aritmeticos

Este guion permite ingresar dos valores numericos para realizar las cinco operaciones basicas:

#!/usr/local/bin/bash

read -p "Ingresa un valor numerico: " n1
read -p "Ingresa otro valor numerico: " n2

echo "La Suma de $n1 + $n2 es       = " $((n1+n2))
echo "La Resta de $n1 - $n2 es    = " $((n1-n2))
echo "La Division de $n1 / $n2 es       = " $((n1/n2))
echo "La Multiplicacion de $n1 * $n2 es = " $((n1*n2))
echo "El modulo entre $n1 % $n2 es        = " $((n1%n2))

Operadores de comparacion de cadena

Utiliza doble igual (==) para comparar cadenas.

if [ "$cadena" == "$cadena2" ]     # True si es igual
if [ "$cadena1" != "$cadena2" ]     # True si es no igual

Operadores de comparacion numerica

Como en todos los shells de tipo Unix, en Bash podras utilizar operadores de comparacion numerica.

((n1 == n2))    ## n1 es igual a n2
((n1 != n2))    ## n1 es no igual a n2
((n1 > n2))     ## n1 es mayor a n2
((n1 >= n2))    ## n1 es mayor o igual a n2
((n1 < n2))     ## n1 es menor a n2
((n1 <= n2))    ## n1 es menor o igual a n2

Operadores de incremento y decremento

Bash tambien cuenta con operadores de incremento (++) y decremento (<nowiki>--</nowiki. Ambos usos en dos tipos de pre-incremento/post-incremento y pre-decremento/post-decremento.

## Ejemplo de Post-incremento
$ variable=10
$ echo $((variable++))  ## Primero imprime 10 luego incrementa valor en 1

## Ejemplo de Pre-incremento
$ variable=10
$ echo $((++variable))  ## Primero incrementa valor en 1 luego imprime 11

En forma similar, puedes utilizar operadores de pre-decremento y operadores de post-decremento.

## Ejemplo de Post-decremento
$ variable=10
$ echo $((variable--))  ## Primero imprime 10 y luego decrementa el valor en 1

## Ejemplo de Pre-decremento
$ variable=10
$ echo $((--variable))  ## Primero decrementa el valor en 1 luego imprime 9

Condicionales

En un lenguaje de programacion se utilizan bucles para repetir la ejecucion de un bloque de codigo hasta que se satisfaga una condicion definida. Esto es sumamente util para realizar tareas repetitivas. Existen tres tipos de bucles principales: for (en caso), do ("hacer"), y do-while ("hacer-en tanto").

Las condiciones cuentan con introduccion, nudo y desenlace. Introducen con su identificador, y desenlazan con un identificador inverso.

If-Else

Al igual que cualquier otro lenguaje de programacion, if-else es la declaracion de toma de decisiones de Bash. Decidira la ejecucion de un bloque de codigo en base al resultado del condicional if. Si se evalua verdadera la condicion, ejecutara el codigo. Si la evalua falsa, se ejecutara el bloque else, cuya existencia es opcional.

Se abre con if e incorpora la llamada then y se cierra con fi. Opcionalmente, en el nudo puede incorporar una funcion condicional else, con la sintaxis general:

if [ condicion ]
then
  hacer-algo
else
  hacer-otra-cosa
fi

Ejemplo if-then

Este programa solicita que ingreses una cifra, y te informa si es mayor a 10.

#!/usr/local/bin/bash

read -p "Ingrese un valor numerico: " mivariable

if [ $mivariable -gt 10 ]
then
    echo "El valor es mayor que 10"
fi

Ejemplos If-else

Usando la declaracion if-else ("si-ademas"), es posible ejecutar una declaracion condicional si esta resulta falsa. Para ello se define un bloque de texto con else.

Este ejemplo implica un condicional por comparacion numerica. Dara Verdadero si el valor ingresado por el usuario es mayor a 10, y si cumple tal prerrogativa ("then", "luego") presentara Correcto; luego si ademas resulta Falso (de aqui el "if-else"), presentara No es correcto.

#!/usr/local/bin/bash

read -p "Ingresa una cifra: " mivariable

if [ $mivariable -gt 10 ]
then
    echo "Correcto"
else
    echo "No es correcto"
fi

Este ejemplo realiza una comparacion de cadenas y las compara:

#!/bin/bash

read -p "Ingresa la primer cadena: " cadena1
read -p "Ingresa la segunda cadena: " cadena2
if [ "$cadena1" == "$cadena2" ]
then
  echo "Ambas cadenas son iguales"
else
  echo "Ambas cadenas son diferentes"
fi

Ejemplo if-elif-else

La condicion elif (else-if, "si ademas") se usa para incorporar multiples condicionales if.

En este ejemplo de condicioniales else-if, debes indicar tus calificaciones. Si son mayores o iguales a 80, presentara Muy satisfactorio. Si son inferiores a 80 o igual a 50 presentara Satisfactorio, etcetera.

#!/usr/local/bin/bash

read -p "Ingresa tus calificaciones: " calificacion

if [ $calificacion -ge 80 ]
then
    echo "Muy satisfactorio"

elif [ $calificacion -ge 50 ]
then
    echo "Satisfactorio"

elif [ $calificacion -ge 33 ]
then
    echo "Aun no satisfactorio"
else
    echo "Insatisfactorio"

Ejemplo de condicionales if anidados

Con los if anidados, solo se comprobara la veracidad de una condicion anidada, si otra condicion nido resulta verdadera.

Por ejemplo, este programa solicita ingresar 3 valores numericos como entrada y realiza una comparacion numerica para analizar cual es el valor mayor.

#!/usr/local/bin/bash

read -p "Cuantas copas tiene Boca :" copasboca
read -p "Cuantas copas tiene River :" copasriver
read -p "Cuantas copas tiene Independiente :" copasindependiente

if [ $copasboca -gt $copasriver ]
then
    if [ $copasboca -gt $copasindependiente ]
    then
        echo "Boca es el mas grande"
    else
        echo "Independiente es el Rey de Copas"
    fi
else
    if [ $copasriver -gt $copasindependiente ]
    then
        echo "River es el mas grande"
    else
 echo "Independiente es el Rey de Copas"
    fi
fi

While

While es una estructura de control de bucle iterativo, que continua ciclando ("iterando") hasta que el condicional suministrado de falso.

Se abre con while ("mientras") e incorpora la llamada do ("hacer") y se cierra con done ("realizado"). Opcionalmente, en el nudo puede incorporar una funcion condicional until ("hasta"), que continuara iterando hasta que se valide el condicional. Tambien puede incorporar la accion break ("interrumpir"), con la sintaxis general:

while condicion
do
  paso1
  paso2
  ...
done

Ejemplo de bucle While

El siguiente bucle se ejecutara 5 veces y se detendra cuando el valor de la variable num sea mayor que 5.

#!/bin/bash

num=1
while [ $num -le 5 ]
do
   echo "$num"
   let num++
done

Ejemplo de bucle While infinito

Los bucles infinitos se ejecutan continuamente hasta que se detienen forzadamente por el usuario, presionando Ctrl+c.

#!/usr/local/bin/bash

while true
do
  echo "Presione Ctrl+c para Salir"
done

Los bucles infinitos tambien pueden detenerse agregando alguna salida condicional al guion:

#!/usr/local/bin/bash

while true
do
   if [condicion];then
      exit
   fi
done

En el caso anterior, cuando la condicion se convierta en verdadera, se saldra del bucle.

En este ejemplo, se utiliza un condicional para ingresar a traves de read cualquier cadena y valor, resenta un mensaje con echo y utiliza la orden read para solicitar interaccion del usuario a traves del teclado). Solo interrumpira el bucle de solicitud condicional si se se introduce s o bien (indicado con -o S), con lo cual interrumpira el bucle:

while true
do
    echo "Introduce algo para procesar (ingresa S para salir)"
    read entradausuario

    if [ $entradausuario == "s" -o $entradausuario == "S" ]
  then
    break
fi

//Procesar lo ingresado...
done

Ejemplo de Bucle While de estilo C

Bash tambien acepta nomenclatura similar a C para escribir un bucle while:

#!/usr/local/bin/bash

num=1
while((num <= 5))
do
   echo $num
   let num++
done

Ejemplo de bucle While leyendo fichero

Esta funcionalidad util de bash permite que el bucle While lea el contenido de un fichero linea por linea. De esta forma se puede leer lineas y desarrollar alguna tarea:

#!/bin/bash

while read mivariable
do
   echo $mivariable
done < /tmp/mi_fichero.txt

En este ejemplo el se leera en bucle linea a linea de nombre_fichero.txt y asignara el valor a la variable mivariable.

Ver tambien: while.sh (ejemplo while-do-done))

Until

Until, es una instruccion de control que sirve para generar bucles, en los cuales grupos de instrucciones anidadas dentro de un bloque se ejecutan de forma repetida hasta que se cumpla una condicion. Sigue la sintaxis until-do-done.

Cabe destacar que Until, primero comprobara si se cumple la condicion y si se cumple y solo si se cumple entonces finaliza la secuencia de las instrucciones contenidas entre do y done.

Cada vez que se ejecuta un bucle completo (paso de bucle) vuelve a verificar si se cumple la condicion antes de volver a ejecutar otro paso de bucle.

Ejemplo Until-do-done

until [ "condicion logica" ]
do
     accion 1
     accion 2
     accion n
done
do y done

Las acciones entre do y done se repetiran secuencialmente hasta que se cumpla la "condicion logica". Cuando la "condicion logica" sea verdadera, no se ejecutara ninguna "accion" y finalizara el bucle.

Ejemplo Do-Done

<limite=5
i=10;

until [ $limite -gt $i ]
do
     echo Accion $i ejecutada
     let i=$i-1
done

El ejemplo anterior mostraria por pantalla:

Accion 10 ejecutada
Accion 9 ejecutada
Accion 8 ejecutada
Accion 7 ejecutada
Accion 6 ejecutada
Accion 5 ejecutada

case

case ("En caso") permite delimitar casos de condicionales.

La introduccion es case, el nudo incorpora el caso, que deben definirse en una linea aislada delimitados por un ). Si indicas los casos separados por un | significa "o tambien". Un caso nomenclado con *) indica se interpreta como "todos los demas". Cada caso debe contar con un separador de linea ;;. El desenlace es esac.

Ejemplo de multiples cadenas en case

Se pueden definir mas de una cadena para el patron de coincidencias en una declaracion case.

user=`whoami` # pone el nombre de usuari@ en
              # la variable $user.
case $user in
    fulana)
      echo "Hola fulana. Se que te gusta saber la hora, de modo que te la presento abajo."
      date
;;
    sultano)
      echo "Hola Sultano, recuerda tu lista de tareas pendientes."
      cat /home/sultano/sultano_to_do.txt
;;
    sosa|molina)
       echo "Hola. No olvides vigilar la carga en el sistema. El tiempo de encendido del mismo es:"
       uptime
;;
    *)
       echo "Hola, quien quiera que seas. ¡Ponte a obrar por la Liberacion, y recuerda!"
       fortune doctrina
;;
esac

La declaracion case es mas util y rapida para procesar que un condicional else-if. En lugar de revizar todas las condiciones if-else, la declaracion case selecciona directamente el bloque a ejecutar basada en una entrada condicional.

Ejemplo de coincidencia de patrones en case

En las declaraciones case puedes utilizar caracteres comodines como *, ? y []. Aun asi, algunas de las expansiones no funcionaran.

Ahora puedes utilizar shopt -s extglob para emplear coincidencia de patrones avanzada.

#!/usr/local/bin/bash

read -p "Ingresa una cadena:" choice
shopt -s extglob
case $choice in
     a*)                    ### coincide todo lo que comience con "a"
          #Aqui va un guion
          ;;

     b?)                    ### coincide cualquier cadena de dos caracteres que comience con "b"
          #Aqui va otro guion
          ;;

     s[td])                 ### coincide "st" o "sd"
          #Aqui va otro guion
          ;;

     r[ao]m)                ### coincide "ram" o "rom"
          #Aqui va otro guion
          ;;

     me?(e)t)               ### coincide "met" or "meet"
          #Aqui va otro guion
          ;;

     @(a|e|i|o|u))          ### coincide una vocal
          #Aqui va otro guion
          ;;

     *)                     ### Coincide todo lo que no coincidio anteriormente
          #Aqui va un guion
          ;;
esac

Bucle for

El bucle for se utiliza para realizar tareas repetitivas. La sintaxis basica de un bucle for, es la siguiente:

for VARIABLE in PARAMETRO1 PARAMETRO2 PARAMETRO3
do
  //declaraciones del bucle for-loop
done

El bucle for ejecutara todos los parametros definidos una vez. El nudo del bucle se comienza con la palabra reservada do ("hacer") y se termina con la palabra reservada done ("finalizado"). Todas las declaraciones deben escribir dentro del nudo del bucle.

En esta sintaxis, VARIABLE se inicializa con los valores del PARAMETRO que puede ser accedido dentro del nudo del bucle. Estos parametros pueden ser cualquier numero, cadena, etcetera.

Ejemplo Bucle For

Este bucle basico itera 5 veces.

#!/usr/local/bin/bash

for i in 1 2 3 4 5
do
   echo "$i"
done

Tambien puedes definir un rango para el bucle for, utilizando valores numericos entre parentesis () dentro del guion de Bash.

#!/usr/local/bin/bash

for i in {1..5}
do
   echo "$i"
done

Los argumentos pueden estar formados por cadenas, como en:

#!/usr/local/bin/bash

for dia in DOM LUN MAR MIE JUE VIE SAB DOM
do
   echo "$dia"
done

Ejemplo de Bucle For en estilo C

En Bash es posible tambien puedes escribir un bucle for siguiendo nomenclatura de estilo C. Por ejemplo, para presentar numeros del 1 al 10.

#!/usr/bin/bash

for ((i=1; i<=10; i++))
do
  echo "$i"
done

Ejemplo de bucle For con ficheros

Puedes acceder nombres de ficheros de uno en uno en un bucle for en el directorio especificado.

Por ejemplo, podremos leer todos los ficheros del directorio de trabajo actual. En el siguiente bucle iterara la cantidad de veces coincidente con el numero de ficheros disponibles en el directorio de trabajo. Seleccionara un fichero por cada iteracion numerica.

#!/usr/local/bin/bash

for nombrefichero in *
do
  ls -l $nombrefichero
done

Ver tambien: manzanas.sh (Ejemplo bucle for-if)

Funciones

Una funcion (tambien llamadas subrutinas o procedimientos) es una seccion de codigo utilizada para realizar una tarea especifica. Estas pueden ser reutilizadas.

Sintaxis:

nombreFuncion(){
  // procedimiento de la funcion
}

nombreFuncion  //llamada a la funcion

Creacion de una funcion

Crea tu primer funcion de un guion de bash para que presente la cadena "Hola Texto-plano.xyz!". Elabora el guion de Shell "saludo.sh" con el siguiente codigo fuente:

#!/usr/local/bin/bash

funSaludazo(){
    echo "¡Hola texto-plano.xyz!";
}

# llama a la funcion saludazo desde cualquier lugar del guion, por ejemplo, ahora mismo:

funSaludazo

Ejecuta el guion creado:

$ ./saludo.sh
¡Hola texto-plano.xyz!

Funcion con argumento

Para indicarle un argumento a la funcion es necesario hacerlo de la misma manera que argumentarias cualquier comando desde el interprete de comandos: agregandolos a continuacion con un espacio en blanco. Las funciones reciben argumentos $1, $2, etcetera. En consecuencia, para crear un guion de shell con argumento podrias incluir un codigo semejante a este:

#!/usr/local/bin/bash

funArgumentos(){
   echo "Primer Argumento: " $1
   echo "Segundo Argumento: " $2
   echo "Tercer Argumento: " $3
   echo "Cuarto Argumento: " $4
}

# Llama a funArgumentos desde cualquier lugar en el guion, utilizando parametros como los indicados a continuacion

funArgumentos 1 Bienvenido a Texto-plano.xyz

Ejecuta el guion desde la shell de Bash:

$ ./saludo.sh
Primer Argumetno : 1
Segundo Argumento : Bienvenido
Tercer Argumento : a
Cuarto Argumento : Texto-plano.xyz