La shell Bash ens permet crear programes útils per realitzar tasques repetitives a nivell de sistema operatiu; automatitzar còpies de seguretat, esquemes de directoris, cerques de patrons complexes, gestionar usuaris i els seus permisos ...

Què són els scripts dels SO?

Els intèrprets d’ordres o shells són programes que permeten la interacció dels usuaris amb el S.O. i el seu hardware. També incorporen llenguatges de programació per a crear programari que anomenem scripts (en català, guions).

Els scripts de shell són molt útils per fer tasques de gestió i administració del sistema (com monitoritzar les tasques en execució) i altres treballs repetitius que no requereixen un llenguatge de programació més sofisticat (per exemple, còpies de seguretat)

Intèrprets d'ordres a Windows

Command Prompt (cmd.exe): Aquest és l'intèrpret d'ordres clàssic de Windows. Equivalent i compatible al COMMAND.COM d'MS-DOS i versions de Windows de 16 i 32 bits. Té dos modes d'execució:

  • Interactiu: l’usuari escriu les ordres per ser executades
  • Mode per lots (batch): executa una sentència predefinida d’ordres guardada en un .bat

Windows PowerShell: interfície de consola per a S.O. Windows llançada el 2006, incorporat des de Windows 7, que té com a utilitat principal l’automatització de tasques administratives més avançades. Té una sintaxi més moderna, basada en la programació orientada a objectes.

Els 2 intèrprets coexisteixen i s'utilitzen per propòsits diferents.

Intèrprets d'ordres a Linux

El shell Bash (Bourne-Again Shell) és l’intèrpret d’ordres predeterminat de gairebé totes les distribucions GNU/Linux, així com de Mac OS X, i pot executar-se en la majoria dels sistemes operatius tipus Unix.

Se'n poden instal·lar d'altres a Linux, com Zsh que incorpora algunes funcionalitats més avançades i més personalització.

Intèrprets d'ordres a Mac

Tant macOS (originalment OS X) com Linux tenen les seves arrels en Unix, un sistema operatiu desenvolupat als anys 70 amb llicència BSD () i àmpliament utilitzat per implantar sistemes operatius multiusuari els anys 80. Aquesta herència comuna ha fet que molts components i idees siguin compartides entre ambdós sistemes operatius, incloent-hi els shells.

Quan Apple va llançar OS X (ara macOS) el 2001, es va basar en un nucli Unix BSD i va triar Bash (Bourne Again Shell) com a shell predeterminat per la seva popularitat i compatibilitat amb scripts existents.

Un dels dubtes més freqüents quan s'estudien les llicències dels SO és com pot ser que Mac i Linux, que tenen un enfocament totalment oposat respecte les llibertats del usuaris, poden compartir els mateixos intèrprets d'ordres, part del programari i la arquitectura.

Apple va poder utilitzar components del sistema operatiu BSD (Berkeley Software Distribution), una variant d'Unix desenvolupada a la Universitat de Califòrnia, Berkeley. BSD té una llicència més permissiva que la GPL (General Public License) de Linux, que permet l'ús i la distribució de codi modificat amb menys restriccions. És a dir, que permet llicenciar el programari derivat amb una llicència propietària d'Apple.


Com executar un script a Linux ?

Abans de començar a entendre la sintaxi del llenguatge d'scripts de Bash. El que farem és crear un nou script, a partir d'un fitxer amb instruccions, li donarem els permisos per a que sigui executable i finalment l'executarem.

Per a crear el teu script amb la shell de Linux obre el terminal (cerca el programa que té una pantalla negre com a icona , o usa la drecera Ctrl+Alt+T)

  1. Crea el fitxer hacking.sh amb un editor com nano o gedit, amb extensió sh.
#!/bin/bash
for i in {1..100}
do
 echo "Hacking. $i% completed."
 sleep 2
done
echo "Hacking completed :)"
  1. Converteix-lo a programa, assignant permisos d'execució.
chmod u+x hacking.sh
ls -l
  1. Executa i a gaudir :)
./hacking.sh

Resultat esperat: Cada 2 segons sortirà el mateix missatge, a excepció del número % completed, que gràcies al bucle s'anirà incrementant.

Hacking. 1% completed.
Hacking. 2% completed.
Hacking. 3% completed.

Una alternativa als passos 2 i 3, si no ets un usuari amb permisos d'administrador (no estàs al grup sudo), és aquesta:

sh hacking.sh

Variables locals i variables d'entorn

Al shell, una variable és un nom que representa un valor que volem guardar pel seu posterior ús en un o més punts dels nostres programes.

Poden ser:

  • Locals: només són visibles pel shell en el qual estem treballant, no són visibles per cap shell fill.

Per a declarar variables és important recordar que:

  • Les variables d'entorn no poden tindre espais, i si volem assignar un valor amb espai hem de "entrecomillar", o bé precedir l'espai per un caràcter d’escapament.
  • Per mostrar la variable mitjançant la funció echo l'hem de precedir amb el símbol de dòlar $.
NOM='Katy Perry'
echo $NOM

Katy Perry

NOM=Katy\ Perry
echo $NOM

Katy Perry

Per a eliminar una variable d'entorn podem usar la comanda unset.

Per exemple:

unset $NOM
echo $NOM

D’aquesta manera podrem fer inteccacions com les següents:

variable1='Sabates negres'
variable2=20.5
echo "L’article $variable1 costa $variable2 euros"

L’article Sabates negres costa 20.5 euros

  • D'entorn: són visibles tant pel shell pare com pels shells fills. Per defecte disposem de variables d'entorn predefinides que ens poden resultar útils per a la programació d'scripts.
export NOM_VAR = valor

Hi ha diverses ordres relacionades amb les variables:

Ordre set Permet veure totes les variables (locals i d’entorn) definides en una sessió.

Ordre env Permet veure les variables d’entorn definides en una sessió.

Exemples típics:

  • USER: Nom de l'usuari actiu
  • HOME: Directori de l'usuari (per defecte /home/USER )
  • PWD: Directori on es troba l'usuari
  • PATH: Llista de directoris on cercar programes abans de buscar-los al disc. Permet executar un programa sense dir-li la ruta sencera.

Si volem veure el valor d'una variable en concret; la instrucció és igual que si fos una de local.

echo $PWD

Possible resultat:

/home/mamoros/Documents/

Si volem guardar totes les variables d'entorn en un fitxer usem l'operador de redirecció >:

echo env > vars_ent.txt 

Caràcters especials.

Hi ha caràcters que per a la shell tenen un significat especial. Existeixen diferents tècniques per tal que la shell ignori aquest significat o el tingui en compte:

**** anul·la el significat especial del caràcter que va darrera

‘’  anul·la el significat especial de tots els caràcters que estiguin dins les cometes

“”  anul·la el significat especial de tots els caràcters excepte: $ \ `` “”

Exemple: echo “Això és \”una prova\””

Exemple: echo “Sóc el/la $LOGNAME i estic a $PWD: $PWD”


Tractament de la data del sistema.

Saber la data i hora del sistema en funció de la nostra zona horària és molt important per a certes tasques: monitoritzar la activitat d'un ordinador servidor, posar nom i data de les còpies de seguretat...

Per mostrar la data del sistema en format llarg ho podem fer cridant la funció del sistema date; fixa't que per a què es mostri el resultat de la funció l'hem de rodejar entre parèntesis i un símbol de dòlar: $(date)

echo “La data del sistema és: $(date)

Si volem un format més curt i propi de la nostra zona (que es mostri en format dd/mm/aaaa i la hora per separat) ho podem fer assignant el format que volem com a paràmetre.

data=$(date +%d/%m/%Y)
hora=$(date +%H:%M:%S)
echo "Data en format ddmmaaaa: $data"
echo "Hora en format hh:mm:ss: $hora"

Data en format ddmmaaaa: 06/08/2024 Hora en format hh:mm:ss: 22:40:33

En ocasions el sistema no detectarà automàticament el nostre fus horari (això passa en màquines virtuals sobretot) i si és el cas el podrem consultar i editar:

timedatectl
sudo timedatectl set-timezone Europe/Madrid

Recurs ampliació.

[https://learning.lpi.org/es/learning-materials/102-500/108/108.1/](Certificació Linux LPI 102, zones horàries)

Exercici 1. Crea un script que crei una carpeta dins del directori actual (on et trobes) amb el nom projecte-<data_avui>-<hora_avui> i al final informi de la seva creació correcta. Recorda que pots usar instruccions com mkdir i cd.

Observació Per ara no farem el tractament de què fer si existeix una carpeta igual (ja el farem un cop vists els condicionals)

ex1-scr.sh

data=$(date +%d/%m/%Y)
hora=$(date +%H:%M)
carpeta="projecte-${data}-${hora}"
mkdir $carpeta
echo "Carpeta del projecte $carpeta creada correctament"

Possible resultat execució:

$ nano ex1-scr.sh
$ chmod u+x ex1-scr.sh
$ ./ex1-scr.sh
Carpeta del projecte projecte-18/02/2025-22:48 creada correctament

Pas de paràmetres.

Un script és un fitxer que conté comandos Bash executables. Els fitxers ocults de configuració de Bash com .bash_profile o .bashrc, vistos en la part de la gestió d’usuari són exemples de scripts.

Principalment Bash utilitza variables de tipus cadena de caràcters. Això li diferencia d'altres llenguatges de programació on les variables poden tenir diferents tipus predefinits. Encara que aquest és el tipus per defecte de les variables de Bash, més endavant veurem que les variables en Bash també poden tindre altres tipus, com per exemple números amb els quals realitzar operacions aritmètiques; tal i com passa amb els llenguatges de tipat dinàmic com Python o Javascript.

Per conveni les variables d'entorn exportades (les que passem als subprocesos) s'escriuen en majúscules, i les que no exportem en minúscules.

Aquesta regla, més que per la manera interactiva, és especialment seguida pels scripts i funcions que començarem a programar ara.

Els paràmetres posicionals són els encarregats de rebre els arguments d'un script i els paràmetres d'una funció.

Els seus noms són 1, 2, 3, etc., amb el que per a accedir a ells utilitzarem, com normalment, el símbol $ de la forma $1, $2, $3, etc.

A més tenim el paràmetre posicional 0 ($0) que emmagatzema el nom del script on s'executa.

#!/bin/bash
#Exemple de script que rep paràmetres y les imprimeix
echo "El script $0"
echo "Rep els arguments $1 $2 $3 $4"

Si ho hem guardat en un fitxer anomenat rep amb el permís d'execució activat podem executar-lo així:

chmod u+x rep
./rep hola adios

Resultat:

El script ./rep
Rep els arguments hola adios

Codis de terminació.

En UNIX les comandes acaben retornant un codi numèric al qual es diu codi de terminació (exit estatus) que indica si el comando va tindre èxit o no.

Encara que no és obligatori que siga així, normalment un codi de terminació 0 significa que el comando va acabar correctament, i un codi entre 1 i 255 correspon a possibles codis d'error.

Per a llegir el codi de terminació de l'últim comando executat disposem de la variable ?, el valor del qual és $?.

Per exemple:

$ cd direrroneo
-bash: cd: direrroneo: No such file or directory
$ echo $?

1

La variable ? ha de ser llegida junt després d'executar la comandes, doncs és molt típic guardar el seu valor en una variable ct=$? per al seu posterior ús.


Condicions: if, elif, else

La sentència condicional if permet executar diferents ordres en funció d'una condició.

En Bash, la seva estructura bàsica és:

if condicio
then
    # Sentències si la condició és certa
elif condicio
then
    # Sentències si la segona condició és certa
else
    # Sentències si cap condició anterior és certa
fi

Exemple: Pregunta a l'usuari un número i comprova si un número és positiu, negatiu o 0.

#!/bin/bash

echo "Introdueix un número:"
read num

if [[ $num -gt 0 ]]
then
    echo "El número és positiu."
elif [[ $num -lt 0 ]]
then
    echo "El número és negatiu."
else
    echo "El número és zero."
fi

Lectura de fitxers.

Ara veurem com llegir un fitxer que tinguem al disc dur. Comprovarem que sigui un fitxer vàlid.

Creem un fitxer de prova amb el nano o l'editor que volgueu:

prova.csv

nom,edat,ciutat
Joan,25,Barcelona
Anna,30,Girona
Marc,22,Tarragona
Fanny,21,Barcelona

Per llegir-lo, en cas que existeixi, ho fem així:

#!/bin/bash
if [[ -f prova.csv ]]
then
    cat prova.csv
else
    echo "El fitxer prova.csv no existeix!"
fi

El paràmetre -f verifica que prova.csv sigui un fitxer vàlid.

La comanda cat mostra tot el contingut del fitxer.

Si necessites una solució més robusta, per a qualsevol fitxer passat per paràmetre:

#!/bin/bash

# Comprovar si s'ha passat un fitxer
if [[ $# -ne 1 ]]; then
    echo "Ús: $0 prova.csv"
    exit 1
fi

fitxer="$1"

# Comprovar si el fitxer existeix
if [[ ! -f "$fitxer" ]]; then
    echo "Error: El fitxer '$fitxer' no existeix."
    exit 1
fi

# Mostrar el contingut del CSV (sense la capçalera)
cat "$fitxer"

Exemple lectura/escriptura de fitxers i directoris.

En moltes ocasions necessitem crear una estructura de directoris per a projectes.

Per exemple; crear un script al qual li passis el nom d’un projecte.

Si no existeix una carpeta amb el nom de projecte crearà: a. La carpeta amb el nom del projecte b. Les subcarpetes: css, img, js, static. c. Un fitxer anomenat readme.md que contingui: i. A la primera línia «#Documentació del projecte» ii. A la segona línia la **data d’avui.v iii. La tercera línia el nom de l’usuari.

Si el nom de la carpeta existeix mostrarà un missatge d’avís i no la crearà.

ExCreaprojecte.sh

#!/bin/bash
if [ ! -d $1 ]
  then 
    echo "Creem projecte web amb el nom " $1
    mkdir $1
    cd $1
    echo "Creem carpetes "
    mkdir {css,img,js,static}
    echo "Creem fitxer descriptor"
    touch readme.md
    echo "#Documentació del projecte" >> readme.md
    NOW=$(date +"%d-%m-%Y")
    echo "Data creació: $NOW" >> readme.md
    echo "Creat per: $USERNAME" >> readme.md
    cd ..
    echo "Projecte $1 creat correctament."
else
   if [ $# -eq 0 ]
    then
      echo "Has d'introduir el nom del projecte web que vols crear."
      echo "$0 prj-demo"
   else 
     echo "La carpeta del projecte $1 ja existeix."
   fi
fi

En aquest script hem posat a prova tot el que hem après anteriorment.

Provem les 3 casuïstiques:

  • Què passa si no li passem cap carpeta
  • Si li passem carpeta que no existeix (crea el projecte)
  • Si li passem carpeta que existeix.
isard@ubuntu:~/Baixades/Solucions_Scripts$ ./ExCreaprojecte.sh
Has d'introduir el nom del projecte web que vols crear.

isard@ubuntu:~/Baixades/Solucions_Scripts$ ./ExCreaprojecte.sh prj-demo

isard@ubuntu:~/Baixades/Solucions_Scripts$ ./ExCreaprojecte.sh dawbio1
Creem projecte web amb el nom dawbio1
Creem carpetes
Creem fitxer descriptor
Projecte dawbio1 creat correctament.

isard@ubuntu:~/Baixades/Solucions_Scripts$ ls dawbio1/
css  img  js  readme.md  static

isard@ubuntu:~/Baixades/Solucions_Scripts$ cat dawbio1/readme.md
#Documentació del projecte
Data creació: 28-02-2024
Creat per: isard

isard@ubuntu:~/Baixades/Solucions_Scripts$ ./ExCreaprojecte.sh dawbio1
La carpeta del projecte dawbio1 ja existeix.

Sintaxi comparadors i comprovació atributs de fitxers.

Revisa aquest article per aprendre tots els possibles comparadors de condicions, com -gt. O verificadors d'atributs de fitxers com -f.

-https://atareao.es/tutorial/scripts-en-bash/condicionales-en-bash/


Instal·lació programari necessari.

En ocasions per a què un programa funcioni necessitem que l'equip de l'usuari tingui instal·lat algún programa que no ve preinstal·lat a la seva distribució.

Per exemple, per mostrar els directoris en forma d'arbre necessitarà el tree, o per baixar-se un fitxer d'Internet el curl.

Per estalviar-li feina, podem instal·lar-lo automàticament en cas que no el tingui:

#!/bin/bash
if ! command -v curl &>/dev/null; then
    sudo apt update && sudo apt install -y curl
fi

curl -L -O https://upload.wikimedia.org/wikipedia/commons/e/ee/GNU%2BLinux.png

Ja hauries de tenir el programa curl i la imatge .png descarregada.

Pot ser que consideris que forçar a l'usuari executar scripts que forcin la instal·lació de programari sigui invasiu, però aquesta és una pràctica molt habitual, per exemple per a desenvolupadors, que volen centrar-se que la seva web funcioni sense preocupar-se gaire en aspectes del sistema operatiu.

Per exemple gestors de llibreries Javascript com nvm o bun.

Programari distribucions diferents a les basades en Debian/Ubuntu

Si volem portar l'script en una distribució diferent a les que hem vist com és el cas de Fedora o Redhat totes les comandes haurien de ser igual excepte el bloc de l'apt.

Aquí tenim una mostra del que hauriem de fer per a què el nostre script també funcioni en aquestes distribucions, que també són populars.

echo "⚠️ Curl no està instal·lat. Intentant instal·lar-lo..."
    if command -v apt &>/dev/null; then
        sudo apt update && sudo apt install -y curl
    elif command -v dnf &>/dev/null; then
        sudo dnf install -y curl
    else
        echo "❌ No es pot instal·lar curl automàticament. Instal·la'l manualment."
        exit 1
    fi

Comprovació que un fitxer ja ha estat descarregat.

Si volem crear un programa que llegeixi informació d'algun fitxer amb molta informació i aquest ja l'hem descarregat prèviament, per estalviar temps i un accés innecessari a Internet (que gasta electricitat) ens anirà molt bé usar un condicional per a què:

Cas 1.1 En el cas que el fitxer no el tinguem al disc el descarregui.

Cas 1.2 Si el fitxer ja l'hem descarregat abans (ho podem saber pel nom) no el descarregui.

Cas 2 En qualsevol dels 2 casos hauria de mostrar a l'usuari la informació del fitxer.

Exemple pràctic.

Volem crear un programa que descarregui una seqüència d'Uniprot passada per paràmetre i mostri per pantalla el seu contingut (el format d'aquestes comença per P, per exemple P12345) i es baixaran en format .fasta Si ja s'ha descarregat abans no cal que ho faci. Si l'usuari no envia cap identificador el programa avisarà un text com aquest: "❌ Ús: $0 <UniProt_ID>" i es tancarà.

La URL on es pot descarregar qualsevol proteïna pel seu UNIPROT_ID té la següent forma:

URL="https://rest.uniprot.org/uniprotkb/${UNIPROT_ID}.fasta"

Aquí tenim una solució completa a aquest problema:

#!/bin/bash

# Comprova si s'ha proporcionat un ID
if [ -z "$1" ]; then
    echo "❌ Ús: $0 <UniProt_ID>"
    exit 1
fi

# Instal·la el curl per baixar la proteïna si l'usuari del SO no el té.
if ! command -v curl &>/dev/null; then
    sudo apt update && sudo apt install -y curl
fi

# Identificador de la proteïna
UNIPROT_ID="$1"

# Nom del fitxer de sortida
OUTPUT_FILE="${UNIPROT_ID}.fasta"

# URL d'UniProt per descarregar en format FASTA
URL="https://rest.uniprot.org/uniprotkb/${UNIPROT_ID}.fasta"

# Comprovar si el fitxer ja existeix
if [ -f "$OUTPUT_FILE" ]; then
    echo "ℹ️ El fitxer $OUTPUT_FILE ja existeix. No es farà la descàrrega."
else
    echo "📥 Descarregant la proteïna $UNIPROT_ID en format FASTA..."
    curl -L -s -o "$OUTPUT_FILE" "$URL"

    # Comprovar si la descàrrega ha estat exitosa
    if [ ! -s "$OUTPUT_FILE" ]; then
        echo "❌ Error en descarregar la proteïna. Comprova l'ID proporcionat."
        rm -f "$OUTPUT_FILE"  # Esborra el fitxer buit si la descàrrega ha fallat
        exit 1
    fi
    echo "✅ Descarrega completada! Fitxer guardat com: $OUTPUT_FILE"
fi

# Mostrar el contingut del fitxer
echo "📄 Contingut de $OUTPUT_FILE:"
cat "$OUTPUT_FILE"

I els resultats que evidencien que funciona:

✅ Descarrega completada! Fitxer guardat com: P12345.fasta
📄 Contingut de P12345.fasta:
>sp|P12345|AATM_RABIT Aspartate aminotransferase, mitochondrial OS=Oryctolagus cuniculus OX=9986 GN=GOT2 PE=1 SV=2
MALLHSARVLSGVASAFHPGLAAAASARASSWWAHVEMGPPDPILGVTEAYKRDTNSKKM
NLGVGAYRDDNGKPYVLPSVRKAEAQIAAKGLDKEYLPIGGLAEFCRASAELALGENSEV
VKSGRFVTVQTISGTGALRIGASFLQRFFKFSRDVFLPKPSWGNHTPIFRDAGMQLQSYR
YYDPKTCGFDFTGALEDISKIPEQSVLLLHACAHNPTGVDPRPEQWKEIATVVKKRNLFA
FFDMAYQGFASGDGDKDAWAVRHFIEQGINVCLCQSYAKNMGLYGERVGAFTVICKDADE
AKRVESQLKILIRPMYSNPPIHGARIASTILTSPDLRKQWLQEVKGMADRIIGMRTQLVS
NLKKEGSTHSWQHITDQIGMFCFTGLKPEQVERLTKEFSIYMTKDGRISVAGVTSGNVGY
LAHAIHQVTK
isard@ubuntu:~/prova$ ./uniprot.sh P12345
ℹ️ El fitxer P12345.fasta ja existeix. No es farà la descàrrega.
📄 Contingut de P12345.fasta:
>sp|P12345|AATM_RABIT Aspartate aminotransferase, mitochondrial OS=Oryctolagus cuniculus OX=9986 GN=GOT2 PE=1 SV=2
MALLHSARVLSGVASAFHPGLAAAASARASSWWAHVEMGPPDPILGVTEAYKRDTNSKKM
NLGVGAYRDDNGKPYVLPSVRKAEAQIAAKGLDKEYLPIGGLAEFCRASAELALGENSEV
VKSGRFVTVQTISGTGALRIGASFLQRFFKFSRDVFLPKPSWGNHTPIFRDAGMQLQSYR
YYDPKTCGFDFTGALEDISKIPEQSVLLLHACAHNPTGVDPRPEQWKEIATVVKKRNLFA
FFDMAYQGFASGDGDKDAWAVRHFIEQGINVCLCQSYAKNMGLYGERVGAFTVICKDADE
AKRVESQLKILIRPMYSNPPIHGARIASTILTSPDLRKQWLQEVKGMADRIIGMRTQLVS
NLKKEGSTHSWQHITDQIGMFCFTGLKPEQVERLTKEFSIYMTKDGRISVAGVTSGNVGY
LAHAIHQVTK

2. Crea un script que realitzi una còpia de seguretat i comprimeixi tots els fitxers i directoris d'un directori passat per paràmetre per l'usuari (pex. /home/$USER/projecte ) sempre cap a la ruta destí /home/$USER/Documents.

El nom de la còpia ha de contenir el dia actual. Pots usar aquest codi per aconseguir-la:

NOW=$(date +"%d_%m_%Y")

#!/bin/bash

# Comprovar si s'ha proporcionat el nom de la carpeta
if [ $# -eq 0 ]; then
    echo "❌ Has d'introduir el nom de la carpeta on vols posar la còpia de seguretat."
    echo "🔹 Ús: $0 <nom_de_la_carpeta>"
    exit 1
fi

# Carpeta on es vol fer la còpia
BACKUP_DIR="$1"

# Comprovar si la carpeta ja existeix
if [ -d "$BACKUP_DIR" ]; then
    echo "ℹ️ La carpeta '$BACKUP_DIR' ja existeix. No es crearà una nova còpia."
    exit 0
fi

# Generar nom de la còpia amb data
NOW=$(date +"%d_%m_%Y")
BACKUP_NAME="${BACKUP_DIR}_${NOW}"

echo "📂 Creant còpia de seguretat amb el nom: ${BACKUP_NAME}.tar.gz"

# Crear còpia de seguretat
SOURCE_FOLDER="/home/$USER/Documents/"
tar -czvf "${BACKUP_NAME}.tar.gz" "$SOURCE_FOLDER"

# Comprovar si la còpia s'ha creat correctament
if [ $? -eq 0 ]; then
    echo "✅ Còpia de seguretat completada: ${BACKUP_NAME}.tar.gz"
else
    echo "❌ Error en crear la còpia de seguretat!"
    exit 1
fi

💡 Exemple d'ús

./backup.sh copia_segur

Si no passes cap argument:

❌ Has d'introduir el nom de la carpeta on vols posar la còpia de seguretat.
🔹 Ús: ./backup.sh <nom_de_la_carpeta>

Si la carpeta ja existeix:

ℹ️ La carpeta 'copia_segur' ja existeix. No es crearà una nova còpia.

Si la còpia es fa correctament:

📂 Creant còpia de seguretat amb el nom: copia_segur_25_02_2025.tar.gz
✅ Còpia de seguretat completada: copia_segur_25_02_2025.tar.gz

Bucles.

Per a repetir la mateixa instrucció un número determinat de vegades usem els bucles.

Podem usar el bucle for o el while

El bucle for en Bash és una mica diferent als bucles for tradicionals d'altres llenguatges com a C o Java, sinó que s'assembla més al bucle for each d'altres llenguatges com Python, ja que ací no es repeteix un nombre fix de vegades, sinó que es processen les paraules d'una frase una a una.

La seua sintaxi és la següent:

for var [in lista]
do
·····
Sentencies que fan servir $var
·····
done

Un exemple d'aquest bucle l'hem vist al principi de tot; el fitxer hacking.sh:

#!/bin/bash
for i in {1..100}
do
 echo "Hacking. $i% completed."
 sleep 2
done
echo "Hacking completed :)"

Un altre exemple senzill és aquest programa, que simula les tirades d'un dau. El nombre de vegades a tirar el dau se li ha de passar per teclat.

#!/bin/bash
echo "Introdueix el nombre de vegades que vols llençar el dau"
read vegades

for ((i=1;i<=$vegades;i++))
do
    dau=$((1+$RANDOM % 6))
    echo $i. $dau
done

En canvi, un exemple del while pot ser la lectura de fitxers amb la extensió fasta:

#!/bin/bash
for i in *.fasta; do
    echo $i 
    grep "^>" $i
done

3. Crea un shell script que realitzi un sorteig entre els 13 nans de la novel·la The Hobbit per tal de saber qui s’enfrontarà al drac Smaug.

Disposarem del nom del 13 nans en un fitxer anomenat nans i amb el nom de cadascú a una línia diferent

nans.txt

Thorin
Balin
Glóin
Bifur
Bofur
Bombur
Dwalin
Ori
Dori
Nori
Óin
Kíli
Fíli

Quan executem el script la sortida # haurà de ser del tipus:

S’ha d’enfrontar a Smaug: Nori 

Ex2Tolkien.sh

# Numero de nans dintre del fitxer `
num_nans=`cat Nans.txt | wc -l`
echo "Número de nans del sorteig = $num_nans"
# num_nans = 13
# Sorteig... Numero aleatori
aleat=$(($RANDOM%$num_nans+1))
echo "id de la persona petita $aleat" 
nan=`cat Nans.txt | head -$aleat | tail -1`
echo "S'ha d'enfrontar a Smaug: $nan"

🖥️Funcions en Bash

Les funcions en Bash permeten modularitzar el codi i reutilitzar-lo fàcilment. A continuació, es mostren dos exemples:

🔹 Funció sense arguments

Aquesta funció mostra la data i l'hora actual.

#!/bin/bash

# Funció sense arguments
mostrar_data() {
    echo "📅 Avui és: $(date +"%d-%m-%Y %H:%M:%S")"
}

# Crida a la funció
mostrar_data

📝 Explicació

  • La funció mostrar_data no rep cap argument.
  • Fa servir date per mostrar la data i l'hora en format llegible.
  • Es crida simplement amb mostrar_data.

🔹 Funció amb 1 o 2 arguments
Aquesta funció saluda un usuari.

#!/bin/bash

# Funció amb 1 o 2 arguments
saludar() {
    if [ $# -eq 0 ]; then
        echo "❌ Has d'introduir almenys un nom."
        return 1
    fi

    NOM="$1"
    COGNOM="${2:-}"  # Opcional, buit per defecte

    if [ -z "$COGNOM" ]; then
        echo "👋 Hola, $NOM!"
    else
        echo "👋 Hola, $NOM $COGNOM!"
    fi
}

# Crides a la funció
saludar "Anna"
saludar "Pau" "Martí"
saludar  # Sense arguments (error)

📝 Explicació

  • saludar rep un o dos arguments.
  • Si no rep cap argument, mostra un missatge d’error.
  • Si només es passa un nom, saluda amb aquest nom.
  • Si es passen nom i cognom, saluda amb els dos.
  • COGNOM="${2:-}" assegura que no hi hagi errors si només hi ha un argument.

🎯 Exercici 4: Còpia de seguretat amb funció
Descripció: Escriu un script Bash que creï una còpia de seguretat d’una carpeta i li afegeixi la data actual. Si el fitxer ja existeix, ha de mostrar un missatge i no tornar-lo a crear.

#!/bin/bash

# Funció per fer la còpia de seguretat
fer_backup() {
    if [ $# -ne 1 ]; then
        echo "❌ Ús: fer_backup <carpeta_a_guardar>"
        return 1
    fi

    FOLDER="$1"
    
    if [ ! -d "$FOLDER" ]; then
        echo "❌ Error: la carpeta '$FOLDER' no existeix!"
        return 1
    fi

    NOW=$(date +"%d_%m_%Y")
    BACKUP_FILE="${FOLDER}_backup_${NOW}.tar.gz"

    if [ -f "$BACKUP_FILE" ]; then
        echo "ℹ️ Ja existeix un backup: $BACKUP_FILE"
        return 0
    fi

    echo "📂 Creant còpia de seguretat..."
    tar -czvf "$BACKUP_FILE" "$FOLDER"

    if [ $? -eq 0 ]; then
        echo "✅ Backup creat: $BACKUP_FILE"
    else
        echo "❌ Error en crear el backup!"
        return 1
    fi
}

# Exemple d’ús
fer_backup "/home/$USER/Documents"

📝 Explicació

  • La funció fer_backup rep un argument obligatori (el directori a fer la còpia).
  • Comprova que el directori existeixi abans de continuar.
  • Genera un fitxer .tar.gz amb la data actual.
  • Si el backup ja existeix, avisa l'usuari i no el crea de nou.
  • Fa servir tar -czvf per comprimir la carpeta.
  • Gestiona errors per evitar problemes en l'execució.

💡 Prova-ho!
Executa-ho en un terminal:

./script.sh /home/usuari/Documents

Així aprens a modularitzar el teu codi amb funcions! 🚀


Recursos:

Segueix amb els exemples: