materials

Introducció

Un script és un fitxer de text que conté un comando en cada línia. Executar un script equival a escriure en la consola els comandos que conté un darrere l’altre.

Per a crear un script en GNU/Linux simplement obrim un editor de text i escrivim el seu codi, per exemple:

#!/bin/bash
echo "Hola, Mundo!"

Aquest scrit simplement mostra per consola el text “Hola, Mundo!”. La primera linia sempre indica el shell o intèrpret de comandos que volem que execute aquest script.

Ara el guardem amb el nom que vulguem i extensió .sh.

Per a executar-lo tenim 2 opcions:

Variables

Podem definir variables asignant-les un valor:

nom="Juan"
edat=25

Per a accedir al seu contingut anteposem al seu nom el caràcter $:

echo "$nom te $edat anys"

Podem incloure comentaris anteposant el símbol #.

Per a demanar un valor a l’usuari utilitzem read:

echo "Escriu el teu nom"
read nom
# El que ha escrit l'usuari es guarda en la variable nom
echo "Hola $nom"

Pas de paràmetres

Quan executem un scrip podem pasar-li paràmetres des de la línia de comandos. Aquest paràmetres es guardaran en unes variables anomenades $1 per al primer, $2 per al segon, etc.

En la variable $* tenim tots els paràmetres junts i en $# el compte dels paràmetres pasats.

Per exemple, si execute ./lista_nombres.sh Juan Marta Eva Pep sobre el següent script:

echo "El primer nom és $1"
echo "El tercer nom és $3"
echo "Has pasat $# paràmetres, que són $*"

el resultat serà:

El primer nom és Juan
El tercer nom és Eva
Has pasat 4 paràmetres, que són Juan Marta Eva Pep

Estructura condicional “if”

L’estructura de control if s’utilitza per prendre decisions basades en una condició. Si la condició especificada és certa, s’executa un bloc de codi i si no, s’executa un altre bloc de codi o es salta completament. La forma bàsica d’una sentència if és la següent:

if [ condició ]
then
  # Codi a executar si la condició és certa
else
  # Codi a executar si la condició és falsa
fi

La condició en un “if” pot ser qualsevol expressió que retorni un valor cert o fals. Pots utilitzar operadors i comparadors per construir condicions.

Operadors que podem utilitzar en la condició

Els operadors són diferents segons el tipus de les dades que estem comprovant. Alguns dels més comuns són:

Operadors de comparació de cadenes

Exemple:

if [ "$opció" = "sí" ]
then
  echo "Has triat sí."
fi

Operadors de comparació de números

Exemple:

if [ $edat -ge 18 ]
then
  echo "Ets major d'edat."
else
  echo "Ets menor d'edat"
fi

Operadors lògics

Exemple:

if [ ! $edat -ge 18 ]
then
  echo "Ets menor d'edat"
fi
if [ $edat -ge 18 -a $te_carnet = "sí" ]
then
  echo "Pots conduir"
fi

Operadors de comprovació d’arxius

Exemples:

if [ -d /ruta/a/la/carpeta ]
then
  echo "El directori existeix"
fi
if [ -s arxiu.txt ]
then
  echo "L'arxiu no està buit"
fi
if [ ! -r arxiu.txt ]
then
  echo "No tens permisos per a llegir l'arxiu"
fi

Anidació de sentències “if”

Pots anidar múltiples sentències “if” per gestionar condicions més complexes. Aquí tens un exemple d’anidació:

if [ $edat -ge 18 ]
then
  if [ "$té_licència" = "sí" ]
  then
    echo "Pots conduir."
  else
    echo "Ets major d'edat però no tens llicència."
  fi
else
  echo "Ets menor d'edat."
fi

Això permet gestionar casos en què hi ha múltiples condicions que s’han de complir o no.

En el cas de anidar múltiples “if” en el “else” podem juntarles amb “elif”. Per exemple el codi:

if [ $edat -gt 18 ]
then
  echo "Ets menor d'edat"
else 
  if [ "$té_licència" = "sí" ]
  then
    echo "Pots conduir."
  else
    echo "Ets major d'edat però no tens llicència."
  fi
fi

podríem fer-ho més senzill amb “elif”:

if [ $edat -gt 18 ]
then
  echo "Ets menor d'edat"
elif [ "$té_licència" != "sí" ]
then
  echo "Ets major d'edat però no tens llicència"
else
  echo "Pots conduir"
fi

exit

El comando exit finalitza l’execució d’un script i no s’executarà cap codi despres del mateix. Normalment se li pasa un número que indica el resultat del script (exit 0 sol significar que el script ha acabat correctament i exit 1 o qualsevol número major que 0 indica que ha hagut un error).

Això ens permet poder fer comprovacions sense haver de anidar molts if...else if, per exemple:

if [ -e $1 ]
then
    echo "No existeix el fitxer $1"
    exit 1
fi
# resta del codi...

Estructura condicional “case”

Si volem comparar una variable amb més de 2 valors podem anidar varios if o utilitzar una sentència case que compara una variable amb diversos patrons i executa el codi basat de la coincidència. Per exemple:

#!/bin/bash

echo "Escull una opció (A, B, o C):"
read opcio

case $opcio in
  A)
    echo "Has seleccionat l'opció A."
    # Afegiu aquí el codi per a l'opció A
    ;;
  B)
    echo "Has seleccionat l'opció B."
    # Afegiu aquí el codi per a l'opció B
    ;;
  C)
    echo "Has seleccionat l'opció C."
    # Afegiu aquí el codi per a l'opció C
    ;;
  *)
    echo "Opció no vàlida."
    ;;
esac

Al acabar cada bloc has de posar ;; per a que no continue l’execució del codi del bloc següent. L’ús de *) proporciona una opció per qualsevol entrada que no coincidisca amb cap de les anteriors.

Els bucles

Per a fer que un codi s’execute més d’una vegada (per exemple per a cada paràmetre o per a cada element d’un arxiu) tenim els bucles. El bash tenim 3 tipus de bucles: for, while i until:

El bucle “for”

L’estructura de control for es fa servir per recórrer una llista d’elements i executar una sèrie d’instruccions per a cada element de la llista.

La seua sintaxi bàsica és la següent:

for variable in llista
do
  # Codi a executar per a cada element de la llista
done

On:

Exemple: volem mostrar cada paràmetre pasat (recirda que la llista de paràmetres la tenim en $*)

for param in $*
do
  echo "Paràmetre: $param"
done

Exemple: volem recórrer una llista de noms que tenim guardats en una variable i imprimir-los:

noms=("Ana" "Bob" "Carlos" "David")

for nom in "${noms[@]}"
do
  echo "Hola, $nom"
done

Aquest for recorre la llista de noms i imprimeix un missatge per a cada un d’ells.

Podem fer que la llista siga una sequència de números. Per exemple, aquest bucle for escriurà els números de l’1 al 5:

for numero in {1..5}
do
  echo "Número: $numero"
done

També pots utilitzar un for per recórrer tots els arxius i subdirectoris d’un directori. Per exemple, aquest for llistarà els arxius d’un directori:

directori="/ruta/al/directori"

for arxiu in "$directori"/*
do
  echo "Arxiu: $arxiu"
done

També podem utilitzar for amb una variable contadora que es pot incrementar o disminuir en cada iteració, igual que en la majoria de llenguatges de programació. Per exemple:

for ((i=1; i<=5; i++))
do
  echo "Iteració $i"
done

Aquest for comença amb i=1, incrementa i en cada iteració i s’atura quan i és major que 5.

El bucle “while”

Aquest bucle continua executant el codi que conté mentre que la seua condició siga certa, per exemple:

#!/bin/bash
numero_secret=42

echo "Adivina el número secret (entre 1 i 100):"
read intent

while [ $intent -ne $numero_secret ]
do
  echo "No has encertat. Intenta-ho de nou."
  read intent
done

echo "Ho has encertat!!!"

El bucle “until”

És igual que el “while” el codi s’executa mentre que la condició siga falsa. Per exemple:

#!/bin/bash

contrasenya_correcta="secret123"
intents=0

echo "Introdueix la contrasenya correcta:"

until [ "$intent" = "$contrasenya_correcta" ]
do
  read -s intent  # El modificador -s oculta la contrasenya mentre l'usuari l'escriu
  echo  # Afegeix un salt de línia després de l'entrada de la contrasenya

  if [ "$intent" = "$contrasenya_correcta" ]
  then
    echo "Contrasenya correcta. Accés concedit."
  else
    echo "Contrasenya incorrecta. Intenta-ho de nou."
  fi

  intents=$((intents + 1))
done

echo "Nombre d'intents: $intents"

Fixa’t que $((...)) calcula una expressió.

Aturar el bucle: break i continue

Dins d’un bucle les comandes continue i break fan que es deixe d’executar el codi:

continue

Exemple:

for numero in {1..5}
do
  if [ $numero -eq 3 ]
  then
    continue  # Salta la iteració actual si el número és 3
  fi
  echo "Número: $numero"
done

Aquest bucle imprimirà els números de l’1 al 5, però saltarà la iteració quan numero sigui igual a 3, és a dir, mostrarà els números 1, 2, 4 i 5.

break

Exemple:

for numero in {1..10}
do
  if [ $numero -eq 5 ]
  then
    break  # Surts del bucle quan el número és 5
  fi
  echo "Número: $numero"
done

Aquest bucle imprimirà els números de l’1 al 4 i, en arribar al 5, sortirà del bucle sense completar les iteracions restants.

En resum, continue s’utilitza per saltar a la següent iteració dins del bucle, mentre que break s’utilitza per sortir del bucle.

Llegir dades d’un fitxer

Moltes vegades hem de fer un script per a treballar amb dades que es troben en fun fitxer de text. La forma més habitual d’accedir a les dades del fitxer és utilitzant un bucle while amb la següent sintaxis:

while IFS="delimitador" read -r camp1 camp2 camp3 ...
do
  # Ací el nostre codi
done < "nom_del_fitxer"

on IFS indica el caràcter utilitzat en el fitxer per a delimitar els diferents camps dins de cada línia.

Per exemple, tenim el fitxer usuaris.txt amb els noms i contrasenyes d’usuaris que hem de crear:

juan:1234
marta:5678

El script que crearia aquest usuaris podria ser alguna cosa com:

#!/bin/bash

fitxer_usuaris="usuaris.txt"

while IFS=":" read -r nom_usuari contrasenya
do
  # Comprova si l'usuari ja existeix
  if id "$nom_usuari" &>/dev/null
  then
    echo "L'usuari $nom_usuari ja existeix."
  else
    # Crea l'usuari amb la contrasenya
    useradd -m -p $(openssl passwd -1 "$contrasenya") "$nom_usuari"
    echo "L'usuari $nom_usuari ha estat creat."
  fi
done < "$fitxer_usuaris"

Funcions

Quan el nostre codi és molt gran podem “dividir-ho” en funcions per a que siga més clar. Les funcions són blocs de codi que poden ser cridats i reutilitzades en diferents parts d’un script. Cada funció és com un xicotet script al qual podem cridar dins del nostre script.

saludar() {
  echo "Hola, $1."
}

saludar "Juan"
saludar "Marta"

saludar és una funció que mostra un salut amb el primer paràmetre que li pasem. La cridem posant el seu nom. La eixida serà:

Hola, Juan
Hola, Marta

Les funcions poden retornar un valor amb el comando return:

doble() {
  resultat=$((2 * $1))
  return $resultat
}

doble 5
echo "El doble de 5 és $?"

Fixa’t que la variable especial $? emmagatzemma el resultat de l’ultim comando executat.

Altres sentències útils

Execució de comandos

Podem executar una comando posant-lo en una línia del nostre script. Per exemple per a obtindre el UID de l’usuari batoi farem:

grep "batoi" /etc/passwd | cut -d ":" -f 1

Però què pasa si vuig guardar el resultat en una variable. El següent codi NO FUNCIONARIA:

uidbatoi=grep "batoi" /etc/passwd | cut -d ":" -f 1

Per a executar un comando DINS d’una línia del script (per a guardar el resultat en una variable o per a usar-ho en una condició) hem d’escriure-lo dins de $(...), per exemple:

uidbatoi=$(grep "batoi" /etc/passwd | cut -d ":" -f 1)

echo "$uidbatoi" # Mostrarà el resultat del comando

o també

if [ $(grep "batoi" /etc/passwd) = "" ]
then
  echo "No existe el usuario batoi"
fi

Resultat de l’últim comando: $?

El valor retornat per l’ultim comando executat (o la última funció cridada) es guarda en la variable especial $?. Si es tracta d’un comando del sistema que retorne 0 significa que s’ha executat correctament i si retorna altre número és que ha hagut un error. Per exemple:

cp alumnes.txt ./prova
if [ $? -eq 0 ]
then
  echo "El fitxer s'ha copiat correctament"
else
  echo "S'ha produit l'error $? copiant el fitxer"
fi

Calcular una expresió

Per a calcular una expressió utilitzem $((...)):

comptador=1
comptador=$((comptador + 1))
echo "$comptador"   # Mostrarà 2

Depurar codi

Per a depurar un script podem posar al principi el comando:

set -x

això farà que es mostre cada línia abans d’executar-se.