Guía sobre la programacion bash
¿Que es un script bash?
Un script bash, es básicamente un conjunto de instrucciones que permiten ejecutar
una serie de comandos en la Shell (terminal) de una forma secuencial.
Estos scripts, se pueden perfeccionar, hasta convertirse en un pequeño programa
con el que realizar casi cualquier tarea administiva en GNU/Linux, ya sea mediante bucles, funciones, etc...
Ficheros bash
Para que Linux identifique el fichero a ejecutar como un script hay que
realizar varios pasos:
- Identificación de la shell a usar.
El fichero debe de tener obligatoriamente en su primera línea lo siguiente:
#!/bin/bash
TRUCO: Si queremos que un script bash se ejecute en modo 'depuración'
incluiremos un -x al final de la linea anterior quedando así:#!/bin/bash -x
- Permisos de ejecución.
El fichero a ejecutar debe tener permisos de ejecución, una vez creado el script
bash hay que darle los permisos necesarios:
chmod 755 nombre_del_script
Tambien puedes usar:
chmod +x nombre_del_script
- Nombre y extension.
El fichero a crear, puede tener cualquier extensión o incluso no tenerla, ya que Linux sabe que debe realizar al leer la primera línea del fichero (paso 1).
Aunque lo recomendable es identificarlo mediante la extension .sh
Todo lo que vaya detras de #
se considera Comentario
# Esta linea es un comentario
echo "Bienvenido" # Sacar por pantalla 'Bienvenido' (Comando + Comentario)
Los scripts Bash y sus comandos se ejecutan de forma secuencial de la primera a la última linea, excepto las funciones como veremos más adelante.
Mi primer script bash
Para la edición de los ficheros de script Bash, usaremos un editor en Linux,
ya sea un editor gráfico (pluma, atom, vscode, etc.)
o un editor de consola (vi, vim, nano, joe, etc.).
En mi caso suelo usar vi/vim, por ser uno de los que siempre están instalados
por defecto en todos los equipos con GNU/Linux.
Puedes revisar el artículo monográfico sobre VIM
para aprender a usar Vim.
Vamos a crear nuestro primer script hola.sh
vim hola.sh
Dentro del editor escribiremos dentro las siguientes líneas:
#!/bin/bash
echo "Hola. Soy tu primer script Bash"
NOTA 1: echo
es un comando Linux que nos muestra por pantalla un texto o
el contenido/valor de una o varias variables.
NOTA 2: printf
es un comando Linux que imprime texto o variables,
pero permitiendo aplicar un formato a la salida.
Salimos guardando, para ello en vim pulsamos ESC
y luego escribimos :wq
Ahora le damos permisos de ejecucion:
chmod 755 hola.sh
Ya está listo, vamos a probarlo.
Desde la consola/terminal/Shell, y desde la misma carpeta en donde se encuentra
el script, escribe lo siguiente:
./hola.sh
Mostrará por pantalla lo siguiente:
Hola. Soy tu primer script Bash
Parámetros
Los párametros en bash
Un script Bash puede recibir parámetros y estos pueden ser procesados dentro de él.
Los parámetros que se pueden usar dentro de un script bash son:
Parámetro | Significado del parámetro |
---|---|
$# |
Nº de parametros recibidos |
$0 |
Nombre y ruta del propio script |
$1 ... $9 |
Parámetros del 1 al 9 recibidos |
${N} |
Parámetro de la posicion N recibido |
$* |
Todos los parámetros recibidos (excepto $0 ) [1] |
$@ |
Array de parámetros recibidos (excepto $0 ) [2] |
$$ |
El PID (numero de proceso) del script |
$? |
El código de error del ultimo comando ejecutado |
Comando shift
para desplazar parámetros
shift
Comando que desplaza los parametros recibidos (excepto $0
) a una posición anterior.\newline
Ejemplo: Hemos recibido dos parámetros, $1
con el valor UNO
y el segundo parámetro $2
con el valor DOS
.\newline
Si ejecutamos shift
el 2º parametro se desplaza y ocupa la posicion del 1º, el valor de este desaparece.
Tras la ejecución si revisamos el contenido de la variable $1
obtendríamos DOS
.
El comando shift se utiliza para procesar los parámetros y realizar distintas
acciones cuando se ejecuta un script. \newline
Un ejemplo de uso de un script con parámetros es:
./find-truncate.sh --directory /var/tmp --max-size 300M --days 15
Los parámetros son --directory
, --max-size
y --days
a continuación de cada
parámetro se indica el valor que tendrá. \newline
Para explicarlo con detalle, vamos a crear nuestro propio script usando nuestros propios parámetros.
Procesaremos los parámetros recibidos que podremos recibir en cualquier orden y haremos uso de shift
,
para que independientemente del orden de los parámetros, los pueda procesar. \newline
Script usando shift
Script: nombre-apellido.sh
#!/bin/bash
# USO: ./nombre-apellido.sh --nombre NOMBRE --apellido APELLIDO
while [[ $# > 0 ]] # Recorrer todos los parametros recibidos
do
case "$1" in
-n | --nombre )
shift # Encontre "-n" o "--nombre" Con shift salto al siguiente parametro que es el valor del nombre.
nombre="$1" # guardo en la variable nombre el valor.
shift # Salto al siguiente parametro
;;
-a | --apellido )
shift
apellido="$1"
shift
;;
* )
# Algo no es lo que esperaba..
shift # Ignoro y salto al siguiente parametro
;;
esac
done
# Tengo parámetros almacenados en las variables correctas. Las puedo usar.
echo "Tu Nombre es: $nombre y tu Apellido es: $apellido"
Vamos ahora a ejecutarlo indicando los parametros y vemos que funciona correctamente:
./nombre-apellido.sh --nombre Luis --apellido Gutierrez
Tu Nombre es: Luis y tu Apellido es: Gutierrez
Si lo ejecutamos ahora cambiando el orden de los parametros, también funciona:
./nombre-apellido.sh --apellido Gutierrez --nombre Luis
Tu Nombre es: Luis y tu Apellido es: Gutierrez
Variables
En un script Bash se pueden usar variables para almacenar valores o
el resultado de la ejecución de comandos.
La variables no son tipadas
y en ellas se puede almacenar cualquier cosa:
- Texto
- Números
- Arrays
Nota: No hay Booleanos en bash.
Variables Globales/Locales
Las variables pueden ser globales a nivel de todo el script o locales cuando están dentro de una función.
Nota: En bash se distingue entre mayúsculas y minúsculas en la declaración de variables.
Declaración de variables
Para declarar una variable, simplemente hay que escribir el nombre de la variable,
seguida de un igual y el valor que se le quiera asignar.
Veamos algunos ejemplos sobre como declarar diferentes variables.
Variables numéricas y de texto
VAR=n # Variable $VAR almacena un número
VAR=texto # Variable $VAR con un texto
VAR='cadena de texto' # Variable $VAR almacena texto (no interpretable)
VAR="cadena de texto" # Variable $VAR almacena texto (interpretable)
Observa que se pueden usar comillas simples o dobles para almacenar una
cadena de texto.
En un script podriamos usarlo así:
nombre=Luis
CALLE="Calle Larga"
Despacho=401
En el ejemplo anterior se crean las variables $NOMBRE
, $CALLE
y $Despacho
Si una variable va a contener espacios en blanco en la cadena de texto, deberemos
usar comillas dobles " o simples ' para almacenarlo o sólo podremos almacenar la
primera palabra recibida.
Variables para almacenar resultado de una ejecución
Podemos almacenar dentro de una variable el resultado de la ejecución de un comando.
Los dos formatos soportados para ejecutar un comando y almacenarlo en una variable son:
VAR=$(comando)
VAR=`comando`
Ejemplo para almacenar el resultado de comandos.
Se crea la variable $Usuario
y se almacena el resultado de ejecutar el comando whoami
# Opcion 1 con $ (dolar)
Usuario=$(whoami)
# Opción 2 con comilla inclinada (`)
Usuario=`whoami`
Ejemplo para almacenar en $Texto
el contenido del archivo fichero.txt
Texto=`cat fichero.txt` # Opcion 1
read Texto < cat fichero.txt # Opcion 2
Un ejemplo en el que se almacena en $Salida
el resultado de stdout
y stderr
Salida=$(comando 2>&1)
Almacenamos en $VarA
los valores de otra variable $OTRA
, en $VarB
concatenamos Variables y Texto.
VarA=$OTRA
VarB="$V1 texto1 $V2 texto2 ..."
Ejemplo de cómo almacenar en la variable $OPCION
el primer parámetro recibido:
OPCION=$1
Se crea la variable $SORTEMARAP
y se almacenan concatenados y en orden inverso
los 3 primeros parámetros recibidos
SORTEMARAP="$3 $2 $1"
En el siguiente ejemplo en el paso 1 se crea $Cadena
y en el paso 2 se le asigna a ella misma su valor anterior y además se incluye un texto.
Cadena="En un lugar" #paso 1
Cadena="$Cadena de la Mancha" #paso 2
El resultado que se obtiene al final en $Cadena
es: En un lugar de la Mancha
Interpretar o No Interpretar variables
Cuando se utilizan de comillas simples en una variable, todo el contenido incluido
entre ellas se considera como no-interpretable y todo se trata como texto:
VAR=' NO procesar $V1'
echo $VAR
NO procesar $V1
Si utilizamos comillas dobles, en el caso de existir alguna variable entre ellas,
el valor de la variable encontrada es interpretado y sustituido por el valor que contenga:
VarA="En un lugar"
VarB='de la Mancha'
VarC="de cuyo nombre no quiero"
VarD=acordarme
TEXTO="$VarA $VarB $VarC $VarD"
echo $TEXTO
En un lugar de la Mancha de cuyo nombre no quiero acordame
Nota: Se pueden concatenar cadenas de texto tanto con comillas simples como dobles o sin ellas.
Mucho cuidado al usar las comillas simples y dobles, o es lo mismo usar "
que '
, ya que las comillas simples impiden que se procese dentro de ellas el valor de las variables.
NONO='$3 $2 $1'
En este caso se crea la variable y se guarda '$3 $2 $1'
. NO se guarda el valor de los parámetros
Arrays
Bash permite usar arrays, se pueden definir de varias formas:
declare ARRAY[n]
typeset ARRAY[n]
ARRAY=(a b c ... x y z)
Creación de Arrays vacios
Podemos usar declare
y typeset
:
declare Marca[9] (opcion 1)
typeset Marca[9] (opcion 2)
En ambos casos se crea la variable $Marca
que es un array vacio de 9 elementos.
Creación de Array sin definir
Podemos usar declare
:
declare -a Colores
En este caso se crea una variable $Colores
que es un array sin tamaño definido.
Arrays con elementor predefinidos
Simplemente usamos el formato ARRAY=(a b c ...)
:
Frutas=(Pera Manzana Platano)
Se crea la variable $Frutas
que es un array con las frutas indicadas.
Se almacena en la variable $Marca, que es un array, en su primer elemento (0) el valor indicado.
Escribir valor en un elemento del Array
Para escribir un valor en un elemento de un array, debemos indicar el nombre del
Array y entre corchetes la posición del elemento, seguido de un igual y despues el valor a asignar.
Sintaxis:
ARRAY[n]=valor
Por ejemplo para escribir un valor en el primer elemento (posicion 0) escribiremos:
# Fijar valor en el 1er elemento (posicion 0 del array)
Marca[0]="Tranqui-Cola"
Al igual que en todos los lenguajes la numeración si un array tiene n elementos, sus índices van del 0 a n-1.
# Array COCHE tendrá 2 elementos
COCHE[0]="Seat"
COCHE[1]="Opel"
Usando los Arrays
Recuerda: Cuando queriamos usar/leer variables, escribimos delante del nombre de la variable, el simbolo$
. Ejemplo:$MiVariable
Para los Arrays existe una sintaxis especial, para poder usarlos debemos usar ${}
:
${ARRAY[n]}
Acceder al elementon
del array${ARRAY[@]}
Devuelve todos los elementos del array (array completo)${#ARRAY[@]}
Devuelve el tamaño del array (numero de elementos)${!ARRAY[@]}
Índices del array
Veamoslo con algunos ejemplos.
Ejemplo 1: Devolver un elemento determinado:
Frutas=(Pera Manzana Platano) # Elementos 0, 1 y 2
echo ${Frutas[2]}
Platano
Ejemplo 2: Queremos ver todos los elementos del array:
Frutas=(Pera Manzana Platano)
echo ${Futas[@]}
Pera Manzana Platano
Ejemplo 3: Queremos contar o saber cuantos elementos tiene el array:
Frutas=(Pera Manzana Platano) # Elementos 0, 1 y 2 => 3 elementos
echo ${#Frutas[@]}
3
Ejemplo 4: Ver los índices y elementos reales del array. Debemos usar ${!ARRAY[@]}
Frutas=(Pera Manzana Platano)
echo ${!ARRAY[@]}
0 1 2
Ejemplo 5: Incluir nuevo elemento, y ver nº de elementos y sus índices.
Nota: Se puede dar la particularidad de incluir elementos en un array y dejar "huecos"
y tener posiciones sin usar.
Frutas=(Pera Manzana Platano)
echo "Num Elementos: ${#Frutas[@]}"
Num Elementos: 3
echo "Indices en uso: ${!Frutas[@]}"
Indices en uso: 0 1 2
# Incluimos nueva fruta (en 6ª posición)
echo "Num Elementos: ${#Frutas[@]}"
Num Elementos: 4
Frutas[5]="melocoton"
echo "Nº Elementos: ${#Frutas[@]}"
Nº Elementos: 4
echo "Indices en uso: ${!Frutas[@]}"
Indices en uso: 0 1 2 5
# Ver todos los elementos
echo ${Frutas[@]}
Pera Manzana Platano melocoton
Recorrer todos los elementos de un Array
Mediante los índices podríamos recorrer un array al que le falten elementos
intermedios y evitaríamos errores de elementos no existentes.
Ejemplo de bucle para recorrer todos los elementos de un array:
for i in $(echo ${!ARRAY[@]})
do
# Imprimir elemento con su indice y su valor
echo "ARRAY[$i] = ${ARRAY[$i]}"
done
Concatenar Arrays
Podemos unir varios arrays, simplemente debemos usar los paréntesis para definir
la variable que los almacena como array e incluir una coma ,
como elemento de concatenación:
Unix=('SCO', 'HP-UX', 'IRIX', 'XENIX')
Linux=('Debian', 'Suse', 'RedHat')
NIX=("${Unix[@]}", "${Linux[@]}")
echo ${NIX[@]}
SCO, HP-UX, IRIX, XENIX, Debian, Suse, RedHat
Estructuras de Control
IF
IF | IF - ELSE | IF - ELIF |
---|---|---|
if [ condicion ] |
if [ condicion ] |
if [ condicion ] |
then |
then |
then |
comandos |
comandos |
comandos |
fi |
else |
elif [ condicion ] |
comandos |
comandos |
|
fi |
else |
|
comandos |
||
fi |
FOR
FOR | FOR (Estilo C) |
---|---|
for variable [in lista] |
for ((i=0; i<5; i++)) |
do |
do |
comandos $variable |
comandos $variable |
done |
done |
WHILE y UNTIL
WHILE | UNTIL |
---|---|
while condicion |
until condicion |
do |
do |
comandos |
comandos |
done |
done |
CASE
case $VARIABLE in
valor1)
comandos_opcion-1
;;
valor2 | valor3 | valor4)
comandos_opcion-2-3-4
;;
valorN)
comandos_opcion-N
;;
*)
comandos_opcion-por-defecto
;;
esac
SELECT
select NumOpcion in [Lista]
do
comandos $NumOpcion
done
[Lista]
es un grupo de elementos separados por espacios
$NumOpcion
es el Nº de opcion escogida de la lista
Ejemplo para usar select
para un menu sencillo:
#!/bin/bash
# Menu Sencillo
PS3="Escoja una opcion: "
select Opcion in "Actualizar" "Listar" "Salir"
do
echo "escogio: $Opcion"
if [ $Opcion == "Salir" ]
then
break
fi
done
SALIR DE BUCLES
break
Sale del buclebreak N
Sale de N bucles anidadoscontinue
No finaliza los procesos/comandos que faltan del bucle y comienza la siguiente iteración
Operadores y Operaciones
Operadores Lógicos
- AND/Y:
&&
ó-a
- OR/O:
||
ó-o
- NOT/NEGACION:
!
\newline
Si queremos realizar un comparación conif
debemos escribirlo de la siguiente forma:
if [ condicion ]
Hay que respetar los espacios entre los corchetes.
Si queremos combinar condiciones los operadores lógicos van fuera de los corchetes:
if [ $a > $b ] || [ $j < $k ]
Operadores Aritméticos
+
Sumar-
Restar*
Multiplicar/
Dividir%
Resto de division (módulo)**
Elevar++
Incrementar--
Decrementar
Para realizar operaciones aritméticas se debe usar dos sintaxis diferentes:
$((operacion))
$[operacion]
Ejemplos de operaciones:
a) echo $((2+5)) => 7
b) echo $[21/7] => 3
c) i=1; echo $[++i] => i=2
d) echo $[3**2] => 9
Operadores de Cadenas
Se pueden realizar operaciones con las cadenas de texto directamente:
${#cadena}
Obtener longitud${cadena:N}
Extraer subcadena a partir de la posición N${cadena:N:M}
Extraer M caracteres a partir de la posición N${cadena#texto}
Elimina texto si coincide al principio de la cadena${cadena%texto}
Elimina texto si coincide al final de la cadena${cadena/texto1/texto2}
Reemplaza en la cadena la 1ª coincidencia de texto1 por texto2${cadena//texto1/texto2}
Reemplaza en la cadena todas las coincidencia de texto1 por texto2${cadena/#texto1/texto2}
Reemplaza en la cadena texto1 por texto2 si aparece al principio${cadena/%texto1/texto2}
Reemplaza en la cadena texto1 por texto2 si aparece al finalarray=(${cadena/delimitador/ })
Transformar una cadena en un array, segun el delimitador indicado
Comparadores
Comparadores Alfanuméricos/Texto
==
Igual!=
Distinto>
Mayor que<
Menor que-z
Es nulo-n
Longitud no es cero (mayor que 0)
Comparadores Numéricos
-eq
Igual que (=)-gt
Mayor que (>)-ge
Mayor o Igual que (>=)-lt
Menor que (<)-le
Menor o Igual que (<=)-ne
No igual/Distinto (!=)
Comparadores para ficheros (archivos y directorios)
-e
El Fichero ó Directorio existe-s
El Fichero existe y no está vacio-d
El Directorio existe (y no es un fichero)-f
El Fichero existe (y no es un directorio)-r
Fichero ó Directorio tiene permiso de Lectura-w
Fichero ó Directorio tiene permiso de Escritura-x
Fichero tiene permiso de Ejecución, y si es Directorio se puede acceder a él-O
Eres propietario del Fichero ó Directorio-G
El Fichero ó Directorio es de tu grupo-nt
El fichero es más reciente (new) que el otro (F1 -nt F2)-ot
El fichero es más antiguo (old) que el otro (F1 -ot F2)
Entrada/Salida y Operadores de Redirección
Dispositivos de Entrada y Salida
stdin
Es el dispositivo de Entrada (teclado).stdout
Es el dispositivo de Salida estandard (pantalla).stderr
Es el dispositivo de Salida de Errores (generalmente la pantalla).
Otros dispositivos especiales (/dev)
/dev/null
Dispositivo nulo, en el que todo lo que entra en él se descarta./dev/random
Dispositivo de generación de números pseudo-aleatorios./dev/urandom
Dispositivo de generación de números aleatorios de alta calidad/dev/zero
Dispositivo que genera únicamente caracteres nulos (0x00)./dev/full
Dispositivo que está siempre lleno. Usado para test de disco lleno. Al leer de él se comporta como/dev/zero
Redirectores
>
Enviar salida (stout y stderr) hacia ...<
Recibir contenido de ...
En Linux la entrada/salida está asociada a los siguientes descriptores de ficheros:
- stdin asociado a 0
- stdout asociado a 1
- stderr asociado a 2
Todos estos descriptores se pueden usar para capturar los datos que pasan por ellos y reenviarlos a variables o ficheros.
Ejemplo: Listar el contenido de /tmp/Carpeta1
y almacenarlo en el fichero /tmp/lista_ficheros.txt
ls -1 /tmp/Carpeta1 > /tmp/lista_ficheros.txt
Si tenemos acceso a la carpeta indicada, se almacenará en el fichero lista_ficheros.txt
la lista de todos los ficheros existentes de esa carpeta.
Separación de la salida
Si por algún motivo, no tenemos acceso a la ruta, o se produce cualquier error,
lo que obtendremos será un fichero en el que veremos mezclados la lista de ficheros que queriamos obtener,
junto los mensajes de error que se produzcan.
Podemos separar las diferentes salidas (stdout
y stderr
), usando sus descriptores.
Vemos algunos ejemplos:
- Enviar el resultado (stdout/1) del comando a un fichero y dejar que los mensajes de error salgan por pantalla:
ls -1 /tmp/Carpeta1 1> /tmp/lista_ficheros.txt
- Enviar el resultado (stdout/1) a un fichero y almacenar los errores (stderr/2) en otro fichero para revisar los logs:
ls -1 /tmp/Carpeta1 1> /tmp/lista_ficheros.txt 2>/tmp/errores.txt
- Enviar el resultado (stdout/1) a un fichero e ignorar los errores (stderr/2), evitando que salgan incluso por pantalla, redirigiendolos al dispositivo
/dev/null
:
ls -1 /tmp/Carpeta1 1> /tmp/lista_ficheros.txt 2>/dev/null
- Evitar que salgan ningun resultado, ni siquiera errores por pantalla:
ls -l /tmp/Carpeta1 >/dev/null 2>&1
Explicación. Primero se redirige el resultado a /dev/null
y la salida 2 (stderr) se reenvia a la 1 (stdout) a la que ya habiamos enviado previamente su salida a /dev/null con lo que no saldrá nada por pantalla.
Tabla resumen de Redirecciones
Redirección | Acción que se produce |
---|---|
> Fich |
Redirigir las dos salidas stdout y stderr hacia fich (fichero/dispositivo), |
se sobreescribe el fichero Fich cada vez. Se pierde el contenido anterior. | |
>> Fich |
Redirigir las dos salidas stdout y stderr hacia fich (fichero/dispositivo), |
se añade la salida del comando al final del fichero Fich y si no existe se crea. | |
1> Fich |
Redirigir salida estandard stdout hacia fich (fichero/dispositivo). |
2> Fich |
Redirigir salida de errores stderr hacia fich (fichero/dispositivo). |
1> Fich1 2> Fich2 |
Redirige salida estandar stdout hacia Fich1, la salida de errores (stderr) hacia Fich2, |
Fich1 y Fich2 pueden ser ficheros ó /dispositivos. | |
> Fich 2>&1 |
Redirigir salida stdout hacia fich y la de errores stderr a donde apunta stdout. |
2>&1 |
Redirigir salida de errores stderr a stdout. |
1>&2 |
Redirigir salida stdout a la salida de errores stderr. |
< Fich |
Lee el contenido del fichero Fich y lo envia al comando que le precede. |
Tuberias o Pipes
|
Reenvia la salida al comando que le sigue. Se le conoce como pipe o tubería.
Un ejemplo sencillo:
Tenemos una carpeta con varios ficheros y queremos su lista en orden alfabético ascendente y descendente:
$ ls -l
total 24
-rw-r--r-- 1 luisgulo luisgulo 1 ene 21 00:30 aaa
-rw-r--r-- 1 luisgulo luisgulo 4 ene 21 00:30 ddd
-rw-r--r-- 1 luisgulo luisgulo 2 ene 21 00:30 bbb
-rw-r--r-- 1 luisgulo luisgulo 3 ene 21 00:30 ccc
-rw-r--r-- 1 luisgulo luisgulo 6 ene 21 00:30 fff
-rw-r--r-- 1 luisgulo luisgulo 4 ene 21 00:30 ddd
-rw-r--r-- 1 luisgulo luisgulo 5 ene 21 00:30 eee
Usaremos l comando sort
que nos permite ordenar de modo normal o en modo inverso/reverso sort -r
un fichero o cualquier dato que reciba.
Podemos concatenar (pasar) la salida de un comando a otro mediante pipe |
# Orden normal
ls -1 | sort
aaa
bbb
ccc
ddd
eee
fff
# orden inverso/reverso
ls -1 | sort -r
fff
eee
ddd
ccc
bbb
aaa
Se pueden concatenar las salidas de un comando a otro mediante tuberias (pipes).
Ejemplo de 'tuberias' consecutivas:
RESOLUCION=$(xrandr | grep current | awk -F "," '{print $2}' | awk '{print $2"x"$4}')
echo $RESOLUCION
2646x1024
Funciones
Las funciones nos permite agrupar varias acciones o grupo de comandos repetitivos dentro de un script, para usarlas cuando las necesitemos.
Las funciones no se ejecutan al procesar el script, hay que llamarlas explícitamente para que se ejecuten.
Definir Funciones
Las funciones se puede definir de varias formas:
- Con la palabra reservada
function
:
function NombreFuncion {
comandos
...
}
- Mediante
()
omitiendofunction
:
NombreFuncion () {
comandos
...
}
Si se omite la palabra reservada function, hay que identificar la función obligatoriamente
mediante ()
para que se interprete como función.
En ambos formatos, los comandos a ejecutar en la función van entre llaves de inicio y cierre: { }
Ámbito de las variables en las Funciones
Variables de ámbito Global
Ámbito Global: Una variable definida en la parte principal del script (fuera de cualquier función) es accesible (se puede leer y escribir) desde dentro de cualquier función.
Se define normalmente como VARIABLE="valor"
Variables de ámbito Local
Ámbito Local: Una variable definida con el atributo local es unicamente accesible desde dentro de esa función.
Se define dentro de la función con la palabra reservada local
: local VARIABLE="valor"
Vamos a comprobar como se comportantan las variables según su ámbito.
Abre tu editor de texto (vi/vim, nano, joe) y copia y pega el siguiente contenido del script.
El fichero lo vamos a grabar con el nombre: que_pasa.sh
#!/bin/bash
function AlgoPasa {
local UNO="Texto uno"
DOS="Texto dos"
echo "DENTRO DE LA FUNCION -> Variable UNO = $UNO y Variable DOS = $DOS"
}
# --- Parte Principal del programa ---
UNO="1" # UNO es una variable Global
DOS="2" # DOS es tambien variable Global
# Mostramos las variables globales UNO y DOS recien inicializadas.
echo "ANTES DE LA FUNCION -> Variable UNO = $UNO y Variable DOS = $DOS"
# Llamamos a la función
AlgoPasa
# Mostramos de nuevo las variables globales UNO y DOS ...
echo "DESPUES DE LA FUNCION -> Variable UNO = $UNO y Variable DOS = $DOS"
Damos permisos de ejecución al script: chmod +x que_pasa.sh
Y lo ejecutamos para comprobar que hace con las variables:
./que_pasa.sh
ANTES DE LA FUNCION -> Variable UNO = 1 y Variable DOS = 2
DENTRO DE LA FUNCION -> Variable UNO = Texto uno y Variable DOS = Texto dos
DESPUES DE LA FUNCION -> Variable UNO = 1 y Variable DOS = Texto dos
Salida y Devolución de valores
Tras la ejecución de cualquier programa o aplicación de Linux, se genera un código de salida.
Este código sirve para poder comprobar si se ha realizado correctamente o se ha producido algún error.
La estandarización de estos códigos de salida es la siguiente:
- 0: El programa ha terminado correctamente y sin error.
- n: Donde n es cualquier valor distinto de cero (0). Indica que se ha producido algún error.
El desarrollador de la aplicación debe proporcionar la lista de códigos de salida ó códigos de error, para poder analizar lo que ha sucedido.
En un script de bash, debemos realizar lo mismo y finalizar siempre devolviendo un código de salida adecuado al resultado de la ejecución.
Para ello, la última línea de código que se ejecute debe emitir ese código.
Esto se realiza mediante el comando: exit num
Ejemplo:
#!/bin/bash
RUTA="$1" # Recibimos como parámetro una ruta
ls -l $RUTA 2>/dev/null
CodError=$? # Capturamos en variable $CodError codigo salida del comando anterior
if [ $CodError -eq 0 ] ; then
echo "Todo correcto"
else
echo "Atencion: Se produjo algun error"
fi
exit $CodError
NOTA: Recuerda que$?
devuelve el codigo de salida/error de la última ejecución. Lo podemos usar, consultar o imprimir en la consola mediante:echo $?
Códigos de error en Funciones
Las funciones, tambien pueden devolver un código de salida (un número).
En lugar de utilizar exit las funciones tienen que usar la instrucción return num
para notificar el código de salida/error que produzcan.
NOTA: return
únicamente permite devolver un número como resultado de su ejecución.
Si queremos que una función nos devuelva otro valor que no sea un número, por ejemplo una cadena de texto, un array o cualquier otra cosa, deberemo usar una varible global en la que almacenaremos el valor deseado.
Ejemplo de control de errores: une-2palabras.sh
#!/bin/bash
function Une {
local palabra1=$1 # Primer parametro recibido por la funcion
local palabra2=$2 # Segundo parametro recibido por la funcion
Cadena="$palabra1 $palabra2" # Cadena es una variable Global
}
# Recibo 2 palabras desde la linea de comandos
P1="$1"
P2="$2"
if [ -z $P1 ] || [ -z $P2 ]
then
codError=1
echo "Escriba 2 palabras por favor"
else
# LLamo a la funcion Une y le paso como parametros P1 y P2
Une $P1 $P2
echo "$Cadena"
codError=0
fi
exit $codError
ANEXO: Ejemplos de scripts en bash
A continuación se dejan una serie de scripts reales en bash, para su estudio.
Script para Leer un fichero línea a línea
leer_fichero.sh
#!/bin/bash
# Leer fichero linea a linea
FICHERO="$1"
if [ -f "$FICHERO" ]
#|| [ -n "$FICHERO" ]
then
# Leemos el fichero
while read LINEA
do
# hacemos con la LINEA leida lo que deseemos
echo "$LINEA"
done < $FICHERO
exit 0
else
echo "Debe indicar un fichero y que contenga algo"
exit 1
fi
Script para Grabar la Pantalla del escritorio en un fichero .avi
grabar_pantalla.sh
#!/bin/bash
# Este script graba la Pantalla 1
function Inicia {
FICH=$(tempfile --suffix=.avi)
zenity --info --text "** GRABACIÓN DE VÍDEO DE LA PANTALLA PRINCIPAL **\n\nSe va a grabar video del Escritorio 1\nNombre del vídeo:\n$FICH\n\n¡Para detener la grabación ejecute de nuevo el programa!" --no-wrap
NUMMONITORES=$(xrandr |grep '*'|wc -l)
# Solo se graba primer monitor
RESOLUCION=$(xrandr |grep current|awk -F "," '{print $2}' | awk -v NP=$NUMMONITORES '{print $2/NP"x"$4}')
ffmpeg -y -f x11grab -s $RESOLUCION -r 25 -i :0.0 $FICH -loglevel quiet &
}
function Finaliza {
killall -9 ffmpeg
FICH=$(ls -1tr /tmp/*avi|tail -1)
zenity --info --text "** GRABACIÓN DE VÍDEO DE LA PANTALLA PRINCIPAL **\n\n¡ GRABACIÓN DETENIDA!\nSu video esta en: $FICH" --no-wrap
}
function Ejecuta {
# Comprobamos si esta en ejecución
EJECUTANDOSE=$(ps aux |grep -i ffmpeg|grep -v grep|wc -l|awk '{print $1}')
if [ "$EJECUTANDOSE" = "1" ] ; then
Finaliza
else
Inicia
fi
}
# Lanzar el programa
Ejecuta
Script para facilitar sudo
a los usuarios
sudo_seguro.sh
Activa 'sudo' en modo seguro para tu usuario (pide contraseña para cada ejecución)
#!/bin/bash
# SUDO-SEGURO (Se pide la clave del usuario siempre)
# Este script crea un fichero para poder usar sudo al usuario
echo "Debe proporcionar la clave de root cuando se le solicite"
echo "$USER ALL=(ALL:ALL) ALL" > /tmp/autorizado_$USER; su -c "cp /tmp/autorizado* /etc/sudoers.d/."
sudo_comodo.sh
Activa 'sudo' en modo cómodo para tu usuario (No pide contraseña para la ejecución)
#!/bin/bash
# SUDO-COMODO (No se necesita la clave del usuario)
# Este script crea un fichero para poder usar sudo al usuario
echo "Debe proporcionar la clave de root cuando se le solicite"
echo "$USER ALL = (ALL) NOPASSWD: ALL" > /tmp/autorizado_$USER; su -c "cp /tmp/autorizado* /etc/sudoers.d/."
Script de ejemplo de uso de 'tput', 'printf' y arrays de texto
nieve.sh
#!/bin/bash
# Usamos tput (permite poner colores y/o mover el cursor por la pantalla)
LINEAS=$(tput lines)
COLUMNAS=$(tput cols)
# Lo declaramos como Array
declare -A CopoDeNieve
declare -A UltimosCopos
#Limpiamos antes de comenzar la nevada...
clear
# Funcion que mueve el copo por la pantalla (cayendo)
function mover_copo() {
i="$1"
if [ "${CopoDeNieve[$i]}" = "" ] || [ "${CopoDeNieve[$i]}" = "$LINEAS" ]; then
CopoDeNieve[$i]=0
else
if [ "${UltimosCopos[$i]}" != "" ]; then
printf "\033[%s;%sH \033[1;1H " ${UltimosCopos[$i]} $i
fi
fi
printf "\033[%s;%sH*\033[1;1H" ${CopoDeNieve[$i]} $i
UltimosCopos[$i]=${CopoDeNieve[$i]}
CopoDeNieve[$i]=$((${CopoDeNieve[$i]}+1))
}
# Aqui empieza el codigo principal para hacer nevar.
while :
do
# la variable del sistema $RANDOM devuelve un valor aleatorio :-D
i=$(($RANDOM % $COLUMNAS))
mover_copo $i
for x in "${!UltimosCopos[@]}"
do
mover_copo "$x"
done
sleep 0.1
done
Puedes descargarte este manual en formato PDF desde el siguiente enlace: