Programació del shell Bash
El shell Bash (Bourne-Again shell) és un intèrpret d’ordres, un programa informàtic que té la funció d’interpretar ordres. El shell ens proporciona la possibilitat de programar l’execució d’un conjunt d’ordres amb el seu propi llenguatge i d’emmagatzemar-les en un fitxer, que executarem com qualsevol altra ordre del sistema. Aquest fitxer d’ordres s’anomena guió de shell oshell script. Es poden escriure guions de shell complexos, ja que el shell admet variables, paràmetres, entrada i sortida de dades interactiva, comparacions, bifurcacions, bucles, etc.
L’única manera d’aprendre shell scripting és fent guions de shell. Per això és molt recomanable que a mesura que aneu avançant en la lectura d’aquest apartat copieu els exemples i els proveu al vostre sistema, i que aneu fent les activitats d’aprenentatge proposades al web de la unitat.
Creació i execució d'un guió de shell
Un guió de shell o shell script és un conjunt d’ordres emmagatzemades en un fitxer de text pla per poder ser executades posteriorment amb el nom del fitxer. En el guió podem incloure la crida a qualsevol programa que sigui executable pel shell (ordres del sistema, altres guions de shell, etc.), així com crides a funcions, estructures de control del llenguatge del shell, comentaris, etc.
Cada ordre que escrivim en un guió ha d’anar separada per un salt de línia o bé pel caràcter ; (punt i coma) si està a la mateixa línia.
Les instruccions del guió s’executen seguides una darrere de l’altra en l’ordre en que estan escrites, com si les estiguéssim escrivint una a una a la línia d’ordres, i el salt de línia o el caràcter punt i coma s’interpreten com si preméssim Retorn després de cada ordre.
Creació i nom del fitxer
Per crear un shell script n’hi ha prou amb obrir un nou fitxer buit en un editor de text, escriure la seqüència d’ordres que volem que s’executin i desar el fitxer amb el nom que li volem donar al guió.
El fitxer del guió de shell ha d’estar compost únicament per text sense format i per això hem d’utilitzar un editor de text pla.
Editors de text avançats
El vi (o el vim a Linux) i l’emacs són editors de text molt potents que s’han utilitzat històricament en entorns Unix, però que poden resultar incòmodes per a usuaris novells. En teniu d’altres més fàcils d’utilitzar, com ara el gedit que es troba per defecte en l’entorn d’escriptori GNOME. I n’hi ha molts altres que no hi són per defecte però que us podeu instal·lar a part.
Els editors de texts plans es distingeixen dels processadors de textos en que es fan servir per escriure només text, sense format i sense imatges, és a dir sense diagramació. També podem optar per fer servir un editor de text avançat que reconegui el llenguatge del shell i utilitzi diferents colors per al ressaltat de la sintaxi, com el de la figura. Aquesta mena d’editors són especialment útils quan som principiants, ja que ens ajuden a prevenir errors de sintaxi, com ara oblidar de posar una clau de tancament, unes cometes dobles, etc.
El nom vim ve de vi improved. És compatible amb el vi però hi afegeix algunes funcionalitats.
El nom que donem al fitxer pot ser qualsevol nom vàlid d’acord amb les normes que hi ha per anomenar fitxers en el sistema en què estem treballant. Normalment triarem un nom que sigui representatiu del que fa el guió, per exemple, si fa una còpia de seguretat, podem anomenar-lo copia. En tot cas convé que ens assegurem que el nom no entra en conflicte amb d’altres programes o ordres existents al sistema. Per garantir això, els noms dels guions sovint acaben amb l’extensió .sh.
Podem anomenar els nostres guions amb l’extensió .sh. Ara bé, això només és una convenció i no és obligatori fer-ho.
Noms de fitxers
Les ordres which, whereis i locate ens serveixen per verificar si ja existeixen fitxers amb un nom determinat. Per exemple, si executem which date ens retorna /bin/date, que indica el camí complet on podem trobar aquest fitxer. Si no ens retorna res significa que no localitza cap fitxer amb aquest nom.
Per començar, obriu un editor de text i creeu un nou fitxer que contingui les quatre línies següents:
#!/bin/bash # El meu primer guió de shell echo "Hola, món" echo "Sóc $USER"
Tot i que ara no volem analitzar a fons el significat de cada una de les línies anteriors, en fem una descripció breu per saber què fa el guió:
- La primera línia indica al sistema que el programa Bash ubicat al directori /bin ha d’executar la resta d’instruccions que hi ha a l’script.
- La línia següent és un comentari i, per tant, el shell la ignora.
- La tercera línia és una ordre echo, que permet mostrar text per pantalla, en aquest cas, la frase “Hola, món”.
- La darrera línia és una altra ordre echo que mostra per pantalla el missatge
“Sóc $USER”substituint el valor de la variable USER pel nom de l’usuari que executa el guió.
Una vegada hem escrit les línies sense errors, desem el fitxer i li donem un nom per poder executar-lo des de la línia d’ordres.
Execució del guió de shell
Vegeu com obrir una sessió amb Bash en l’apartat “Obrir una sessió amb Bash” d’aquesta unitat.
La manera més habitual d’executar un script és obrir una sessió de terminal i executar-lo com qualsevol altra ordre del sistema, és a dir, escrivint el nom del fitxer que conté l’script en la línia d’ordres seguit d’un salt de línia. Per poder-ho fer cal que el fitxer tingui permís d’execució per als usuaris que han d’executar-lo. En el cas més simple, donarem permís d’execució al propietari del fitxer amb l’ordre següent:
chmod u+x nom_fitxer
Després d’afegir el permís d’execució al fitxer, podem executar-lo amb el seu nom tenint en compte les consideracions següents:
- Si el directori que conté el fitxer del shell script és a PATH, podem posar només el nom del fitxer que conté el guió perquè s’executi:
# Executar script pel nom
nom_fitxer - Si el directori que conté el fitxer del shell script no és a PATH, hem d’escriure el nom del fitxer indicant on es troba (amb camí relatiu o camí absolut):
# Executar un guió de shell des del directori actual
./nom_fitxer
\# Executar un guió de shell ubicat a /home/usuari
/home/usuari/nom_fitxer
Variable PATH
La variable PATH conté la llista de directoris on el shell s’ha d’adreçar per localitzar les ordres que executem. Això ens permet escriure les ordres escrivint només el seu nom i sense especificar el directori on estan situades, és a dir, sense preocupar-nos d’on es troben al disc. La recerca de l’ordre sol·licitada en els directoris que s’especifiquen en la variable PATH es fa d’esquerra a dreta. Si no troba l’ordre en cap dels directoris especificats en la variable PATH, el shell avisa amb un missatge d’error que no s’ha trobat l’ordre. Podeu visualitzar el valor de la variable PATH amb l’ordre: echo $PATH.
Exemple de creació i execució d'un guió de shell
Obriu una sessió de terminal, assegureu-vos que esteu situats al vostre directori d’inici (~) executant l’ordre següent:
cd
Amb un editor de text pla, creeu un fitxer que contingui les línies següents:
#!/bin/bash # holamon.sh echo "Hola, món" echo "Avui és $(date +%x)" echo "Sóc $USER" echo "Treballo amb el sistema $(uname -sr)" echo "Adéu!"
Deseu el fitxer al mateix directori d’inici i anomeneu-lo holamon.sh. Doneu permís d’execució al fitxer:
chmod u+x holamon.sh
Executeu el shell script, feu la crida indicant el nom d’on es troba amb camí relatiu:
./holamon.sh
Executeu una altra vegada el shell script. Ara feu la crida indicant el nom d’on es troba el fitxer amb camí absolut:
~/holamon.sh
Creeu un directori ~/scripts per guardar els vostres programes, moveu el fitxer holamon.sh al directori ~/scripts i afegiu el directori al contingut de la variable PATH amb les ordres següents:
mkdir ~/scripts mv ~/holamon.sh ~/scripts export PATH="$PATH:~/scripts"
Ara podeu executar holamon.sh cridant-lo només pel seu nom, sense indicar on es troba (ni amb camí relatiu ni amb camí absolut), perquè està en un directori contingut a la variable PATH.
holamon.sh
Podeu provar de situar-vos a qualsevol punt de la jerarquia de directoris del sistema i executar holamon.sh posant només el seu nom. Per exemple:
cd /tmp holamon.sh
El canvi que heu fet a la variable PATH només afecta a la sessió de shell que teniu activa. Si voleu que la variable PATH tingui el directori ~/scripts en totes les sessions que obriu, cal que editeu el fitxer ~/.bashrc, afegiu al final la línia amb l’ordre export i deseu els canvis.
export PATH="$PATH:~/scripts"
Un guió de shell també es pot executar explícitament amb un shell determinat, posant el nom del shell i a continuació el nom del fitxer que conté el guió de shell:
bash holamon.sh
Aquest mètode generalment només el fem servir quan volem comprovar que el programa funciona amb un altre shell o quan volem depurar (debug) el guió de shell. Per exemple:
rbash nom_script # Executar el guió amb rbash sh nom_script # Executar el guió amb sh bash -x nom_script # Executar el guió amb mode debug
Quan el Bash executa un shell script, crea un procés fill que executa un altre Bash, el qual llegeix les línies de l’arxiu (una línia per vegada), les interpreta i executa com si vinguessin del teclat. El procés Bash pare espera mentre el Bash fill executa l’script fins al final, i en aquell moment el control torna al procés pare, el qual torna a posar l’indicador o prompt.
Els canvis en l’entorn d’un shell fill no afecten a l’entorn del shell pare.
Per tant, si en el guió hi ha ordres que modifiquen l’entorn, tals com canviar de directori actual, modificar el valor d’una variable d’entorn, crear una nova variable, etc., aquests canvis només tenen efecte a l’entorn del shell fill i desapareixen una vegada finalitza l’execució del guió.
Vegeu el significat, la definició i utilització de les variables d’entorn i locals en l’apartat “Variables del shell” d’aquesta unitat.
Si volem executar un guió en el shell actual en lloc d’amb un shell fill i, per tant, modificar l’entorn actual, hem d’executar el guió amb l’ordre source.
source nom_script # Executar en el shell actual
L’ordre del Bash anomenada source és un sinònim de l’ordre . (punt) del shell Bourne:
. nom_script
No confongueu l’ordre punt (.) del shell Bourne amb el directori punt, que indica el directori actual.
Exemple d'execució d'un guió de shell amb o sense modificació de l'entorn actual
Creeu un guió de shell en el vostre directori de treball que contingui les línies següents:
#!/bin/bash cd /etc echo El directori actual és: pwd
Deseu el fitxer i anomeneu-lo canvi.sh. Doneu permís d’execució al fitxer:
chmod u+x canvi.sh
Executeu el guió des del directori actual mitjançant un shell fill:
./canvi.sh
La sortida us mostra que en el shell fill es canvia de directori. Ara bé, quan finalitza l’execució del guió, el directori on estem situats continua sent el nostre directori de treball, perquè el canvi en el shell fill no ens afecta. Ho comprovem executant l’ordre:
pwd
Executeu el guió en el shell actual (enlloc d’en un shell fill) mitjançant l’ordre source o l’ordre punt (.):
source canvi.sh
En aquest cas, en finalitzar l’execució ens trobem al directori /etc, ja que les ordres del guió s’han executat en el nostre shell. Ho comprovem executant l’ordre:
pwd
Definició del shell d'execució
En escriure guions de shell és recomanable que indiquem el shell que ha d’executar el guió. Per això els dos primers caràcters de la primera línia han de ser #!, seguits del nom del shell que ha d’interpretar les ordres que venen a continuació.
Per exemple:
#!/bin/bash
No comenceu el shell script amb una línia en blanc, perquè aquestes línies també són tingudes en consideració.
Si ometem la definició del shell d’execució, el shell script s’executa amb el shell que hi ha establert per defecte i, en el cas de no ser el shell per al qual s’ha escrit el guió, les ordres contingudes poden donar error en ser executades.
Comentaris al guió de shell
Els comentaris són útils per ajudar a entendre els scripts als lectors. Cal tenir en compte que probablement no serem els únics que llegirem el codi dels guions de shell que fem, o que, passat un temps, la memòria ens pot fallar i no entendre el programa que nosaltres mateixos hem escrit.
Normalment, les primeres línies després de la línia que indica el shell d’execució són un comentari sobre la funcionalitat del programa. La resta del programa es comenta com calgui per a una major claredat i comprensió del codi.
Scripts d'inici
Vegeu els scripts d’inici del sistema al directori /etc/init.d. Aquests guions estan molt ben comentats perquè siguin fàcilment llegibles pels administradors i els programadors del sistema.
Per afegir comentaris al programa només cal posar el símbol # seguit del text que es vulgui. Cada nova línia ha d’anar precedida del símbol #. Es pot posar el símbol \ al final d’una línia per indicar que el text no ha acabat i continua a la línia següent. Per exemple, les primeres línies del guió de shell /etc/rc.init/kerneloops són:
#!/bin/bash # # kerneloops # # chkconfig: 345 90 88 # description: A tool that collects and submits \ # kernel crash signatures to the kerneloops.org \ # website for use by the Linux kernel developers. # ...
Tabulació del codi
En el cas més simple, un guió no és més que una llista d’ordres del sistema escrites una darrere de l’altra que s’executen de manera seqüencial. Ara bé, en programar shell scripts sovint utilitzem estructures de control de flux (condicionals i iteratives) que ens permeten trencar el flux d’execució, repetir una part de les ordres, etc., i que fan que el guió de shell no sigui simplement una enumeració d’ordres.
Per tal de facilitar la lectura del codi del guió de shell, és molt important que el codi estigui ben tabulat. Al Bash no li cal que hi hagi tabuladors per poder interpretar el codi, però a les persones sí que ens va bé que el codi estigui organitzat amb tabuladors per tal de llegir-lo d’una manera còmoda.
El codi d’un shell script ha d’estar ben tabulat per tal que el programa sigui llegible.
Per exemple, considerem el guió de shell següent, escrit sense tabular:
#!/bin/bash echo -n "Escriu un número: " read X if [ $X -lt 10 ]; then echo "X és més petit que 10" else if [ $X -gt 10 ]; then echo "X és més gran que 10" else echo "X és igual a 10" fi fi
La lectura i la comprensió de les estructures condicionals imbricades del guió de shell anterior són difícils. El mateix codi, tabulat i comentat, queda així:
#!/bin/bash # # esdeu.sh # Demana un número i comprova si és igual a 10. # echo -n "Escriu un número: " read X if [ $X -lt 10 ]; then echo "X és més petit que 10" else if [ $X -gt 10 ]; then echo "X és més gran que 10" else echo "X és igual a 10" fi fi
La tabulació ens permet llegir el codi i seguir la lògica del programa amb més comoditat, així com evitar errades de programació (oblidar una paraula clau de tancament d’una estructura de control, situar una paraula clau en algun lloc indegut, etc.).
No hi ha cap norma que fixi com s’ha de tabular el codi, hi ha qui utilitza tabuladors de dos espais i qui prefereix quatre espais. Tant és, el que importa és utilitzar un estil homogeni al llarg de tot el shell script.
Si usem un editor de text pla, hem de sagnar el codi a mà, o bé amb espais o bé amb tabuladors. Alguns editors avançats ens faciliten aquesta feina fent que a mida que escrivim el codi es vagi sagnant de manera automàtica sense que nosaltres ens haguem de preocupar de les tabulacions.
Depurar un guió de shell
De vegades el funcionament d’un guió de shell no és l’esperat i hem de determinar quina és la causa d’aquest funcionament incorrecte, és a dir, hem de depurar el shell script. Una tècnica consisteix a fer un seguiment pas a pas de les ordres que executa per tal de veure on es produeix l’error. Per fer aquest seguiment podem executar l’script amb bash-x seguit del nom del guió així:
bash -x nom_fitxer
O bé podem incloure -x a la primera línia:
#!/bin/bash -x
Si només volem depurar una part del programa, podem fer-ho de la manera següent:
... # activar depuració des d'aquí set -x codi per depurar # parar la depuració set +x ...
L’opció -x de Bash indica que s’han d’imprimir per pantalla les ordres i els seus arguments mentre s’executen. Així podem veure a quin punt de l’execució s’ha arribat quan es produeix un error.
Exemple d'execució pas a pas d'un guió de shell
Feu un guió de shell que es digui prova.sh amb les línies següents:
#!/bin/bash # Shell script de prova N=5 echo "El valor de N és: $N"
Executeu-lo de manera normal i veieu que la sortida és simplement el missatge següent:
El valor de N és: 5
Ara l’executem mostrant cada pas de l’execució de la manera següent:
bash -x prova.sh
El resultat és que ens apareixen les línies que es van executant amb un símbol + al davant:
+ N=5
+ echo ‘El valor de N és: 5’
El valor de N és: 5
Podem afegir l’opció -v per mostrar totes les línies d’entrada al shell, incloent comentaris, a mesura que les va llegint:
bash -xv prova.sh
El resultat és:
#!/bin/bash
# Shell script de prova
N=5
+ N=5
echo “El valor de N és: $N”
+ echo ‘El valor de N és: 5’
El valor de N és: 5
Interacció amb l'usuari
Un guió de shell pot requerir interacció amb l’usuari, és a dir, sol·licitar-li l’entrada de dades o mostrar-li dades de sortida. Per poder interaccionar amb el programa normalment utilitzem les ordres echo i read, que ens permeten mostrar dades a la pantalla del terminal i llegir dades del teclat en mode text.
Si el que volem és fer una aplicació gràfica, aleshores Bash no és l’eina indicada. Tanmateix, podem tenir una interacció bàsica amb mode gràfic i utilitzar caixes de diàleg senzilles amb algun programari que ho permeti, com ara el Zenity.
Ordres echo i read
L’ordre echo mostra una cadena de text afegint un salt de línia per la sortida estàndard. La sintaxi de l’ordre és:
echo [opció] [cadena]
Executeu man echo per consultar la llista de caràcters d’escapament possibles del vostre sistema.
Les opcions que es poden utilitzar amb echo són:
- -n perquè no afegeixi un salt de línia després de mostrar la cadena.
- -e per habilitar la interpretació dels caràcters d’escapament (\n per afegir un salt de línia, \t per afegir un tabulador, etc.). Si utilitzem aquesta opció cal que posem el text de la cadena entre cometes dobles.
Per exemple:
echo -e “\n\nHola, món!!\n\n”
L’ordre read permet llegir dades de l’entrada estàndard. La utilització més habitual de l’ordre read és amb la sintaxi següent:
read nom_variable
L’ordre read llegeix el que l’usuari introdueix pel teclat fins que hi ha un salt de línia i assigna les dades a la variable nom_variable. Per exemple:
read N
Executeu man read per veure la sintaxi completa d’aquesta ordre.
Exemple de guió de shell que interacciona amb l'usuari amb les ordres echo i read.
Feu un guió de shell que s’anomeni salutacio.sh que demani un nom i a continuació mostri un missatge de salutació amb el nom que hem introduït.
#!/bin/bash # # salutacio.sh # # Exemple d'ús de les ordres echo i read # echo "Com et dius?" read NOM echo "Hola, $NOM"
Interacció en mode gràfic
El shell Bash és un shell de línia d’ordres pensat per interactuar en mode text, però podem tenir una interacció bàsica en mode gràfic utilitzant altres programes, com ara Zenity. Zenity permet utilitzar caixes de diàleg basades en GTK+ a la línia d’ordres i en els shell scripts.
Eines GTK
Les GTK o grup d’eines del GIMP (GIMP toolkit, en anglès) són unes llibreries pensades per al desenvolupament d’aplicacions gràfiques amb facilitat. Disposen de llicència GPL i hi ha nombrosos projectes que les utilitzen, com el GIMP (d’aquí li prové el nom, ja que inicialment es van crear per a aquest projecte), el GNOME, l’Eclipse o el Firefox, entre d’altres.
Podem usar Zenity per crear diàlegs simples que interactuïn gràficament amb l’usuari, ja sigui per obtenir informació de l’usuari o per proporcionar-li informació. Per exemple, podem demanar a l’usuari que seleccioni una data d’un diàleg del calendari o que seleccioni un arxiu d’un diàleg de selecció d’arxiu. O es pot usar un diàleg de progrés per indicar l’estat actual d’una operació o usar un diàleg d’alerta per notificar a l’usuari algun error.
Per exemple, obriu una sessió de terminal a l’escriptori GNOME i executeu la línia d’ordres següent:
Vegeu la documentació oficial de Zenity a la secció del web “Adreces d’interès”.
/usr/bin/zenity --warning --title="ASIX M6" --text="Hora de descansar\!" --width=500
El resultat és una caixa de diàleg com la de la figura.
Paràmetres i variables especials
La utilització de paràmetres és un mètode per passar dades al programa de manera no interactiva. El llenguatge del shell disposa d’una serie de variables especials que permeten treballar amb aquests paràmetres.
Ús de paràmetres
Un paràmetre o argument de la línia d’ordres no és més que un valor que li passem al programa en el moment de la seva crida. Un programa pot tenir qualsevol nombre d’arguments a la línia d’ordres.
La majoria d’ordres d’Unix poden fer accions diferents en funció dels paràmetres que els donem en executar-les. Per exemple, escriviu l’ordre:
ls /etc/profile
ls és el nom de l’ordre que s’ha d’executar. Tot el que ve a continuació a la línia d’ordres es pren com a arguments per a aquesta ordre. Cada un dels arguments està separat per un o més espais. En aquest cas hi ha un únic argument, el nom del fitxer /etc/profile.
Considerem un exemple de crida d’una ordre amb dos arguments:
tail +10 /var/log/messages
tail és el nom de l’ordre, +10 és el primer argument i el nom del fitxer /var/log/messages és el segon argument.
A la taula podeu veure més exemples de crida d’ordres amb arguments.
| Ordre | Nom de l’ordre | Nombre d’arguments | Nom dels arguments |
|---|---|---|---|
| ls | ls | 0 | N/A |
| ls /etc/resolv.conf | ls | 1 | /etc/resolv.conf |
| cp /etc/resolv.conf /tmp/test.txt | cp | 2 | /etc/resolv.conf /tmp/test.txt |
| sort -r -n nom_fitxer | sort | 3 | -r -n nom_fitxer |
| date +”%d-%m-%Y” | date | 1 | +”%d-%m-%Y” |
De la mateixa manera que ho fan la majoria de les ordres, els guions de shell poden acceptar paràmetres quan s’executen. Un paràmetre o argument és un valor que li donem al guió de shell en el moment de la seva crida. Els arguments d’un shell script es poden referenciar dins del programa mitjançant una sèrie de variables especials.
Variables especials
En executar un guió de shell amb paràmetres, hi ha un conjunt de variables especials del shell anomenat paràmetres posicionals que es fixen automàticament perquè coincideixin amb els paràmetres donats al programa. S’anomenen paràmetres posicionals perquè l’assignació de cada variable depèn de la posició d’un paràmetre en la línia d’ordres. Els noms d’aquestes variables es corresponen amb el valor numèric de la seva situació en la línia d’ordres: 0, 1, 2, 3… I així fins a l’últim paràmetre que es passa.
Els paràmetres posicionals es poden utilitzar dins del guió de shell com qualsevol altra variable del shell, és a dir, per saber el seu valor utilitzarem el símbol $. A partir del desè paràmetre, el nombre s’ha de tancar entre claus.
Els paràmetres dins del programa són accessibles utilitzant les variables: $0, $1, $2, $3… ${10}, ${11}, ${12}…
Exemple d'ús de paràmetres en un shell script
Creeu un shell script que es digui args.sh amb el contingut següent:
#!/bin/bash # # args.sh # # Exemple d'ús de paràmetres # echo "Nom del guió de shell: $0" echo "Valor del primer paràmetre del guió de shell: $1" echo "Valor del segon paràmetre del guió de shell: $2" echo "Valor del tercer paràmetre del guió de shell: $3"
Deseu el fitxer. Executeu-lo així:
chmod +x args.sh ./args.sh blau verd vermell
La sortida del programa és la següent:
Nom del guió de shell: ./args.sh
Valor del primer paràmetre del guió de shell: blau
Valor del segon paràmetre del guió de shell: verd
Valor del tercer paràmetre del guió de shell: vermell
Podeu provar diferents execucions del programa que acabeu de fer i comprovar-ne els resultats, per exemple:
./args.sh A B C ./args.sh Alba 32 Barcelona
A banda dels paràmetres posicionals, hi ha unes altres variables especials definides en qualsevol script que podem usar segons les nostres necessitats. La taula mostra el nom i la descripció de les variables especials més utilitzades.
| Variable | Descripció |
|---|---|
$0 | Nom del shell script que s’està executant |
$n | Paràmetre passat al shell script en la posició n |
$* | Cadena que conté tots els paràmetres rebuts, començant per $1 |
$@ | Igual que $*, excepte quan es posa entre cometes |
$# | Nombre de paràmetres |
$$ | PID del procés del shell que s’està executant |
$! | PID de l’últim procés executat |
$? | Codi de sortida de la darrera ordre executada |
Vegeu el significat de la variable especial $? a l’apartat “Codis de sortida” d’aquesta unitat.
Tingueu en compte les observacions següents:
- $* i $@ són el mateix quan no van entre cometes.
- “$*” expandeix els paràmetres en una cadena: “par1 par2…”. És una sola cadena que comprèn tots els paràmetres units per espais en blanc. Per exemple ‘1 2’ 3 esdevé “1 2 3”.
- “$@” expandeix els paràmetres en cadenes diferenciades: “par1” “par2”… És a dir, la llista de cadenes resultant coincideix exactament amb allò que s’ha donat al guió de shell. Per exemple, ‘1 2’ 3 esdevé “1 2” “3”.
El valor de les variables especials es pot guardar en altres variables, per exemple:
NOM=$1
Ara bé, l’assignació de valors a les variables especials no està permesa, per exemple:
# operació no permesa $1=Alba
Exemple de visualització dels paràmetres i d'altres variables especials.
Modifiqueu el guió de shell args.sh i afegiu-li al final les línies que apareixen en negreta:
#!/bin/bash # # args.sh # # Exemple d'ús de paràmetres # echo "Nom del guió de shell: $0" echo "Valor del primer paràmetre del guió de shell: $1" echo "Valor del segon paràmetre del guió de shell: $2" echo "Valor del tercer paràmetre del guió de shell: $3" echo "Nombre de paràmetres passats al guió de shell: $#" echo "Llista de tots els arguments rebuts: $*"
Executeu-lo:
./args.sh a b c
La sortida del programa és la següent:
Nom del guió de shell: ./cmdargs.sh Valor del primer paràmetre del guió de shell: a Valor del segon paràmetre del guió de shell: b Valor del tercer paràmetre del guió de shell: c Nombre de paràmetres passats al guió de shell: 3 Llista de tots els arguments rebuts: a b c
Control del nombre de paràmetres
La majoria d’ordres mostren un missatge d’error o informatiu quan els arguments requerits per l’ordre no s’han especificat en la seva crida. Per exemple, executeu l’ordre:
rm
La sortida que us dóna és:
rm: missing operand Try `rm --help' for more information.
Anàlogament, si un guió de shell espera rebre paràmetres, hem de verificar dins del programa que la crida al guió de shell s’ha fet amb el nombre de paràmetres esperat i, en cas contrari, mostrar un missatge d’error. Aquest control el podem dur a terme mitjançant l’estructura condicional if i la variable especial $#.
Vegeu el funcionament de l’estructura if en l’apartat “Estructures condicionals” d’aquesta unitat.
Exemple d'un shell script amb control de nombre de paràmetres
Creeu un shell script amb el codi següent i anomeneu-lo suma.sh. Aquest script rep dos números per paràmetre i mostra per pantalla el resultat de la suma dels dos números. A l’inici controla que el nombre d’arguments rebuts és correcte i dóna un error si no és així.
#!/bin/bash # # suma.sh # # Rep dos nombres per paràmetre i mostra la suma per # la sortida estàndard. # # Control del nombre de paràmetres: if [ $# -ne 2 ]; then echo "Error: s'esperaven dos paràmetres." echo "Ús del programa: $0 num1 num2" exit 1 fi # Rebuts dos paràmetres, fem la suma (( SUMA = $1 + $2 )) echo "La suma de $1 i $2 és: $SUMA"
Doneu permís d’execució al fitxer i executeu-lo passant-li dos números com a paràmetres:
chmod +x suma.sh ./suma.sh 10 5
La sortida del programa és la següent:
15
Executeu-lo passant-li un nombre erroni de paràmetres (diferent de 2), per exemple:
./suma.sh
La sortida del programa és la següent:
Error: s'esperaven dos paràmetres. Ús del programa: suma.sh num1 num2
Codis de sortida
En finalitzar l’execució totes les ordres generen un codi de sortida (en anglès, exit status o exit code) que és un nombre sencer entre 0 i 255.
Per convenció, si l’ordre acaba bé, normalment retorna un zero (0) i si acaba malament retorna un valor diferent de zero (entre 1 i 255). Moltes vegades, el valor retornat per l’ordre representa l’error generat. Per exemple, els errors de sintaxi gairebé sempre fan que les ordres retornin el valor 1.
El shell ens proporciona una variable especial anomenada ? (signe d’interrogació), que conté el codi de sortida de l’ordre executada anteriorment.
El codi de sortida pot ser utilitzat pel Bash, el podem mostrar o podem controlar el flux del guió amb ell. Per visualitzar el valor utilitzem l’ordre echo:
echo $?
En els guions de shell, la majoria de les decisions de programació es controlen analitzant el valor dels codis de sortida. Quan avaluem condicions, el codi de sortida ens indica si la condició és vertadera (retorna 0) o falsa (retorna un valor diferent de zero).
Exemple d'execució d'ordres i visualització dels codis de sortida
Obriu una sessió de terminal i executeu l’ordre ls:
ls
Visualitzeu el codi de sortida de l’ordre que acabeu d’executar:
echo $? 0
La sortida és un zero, fet que indica que no hi ha hagut cap error. Executeu l’ordre ls amb algun nom de fitxer inexistent per provocar una errada:
ls asdfg ls: no s’ha pogut accedir a asdfg: El fitxer o directori no existeix
Visualitzeu el codi de sortida de l’ordre que acabeu d’executar:
echo $? 2
La sortida és un valor diferent de zero (un 2), fet que indica que hi ha hagut un error.
Executeu l’ordre cp sense arguments per provocar una errada:
cp cp: manca un operand fitxer Proveu «cp --help» per a obtenir més informació.
Visualitzeu el codi de sortida de l’ordre que acabeu d’executar:
echo $? 1
La sortida és un 1, fet que indica error de sintaxi.
De la mateixa manera que ho fan les ordres, els shell scripts retornen un codi de sortida que és el de la darrera ordre executada. El codi de sortida d’un shell script també es pot consultar just després de la finalització del programa mitjançant la variable $?.
Exemple de visualització del codi de sortida d'un guió de shell
Creeu un guió de shell senzill anomenat hola.sh:
#!/bin/bash echo "Hola, món"
Doneu permís d’execució al fitxer i executeu el guió:
chmod u+x hola.sh ./hola.sh
Consulteu el codi de sortida des de la línia d’ordres:
echo $? 0
El codi retornat és un zero, el de la darrera ordre executada en el guió de shell, és a dir, l’ordre echo “Hola, món”.
Podeu modificar el guió i afegir al final una ordre que doni error per comprovar l’efecte que té sobre el codi de sortida.
Ordre exit
L’ordre exit provoca la finalització del shell script i de manera opcional permet fixar el codi de sortida amb un valor determinat. La seva sintaxi és:
exit [ n ]
essent n un nombre enter entre 0 i 255.
Per convenció el valor que es retorna és un zero si ha anat bé o un enter del rang entre 1 i 255 si hi ha hagut algun error. Normalment, el rang de 126 a 255 es reserva per ser utilitzat directament pel shell o per a finalitats especials, i els codis del rang de 0 a 125 es deixen per ser utilitzats pel programa.
En tot guió de shell convé proporcionar un codi de sortida significatiu, com a mínim un 0 (zero) si finalitza bé i un 1 (en general, un valor diferent de zero) si hi ha algun error. Això permetrà que el procés que ha fet la crida pugui verificar com ha anat l’execució del guió de shell.
Si no es passa cap paràmetre a exit, el codi retornat pel guió de shell és el de la darrera ordre executada just abans de l’exit. Per exemple:
#!/bin/bash ordre_1 .. . ordre_N # Acaba amb el codi de sortida de l'ordre_N. exit
L’exemple anterior, hauria estat exactament igual si s’hagués especificat el valor del codi de retorn de la manera següent:
#!/bin/bash ordre_1 .. . ordre_N # Acaba amb el codi de sortida de l'ordre_N. exit $?
Anàlogament, si un shell script finalitza sense especificar l’ordre exit, el codi de retorn és el de la darrera ordre executada al shell script.
#!/bin/bash ordre_1 .. . ordre_N # Acaba amb el codi de sortida de l'ordre_N.
Exemple d'utilització de l'ordre exit i comprovació dels codis de sortida.
Feu un guió de shell que s’anomeni sortida.sh amb les línies següents:
#!/bin/bash echo "Això és una prova." # Retornem codi de sortida 0 exit 115
Deseu el fitxer i executeu-lo:
chmod +x sortida.sh ./sortida.sh
La sortida del programa és la següent:
Això és una prova.
Visualitzeu el codi de sortida del guió de shell:
echo $?
El valor de sortida és un 115, atès que l’hem forçat amb l’ordre exit.
115
En la programació de guions de shell és molt freqüent utilitzar l’ordre exit per finalitzar el programa en qualsevol punt sense seguir la norma de la programació estructurada que diu que un programa ha de tenir un únic punt de sortida. Per exemple, un shell script fet amb programació estructurada seria així:
if condicio_error1; then
codi=1
else
if condicio_error2; then
codi=2
else
if condicio_error3; then
codi=3
else
# No hi ha errors
instruccions
codi=0
fi
fi
fi
exit $codi
Però habitualment ens trobarem el guió de shell anterior escrit amb un estil similar al següent:
if condicio_error1; then exit 1 fi if condicio_error2; then exit 2 fi if condicio_error3; then exit 3 fi # No hi ha errors instruccions exit 0
Tot i que els dos programes fan el mateix, el primer segueix les normes de la programació estructurada i té un únic punt de sortida en el darrer exit, mentre que el segon té quatre possibles punts de sortida, un per a cada ordre d’exit.
Avaluació aritmètica i lògica
En programar gairebé sempre apareix la necessitat d’operar amb nombres així com d’avaluar el resultat d’expressions aritmètiques o lògiques per prendre decisions.
A Bash disposem del mecanisme d’expansió aritmètica per operar i avaluar expressions amb nombres enters però també tenim altres mètodes, dels quals en veurem dos: l’ordre let i la construcció doble parèntesi. Per aquells casos que necessitem operar amb nombres amb decimals veurem com fer-ho utilitzant l’ordre bc.
Vegeu el mecanisme d’expansió aritmètica descrit a l’apartat “Expansió aritmètica” d’aquesta unitat.
Una altra ordre molt utilitzada en la programació de guions de shell per fer avaluacions lògiques és l’ordre test. Aquesta ordre ens permet avaluar expressions amb tres tipus d’elements: nombres enters, fitxers i cadenes de caràcters.
Ordre let
L’ordre let permet als programes de shell avaluar expressions aritmètiques amb els mateixos operadors que en el mecanisme d’expansió aritmètica i amb la sintaxi següent:
let expressió_aritmètica
L’ordre avalua d’esquerra a dreta l’expressió i torna un codi de sortida igual a 0 (vertader) o 1 (fals). Malgrat que no sempre cal tancar l’expressió entre cometes dobles, podem optar per posar-les per defecte per evitar errades.
L’expressió aritmètica pot constar de nombres enters, variables i operadors de shell. També podem utilitzar parèntesis per canviar l’ordre de preferència d’una expressió. Per exemple:
#!/bin/bash # Definició de variables x=12 y=2 # Utilització de l'ordre let let "z = x / y + 1" echo $z# z val 7, primer s'ha fet la divisió # Alterar precedència amb parèntesis let "z = x / (y + 1)" echo $z# z val 4, primer s'ha fet la suma
Quan en l’expressió utilitzem operadors lògics o relacionals (!, ⇐, >=, <, >, ==, !=) avaluem el codi de sortida tornat pel shell. Per exemple:
let "11 < 10" echo $?
L’ordre anterior retorna un 1 indicant que l’expressió s’ha avaluat com a falsa, ja que 11 no és més petit que 10.
Algunes consideracions a tenir en compte quan utilitzem l’ordre let són:
- L’ordre let no mostra cap resultat per pantalla. Un error comú és esperar que let retorni el resultat d’una operació per la sortida estàndard, per exemple, executar
let 4+2i esperar un 6 per pantalla. Per operar amb let cal utilitzar variables i assignar el resultat a les variables, per exemple,let x=4+2. - L’expressió de let s’escriu sense espais. Si voleu posar espais per separar els operands dels operadors perquè quedi més clar, heu de posar l’expressió entre cometes dobles. Per exemple, let
“x = x + 2”. - Si en una expressió voleu utilitzar parèntesis per alterar l’ordre de precedència dels operadors, heu de posar doble cometa per anul·lar el significat especial dels parèntesis. Per exemple,
let “x=x/(y+1)”. - Les variables que s’utilitzen dins d’una expressió poden anar referenciades o no. Per exemple, let x=x+2 és el mateix que
let x=$x+2.
La construcció doble parèntesi
La construcció doble parèntesi, (( )), permet avaluar expressions aritmètiques de manera equivalent a l’ordre let però amb la sintaxi següent:
(( expressió_aritmètica ))
Per exemple:
(( x = x + (y / 2) ))
És equivalent a:
let "x = x + (y / 2)"
Alguns autors recomanen utilitzar preferentment els dobles parèntesis enlloc de l’ordre let.
Sovint trobem la construcció doble parèntesi formant part de bucles while amb l’estil del llenguatge de programació C. Per exemple:
x=1 while (( x < 10 )); do ... ordres ... (( x++ )) done
Vegeu l’explicació dels bucles while utilitzant la construcció (( )) a l’apartat “Estructures repetitives” d’aquesta unitat.
Igual que amb l’ordre let, les variables utilitzades com a operands en una expressió poden anar precedides del símbol $ o no. Per exemple, les dues expressions següents són correctes:
(( x = x + 1 )) # Correcte (( x = $x + 1)) # Correcte
Però aneu amb compte i no cometeu errades com aquesta:
(( $x = x + 1 )) # Incorrecte!!
Operacions amb nombres amb decimals
L’ordre bc és un programa molt potent que incorpora un llenguatge de programació propi i que permet fer càlculs amb precisió. Aquesta ordre ens pot ser de molta utilitat si necessitem fer operacions amb nombres amb decimals.
Executeu man bc per veure’n el funcionament i la descripció de totes les opcions.
Si executem bc a la línia d’ordres, ens apareix una informació sobre la versió del programa similar a la següent:
bc 1.06.95 Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc. This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty'.
El programa queda esperant l’entrada de dades de manera interactiva des del teclat, de manera que podem introduir les operacions que volem fer, prémer Retorn i a continuació obtenim el resultat, per exemple:
6*3/2 9
Per sortir de la calculadora hem d’escriure la paraula quit seguida de Retorn.
Podem utilitzar bc dins d’un shell script mitjançant una canonada per redirigir les dades d’entrada al programa. Per exemple:
echo "(2 + 3) * 5" | bc
En l’exemple anterior utilitzem l’ordre echo per passar les dades d’entrada a l’ordre bc. El resultat de l’operació ens surt per pantalla, en aquest cas un 25. Per treballar amb nombres amb decimals especifiquem la precisió (quantitat de decimals) amb l’opció scale. Per exemple:
echo "scale=2; 7*5/3" | bc
Nombre π
En matemàtiques,
o pi és la constant d’Arquimedes, una constant que relaciona el diàmetre de la circumferència amb la longitud del seu perímetre. És un nombre irracional, és a dir, la seva part fraccionària té un nombre de xifres infinit i a més no té cap període. Per fer càlculs pràctics s’agafa un valor simplificat de pi, com per exemple 3,14159265.
L’ordre anterior ens mostra un 11,66 per pantalla, que és el resultat de l’operació que hem realitzat.
Veiem un darrer exemple en què mostrem el nombre pi (π) per pantalla utilitzant l’opció -l en la crida de bc per poder usar funcions matemàtiques, en aquest cas la funció a(x), que ens retorna l’arc tangent d’x en radiants:
echo "scale=9; 4*a(1)" | bc -l
Ordre test
L’ordre test avalua expressions lògiques i genera un codi de sortida que indica si l’expressió és certa (codi de sortida igual a zero) o falsa (codi de sortida diferent de zero). Aquesta ordre no escriu res a la sortida estàndard. Per determinar el resultat de l’ordre test cal avaluar el valor del codi de sortida amb la variable ?.
Vegeu el significat de la variable especial ? a l’apartat “Codis de sortida” d’aquesta unitat.
La sintaxi de l’ordre test és la següent:
test expressió_test
O en forma abreujada:
[ expressió_test ]
Cal deixar un espai després del símbol [ i abans del símbol ].
Quan utilitzeu la forma abreujada de test (l’expressió entre els claudàtors) cal que aneu amb molt de compte i no oblideu que hi ha espais entre l’expressió i els claudàtors. Si no poseu els espais es produirà un error de sintaxi.
A la taula podeu veure les expressions que es poden avaluar amb l’odre test.
| Avaluació d’expressions amb nombres entersable | |
| [ num1 -eq num2 ] | cert si num1 i num2 són iguals |
| [ num1 -ne num2 ] | cert si num1 i num2 són diferents |
| [ num1 -gt num2 ] | cert si num1 és més gran que num2 |
| [ num1 -ge num2 ] | cert si num1 és més gran o igual que num2 |
| [ num1 -lt num2 ] | cert si num1 és més petit que num2 |
| [ num1 -le num2 ] | cert si num1 és més petit o igual que num2 |
| Avaluació d’expressions amb cadenes de caràcters | |
| [ cad ] | cert si és una cadena no buida |
| [ -n cad ] | cert si és una cadena no buida |
| [ -z cad ] | cert si és una cadena buida |
| [ cad1 = cad2 ] | cert si cadena1 i cadena2 són iguals |
| [ cad1 != cad2 ] | cert si cadena1 i cadena2 són diferents |
| Avaluació d’expressions amb fitxers | |
| [ -e fit ] | cert si el fitxer existeix |
| [ -d fit ] | cert si el fitxer existeix i és un directori |
| [ -f fit ] | cert si el fitxer existeix i és regular |
| [ -L fit ] | cert si el fitxer existeix i és un enllaç simbòlic |
| [ -r fit ] | cert si el fitxer existeix i té permís de lectura |
| [ -w fit ] | cert si el fitxer existeix i té permís d’escriptura |
| [ -x fit ] | cert si el fitxer existeix i té permís d’execució |
| [ fit1 -nt fit2 ] | cert si fitxer1 és més nou que fitxer2 |
| [ fit1 -ot fit2 ] | cert si fitxer1 és més antic que fitxer2 |
| Operadors lògics | |
| [ exp1 -a exp2 ] | AND. Cert si l’expressió1 i l’expressió2 són certes. |
| [ exp1 -o exp2 ] | OR. Cert si l’expressió1 o l’expressió2 són certes. |
| [ ! exp ] | Negació. Cert si l’expressió és falsa. |
Exemple d'avaluació de diferents tipus d'expressions amb test
Amb l’ordre test, comproveu si el nombre 11 és més gran que el nombre 15.
test 11 -gt 15
Avalueu el codi de sortida de l’ordre que acabeu d’executar per saber si l’expressió anterior és vertadera (0) o falsa (diferent de 0):
echo $? 1
La sortida és un 1, fet que indica que l’expressió “11 és més gran que 15” és falsa.
Amb la forma abreviada de l’ordre test, comproveu si el número 15 és igual que el número 15.
[ 15 -eq 15 ]
Avalueu el codi de sortida de l’ordre que acabeu d’executar per saber si l’expressió anterior és vertadera (0) o falsa (diferent de 0):
echo $? 0
La sortida és un 0, fet que indica que l’expressió “15 és igual que 15” és certa.
Amb la forma abreviada de test, comproveu si la cadena “hola” és igual a la cadena “HOLA”:
[ "hola" = "HOLA" ]
Avalueu el codi de sortida per saber si l’expressió anterior és vertadera (cadenes iguals) o falsa (cadenes diferents):
echo $? 1
La sortida és un 1, fet que indica que l’expressió és falsa, ja que “hola” en minúscules no és igual a “HOLA” en majúscules.
Amb l’ordre test, comproveu si existeix un fitxer anomenat /etc/passwd:
test -f /etc/passwd
Avalueu el codi de sortida de l’ordre que acabeu d’executar per saber si l’expressió anterior és vertadera (el fitxer existeix) o falsa (el fitxer no existeix):
echo $? 0
La sortida és un zero, fet que indica que l’expressió és vertadera (el fitxer existeix).
Assigneu a una variable N el valor 15, a continuació amb l’ordre test comproveu si el valor d’N està entre 10 i 20:
N=15 [ $N -gt 10 -a $N -lt 20 ] echo $? 0
El codi de sortida és un zero, fet que indica que l’expressió és vertadera.
Escriviu amb la forma abreviada de test una comprovació que doni cert si el directori /tmp no existeix:
[ ! -d /tmp ] echo $? 1
El codi de sortida és un 1, fet que indica que l’expressió és falsa, atès que el directori /tmp sí que existeix.
Quan avaluem el valor d’una variable en una expressió, hem d’assegurar-nos que la variable sempre conté algun valor, perquè si no, la variable valdrà null i l’script ens donarà un error. Per exemple:
[ $X -eq 3 ] bash: [: -eq: s'esperava un operador unari
La variable X no té cap valor, per tant el shell ha interpretat [ -eq 3 ] i ha donat un error.
Si treballem amb cadenes de caràcters, podem evitar aquesta errada posant sempre les variables entre cometes dobles (“”). Així ens assegurem que la variable conté almenys el valor null i el shell interpretarà la cadena com a buida. Per exemple:
[ "$X" = 3 ]
En aquest cas, si X no té cap valor, el shell interpreta [ ”” = 3 ] i no dóna error.
El shell interpreta els valors d’una expressió de test com a enters o com a cadenes de caràcters segons els operadors que utilitzem.
Les expressions que s’utilitzen amb l’ordre test poden ser connectades lògicament utilitzant els operadors propis de test -a i -o, però la manera més recomanable de fer-ho és utilitzar les ordres de test de manera independent i combinar-les amb els operadors lògics && (AND lògic) i || (OR lògic). Per exemple, en lloc d’escriure
[ $N -gt 10 -a $N -lt 20 ]
és millor separar les expressions i connectar-les així:
[ $N -gt 10 ] && [ $N -lt 20 ]
Però aneu amb compte; els operadors lògics no poden anar dins dels claudàtors de test:
# Operació errònia [ $N -gt 10 && $N -lt 20 ]
Estructures de control
Les estructures de control (if, case, while, for, etc.) permeten canviar el flux seqüencial d’un programa en funció de l’avaluació d’unes condicions i bifurcar-lo cap a un cantó o cap a un altre o repetir l’execució d’unes instruccions.
Sovint utilitzem l’ordre test abreujada amb claudàtors o la construcció doble parèntesi per avaluar expressions i prendre decisions. Però ni els claudàtors de l’ordre test ni els parèntesis de la construcció doble parèntesi no formen part de la sintaxi de cap estructura de control del shell. No ho confongueu amb altres llenguatges de programació en què l’especificació de condicions en les estructures de control s’ha de fer entre parèntesis perquè la sintaxi ho requereix.
Estructures alternatives
Les estructures alternatives són aquelles que ens permeten executar una part del programa en funció de si es compleix o no una condició.
Estructura if
L’estructura if proporciona un control de flux basat en el codi de retorn d’una ordre. La sintaxi és:
if condició; then ordre1 ordre2 ... ordreN fi
El shell executa l’ordre que estableix la condició potserior a if i avalua el codi de retorn resultant:
- Si el valor del codi de retorn és igual a zero, aleshores s’executen les ordres que hi hagi entre les paraules clau then i fi.
- Si el valor del codi de retorn és diferent de zero, no s’executen les ordres que hi hagi entre les paraules clau then i fi i el programa segueix executant el que hi hagi darrera de la paraula clau fi.
Habitualment utilitzem les ordres test i let (o el doble parèntesi equivalent) per especificar les condicions. Per exemple, el codi següent comprova si existeix el fitxer /etc/profile:
if test -f /etc/passwd; then echo "El fitxer /etc/passwd existeix." fi
O bé amb la forma abreviada de test:
if [ -f /etc/passwd ]; then echo "El fitxer /etc/passwd existeix." fi
Però la sintaxi d’if accepta qualsevol ordre, perquè totes les ordres generen un codi de retorn. Per exemple:
if grep ^root /etc/passwd > /dev/null; then echo "L'usuari root existeix." fi
Noteu que l’estructura if implica una execució de l’ordre que estableix la condició i una avaluació del codi de retorn de manera implícita. De manera equivalent, es pot executar l’ordre que estableix la condició abans de l’if i avaluar el codi de retorn de manera explícita, per exemple:
test -f /etc/passwd if [ $? -eq 0 ]; then echo "El fitxer /etc/passwd existeix." fi
I també:
grep root /etc/passwd > /dev/null if [ $? -eq 0 ]; then echo "L'usuari root existeix." fi
Ara bé, la manera més habitual d’utilitzar l’estructura if és amb l’avaluació del codi de sortida de manera implícita.
Exemple d'utilització de l'estructura de control alternativa if
Feu un guió de shell que s’anomeni esfitx.sh que indiqui si un nom donat com a paràmetre és un fitxer regular.
#!/bin/bash # # esfitx.sh # # Rep un paràmetre i comprova si és un fitxer. # if [ -f $1 ]; then echo "$1 és un fitxer." fi exit 0
L’script anterior es pot millorar fent una comprovació del nombre de paràmetres rebuts.
#!/bin/bash # # esfitx.sh # # Rep un paràmetre i comprova si és un fitxer. # # # Control del nombre de paràmetres if [ $# -ne 1 ]; then echo "Nombre d'arguments erroni." echo "Ús del programa: $0 nom" exit 1 fi # # Comprovar si el paràmetre és un fitxer if [ -f $1 ]; then echo "$1 és un fitxer." fi exit 0
Estructura if-else
L’estructura if-else permet prendre un curs d’acció si el codi de retorn de l’ordre que controla la condició és zero (veritat) i un altre curs d’acció si el codi de retorn és diferent de zero (fals). La sintaxi és la següent:
if condició; then ordres1 else ordres2 fi
Per veure el funcionament fem un guió de shell senzill que demani l’entrada d’un número per teclat i que a continuació ens digui si el número llegit és igual a 10 o no:
#!/bin/bash # # esdeu.sh # # Demana un nombre i comprova si és igual a 10. # echo "Introdueix un número: " read X if [ "$X" = 10 ]; then echo "El número és igual a 10." else echo "El número no és igual a 10." fi
En l’exemple que acabem de veure hem utilitzat l’ordre test per especificar la condició de l’estructura condicional, però també es podria haver fet amb l’ordre let o amb la construcció doble parèntesi de la manera següent:
echo "Introdueix un número: " read X if (( "$X" == 10 )); then echo "El número és igual a 10." else echo "El número no és igual a 10." fi
Exemple d'utilització de l'estructura de control alternativa if-else.
Feu un guió de shell que s’anomeni esdir.sh que rebi un argument de manera que si és un directori mostri el missatge “El contingut del directori <nom_directori> és:” i llisti el seu contingut. Si l’argument no és cap directori ha de donar un missatge informatiu. Feu el control del nombre d’arguments.
#!/bin/bash # # esdir.sh # # Rep el nom d'un directori per paràmetre i en mostra # el contingut. # # Control del nombre de paràmetres. if [ $# -ne 1 ]; then echo "Nombre d'arguments erroni." echo "Ús del programa: $0 nom_directori" exit 1 fi # # Comprovar si el paràmetre és un directori. if [ -d $1 ]; then echo "El contingut del directori $1 és:" ls $1 else echo "$1 no és un directori." fi exit 0
Estructura if-elif-else
L’estructura if-elif-else es pot utilitzar per construir una bifurcació amb múltiples direccions. La sintaxi és:
if condició1; then ordres1 elif condició2; then ordres2 elif condició3; then ordres3 ... else ordresN fi
Noteu que la manera recomanada de tabular l’estructura if-elif-else és diferent de l’habitual.
Amb aquesta estructura, coneguda com a escala if-else-if, el shell avalua les expressions condicionals començant per la primera i continuant per la següent de forma descendent fins a trobar una condició veritable (codi de retorn igual a zero), moment en què s’executa la llista d’ordres associada a aquesta condició i se salta la resta de l’escala. Si cap condició és veritable s’executaran les ordres associades a l’else final. Si totes les condicions són falses i no hi ha else final, no fa res.
En realitat, l’estructura if-then-elif no és més que una manera abreviada d’escriure el mateix codi amb estructures if-else imbricades, és a dir, és equivalent al següent:
if condició1; then
ordres1
else
if condició2; then
ordres2
else
if condició3; then
ordres3
else
if
...
else
ordresN
fi
fi
fi
fi
Fixeu-vos en la forma d’escala que adopta el codi quan s’implementa amb estructures if-else i es tabula de la manera habitual. Per això es coneix amb el nom d’escala if-else.
Per exemple, el programa següent llegeix un nombre del teclat i mostra per pantalla si és més petit, més gran o igual que 10, utilitzant una estructura if-elif-else:
echo "Introdueix un número: " read X if [ $X -lt 10 ]; then echo "El número és més petit que 10." elif [ $X -gt 10 ]; then echo "El número és més gran que 10." else echo "El número és igual a 10." fi
Es podria haver escrit el mateix codi utilitzant estructures if i if-else de la manera següent:
echo "Introdueix un número: "
read X
if [ $X -lt 10 ]; then
echo "El número és més petit que 10."
else
if [ $X -gt 10 ]
then
echo "El número és més gran que 10."
else
echo "El número és igual a 10."
fi
fi
Si el nombre de condicions és elevat, l’estructura if-elif-else permet compactar el codi.
Exemple d'utilització d'una escala if-else
Feu un shell script anomenat nota.sh que demani el valor d’una nota (un nombre enter) i ens digui si la nota és una D (0, 1, 2), una C- (3, 4), una C+ (5, 6), una B (7, 8) o una A (9, 10).
#!/bin/bash # # nota.sh # # Demana el valor d'una nota (enter) i diu si és # D (0, 1, 2), C- (3 o 4), C+ (5 o 6), # B (7 o 8) o A (9 o 10). # echo "Quina nota tens (un enter de 1 a 10)?" read NOTA if [ $NOTA -lt 0 ] || [ $NOTA -gt 10 ]; then echo "Nota fora de rang." elif [ $NOTA -lt 3 ]; then echo "Tens una D." elif [ $NOTA -lt 5 ]; then echo "Tens una C-." elif [ $NOTA -lt 7 ]; then echo "Tens una C+." elif [ $NOTA -lt 9 ]; then echo "Tens una B." else echo "Tens una A." fi
Estructura case
L’estructura case és útil quan tenim múltiples bifurcacions basades en avaluacions de cadenes de caràcters. La seva sintaxi és:
case "$NOM_VAR" in patró1) ordre1 ... ordreN ;; patró2) ordre1 ... ordreN ;; ... patróN) ordre1 ... ordreN ;; esac
Es fa una comparació seqüencial de la cadena que hi ha darrera de la paraula clau case amb els patrons. Quan es troba la primera coincidència s’executa la corresponent llista d’ordres i cap altra. Si no es troba cap coincidència no s’executa res.
Veiem-ne un exemple:
echo "Tria una opció de 1 a 3: " read OPCIO case "$OPCIO" in 1)echo "Has triat l'opció 1." ;; 2)echo "Has triat l'opció 2." ;; 3)echo "Has triat l'opció 3." ;; *)echo "Opció incorrecta." esac
Noteu l’ús del patró *, que es pot utilitzar per indicar patrons per defecte.
Els patrons són cadenes de caràcters i podem utilitzar els mateixos caràcters amb significat especial que utilitzem en la generació de noms de fitxers:
- *, per indicar coincidència amb qualsevol cadena de caràcters, inclòs el caràcter null.
- ?, per indicar coincidència amb qualsevol caràcter simple.
- [ ], per indicar coincidència amb qualsevol dels caràcters que hi hagi entre els claudàtors. Els caràcters de la llista van seguits un darrere de l’altre , no van separats per comes ni per espais ni per cap altre caràcter delimitador. S’accepten rangs amb el símbol menys (-).
També es pot utilitzar el símbol | si es vol coincidència amb més d’un patró (s’interpreta com un o lògic):
case "$NOM_VAR" in patró1|patró2|patró3) ordre1 ... ;; patró4|patró5) ... esac
L’exemple següent mostra l’ús de patrons amb caràcters amb significat especial. Noteu que en alguns casos pot haver més d’una manera d’expressar un mateix patró, per exemple, el patró [0-2] també es podria haver escrit [012] o bé 0|1|2.
Exemple d'utilització d'una estructura case i ús de patrons
Feu el mateix guió de shell notes.sh que heu implementat amb una escala if-else, però ara utilitzant una estructura case.
#!/bin/bash # # nota2.sh # # Demana el valor d'una nota (enter) i diu si és # D (0, 1, 2), C- (3 o 4), C+ (5 o 6), # B (7 o 8) o A (9 o 10). # echo "Quina nota tens (enter de 1 a 10)?" read NOTA case "$NOTA" in [0-2]) echo "Tens una D." ;; [34]) echo "Tens una C-." ;; [56]) echo "Tens una C+." ;; [78]) echo "Tens una B." ;; 9|10) echo "Tens una A." ;; *) echo "Nota fora de rang." esac
Operadors && i ||
Els operadors de control && (AND) i || (OR) permeten fer un control de flux bàsic.
Operador &&
L’operador && s’utilitza per executar una ordre, i, si té èxit (codi de sortida igual a zero), executar la propera ordre de la llista. La sintaxi és:
ordre1 && ordre2
L’ordre2 s’executa si i només si l’ordre1 torna un estat de sortida de zero (vertader). En altres paraules, s’executa ordre1 si el codi de sortida és igual a zero, llavors s’executa ordre2. És a dir, és equivalent a la utilització de l’estructura if així:
if ordre1; then ordre2 fi
Per exemple:
rm /tmp/fitxer && echo "Fitxer esborrat."
L’ordre echo només s’executa si l’ordre rm s’executa amb èxit (amb un codi de sortida de zero). Si el fitxer s’elimina correctament, l’ordre rm dóna un codi de sortida igual a zero i a continuació es mostra el missatge “Fitxer esborrat”. Per evitar els possibles missatges d’error de l’ordre rm, podem redirigir la sortida d’errors a /dev/null així:
rm /tmp/fitxer 2>/dev/null && echo "Fitxer esborrat."
Exemple d'utilització de l'operador &&
Escriviu les ordres necessàries per comprovar si hi ha un directori anomenat /tmp/foo i donar un missatge d’error si no existeix.
test ! -d /tmp/foo && echo "El directori no existeix."
O amb la forma abreviada de test:
[ ! -d /tmp/foo ] && echo "El directori no existeix."
Operador ||
L’operador || s’utilitza per executar una ordre, i, si no té èxit (codi de sortida diferent de zero), executar la propera ordre de la llista. La sintaxi és:
ordre1 || ordre2
L’ordre2 s’executa si i només si l’ordre1 retorna un estat de sortida diferent de zero. En altres paraules, s’executa ordre1, si el codi de sortida de ordre1 és diferent de zero, llavors s’executa ordre2. Per exemple:
cat /etc/shadow 2>/dev/null || echo "No s'ha pogut obrir el fitxer."
L’ordre cat intentarà llegir el fitxer /etc/shadow i si falla mostrarà el missatge “No s’ha pogut obrir el fitxer”.
Exemple d'utilització de l'operador OR
Escriviu les ordres necessàries per cercar el nom d’un usuari anomenat “messi” al fitxer /etc/passwd i donar un missatge si no es troba.
grep "^messi" /etc/passwd || echo "No s'ha trobat messi a /etc/passwd."
Combinació dels operadors && i ||
La llista d’ordres unides per operadors && i || pot ampliar-se. Sovint s’utilitza la combinació dels operadors de la manera següent:
ordre_condició && ordre_cert || ordre_fals
És a dir, s’executa l’ordre_condició. Si el codi de sortida és:
- cert (0), llavors s’executa ordre_cert.
- fals (diferent de 0), llavors s’executa ordre_fals.
És a dir, és equivalent a la utilització de l’estructura if-else així:
if ordre_condicio; then ordre_cert else ordre_fals fi
Exemple d'utilització dels operadors OR i AND combinats
Escriviu les ordres necessàries per cercar el nom d’un usuari anomenat “messi” al fitxer /etc/passwd i donar missatges diferents segons si es troba o no.
grep "^messi" /etc/passwd || echo "No s'ha trobat messi a /etc/passwd." && echo "Trobat messi a /etc/passwd."
Escriviu les ordres necessàries per assegurar-vos que el superusuari root és qui està executant el guió de shell i donar un missatge depenent de si ho és o no.
test $(id -u) -eq 0 && echo "S'executa l'script amb el superusuari root." || echo "Només l'usuari root pot executar aquest script."
Escriviu les ordres necessàries per comprovar si el fitxer /etc/resolv.conf existeix i donar un missatge si hi és i un altre missatge si no hi és.
[ -f /etc/resolv.conf ] && echo "El fitxer /etc/resolv.conf existeix." || echo "El fitxer /etc/resolv.conf no existeix."
Estructures iteratives
Les estructures iteratives són aquelles que ens permeten executar diversos cops una part de codi.
Estructura while
L’estructura while permet l’execució repetitiva d’unes sentències sempre que l’ordre de control del bucle while sigui certa (codi de sortida igual a zero). La sintaxi és:
while condició; do ordre1 ordre2 ... ordreN done
Normalment, s’utilitzen les ordres test o let per controlar la condició del bucle, però podem utilitzar qualsevol ordre que retorni un valor.
Veiem-ne un exemple senzill, un bucle que mostra per pantalla els números de l’1 al 10:
X=1 while (( X <= 10 )); do echo X val $X (( X=X+1 )) done
Exemple d'utilització de l'estructura de control while
Feu un guió de shell que s’anomeni endevina.sh que assigni un número aleatori entre 1 i 6 a una variable N i que a continuació ens demani que l’encertem. El programa finalitza quan encertem el número i ens diu quants intents hem necessitat per endevinar-lo.
#!/bin/bash # endevina.sh # Joc per endevinar un número entre 1 i 6 # INTENTS=1 # Calculem un número aleatori entre 1 i 6 # Mòdul 6 dóna un número entre 0 i 5 (( N = $RANDOM % 6 + 1 )) echo -n "Endevina un número entre 1 i 6: " read TRIA while (( "$TRIA" != $N )); do echo echo -n "El número no és $TRIA, torna-ho a intentar: " read TRIA (( INTENTS++ )) done echo echo "L'has endevinat, era el $N!!" echo "Has necessitat $INTENTS intents." exit 0
Estructura until
L’estructura until és idèntica a while, excepte que la condició és negada. La llista d’ordres s’executa sempre que la condició retorni un codi de sortida diferent de zero.
until condició; do ordre1 ordre2 ... ordreN done
Per exemple:
X=1 until (( X > 10 )); do echo X val $X (( X=X+1 )) done
Estructura for
L’estructura for permet especificar una llista de valors i executar les sentències per a cada valor de la llista. La sintaxi d’aquest bucle és la següent:
for NOM_VAR in llista_de_valors; do ordre1 ordre2 ... ordreN done
La llista de valors és una seqüència de valors separats per espai o tabulador que s’aniran assignant a la variable NOM_VAR en cada iteració del bucle for. Per tant, les sentències que hi ha entre les paraules reservades do i done, s’executaran tantes vegades com valors hi hagi a la llista de valors.
La construcció clàssica de bucle for del shell difereix significativament de la del seu homòleg C i d’altres llenguatges de programació.
Per exemple, el següent és un bucle for que simplement mostra el valor de cada un dels ítems de la llista de valors (mostra els números de l’1 al 5):
for X in 1 2 3 4 5; do echo $X done
L’estructura for és un mecanisme de bucle molt flexible. Es pot construir un bucle amb qualsevol llista que es pugui generar. Podem generar llistes fàcilment, per exemple, mitjançant la llista de paràmetres passats en la crida al shell script o mitjançant la substitució d’ordres. Així, l’exemple que acabem de veure es podria haver expressat obtenint la llista de valors amb el resultat d’executar l’ordre seq de la manera següent:
for X in $(seq 1 5); do echo $X done
I en el cas que el nostre guió s’executi amb paràmetres, per exemple així:
nom_guió 1 2 3 4 5
El codi següent tindrà el mateix efecte que els anteriors:
for X in $*; do echo $X done
La variable especial $* conté la llista de paràmetres passats en la crida al programa. Teniu més informació al respecte a l’apartat “Paràmetres i variables especials” d’aquesta unitat.
Exemple d'utilització de l'estructura de control for
Feu un guió de shell que calculi l’ocupació de disc de cada directori de /home. Podeu obtenir la llista d’aquests directoris executant l’ordre ls dins del directori /home. Per calcular l’ocupació del directori podeu utilitzar l’ordre du (disk usage):
#!/bin/bash # ocupacio.sh # Calcula l'ocupació dels directoris de /home # cd /home for DIR in $(ls); do if [ -d $DIR ]; then # Calcular espai ocupat pel directori du -sh $DIR fi done
Feu el mateix guió de shell, però rebent per paràmetre el nom dels directoris dels quals volem conèixer l’espai que ocupen al disc.
#!/bin/bash # Nom: espai.sh # Mostra ocupació dels directoris rebuts per paràmetre # Crida: espai.sh DIR1 DIR2 DIR3 ... # for DIR in $*; do if [ -d $DIR ]; then # Calcular espai ocupat pel directori du -sh $DIR else echo "Error: $DIR no és cap directori." fi done
El shell Bash també permet utilitzar la construcció for a l’estil del llenguatge de programació C amb una sintaxi com la següent:
for (( expr1 ; expr2 ; expr3 )) ; do ordre1 ordre2 ... ordreN done
La sintaxi de for a l’estil de C que permet Bash no funciona amb sh (shell Bourne).
En aquest cas les expressions només poden ser expressions aritmètiques, no poden ser qualsevol ordre i s’utilitzen amb el propòsit següent:
- expr1: per inicialitzar el bucle.
- expr2: per comprovar si la llista d’ordres entre do i done s’ha d’executar.
- expr3: per canviar la condició després de cada iteració del bucle.
És equivalent a:
(( expr1 )) while (( expr2 )); do ordre1 ordre2 ... ordreN (( expr3 )) done
Per exemple, per mostrar els números de l’1 al 5:
for ((x=1; x<=5; x++)) { echo $x }
Veiem un altre exemple en què utilitzem bucles forimbricats (un bucle for dins d’un altre bucle for):
#!/bin/bash # # estrelles.sh # Exemples de bucles for a l'estil de C # N=10 for (( i=1; i<=$N; i++ )) do for (( j=1; j<=i; j++ )) do echo -n " *" done echo done for (( i=$N; i>=1; i-- )) do for (( j=1; j<=i; j++ )) do echo -n " *" done echo done
El resultat del guió de shell és:
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Funcions
El llenguatge del shell Bash permet definir funcions, encara que amb una implementació una mica limitada.
Una funció és un bloc de codi que implementa un conjunt d’operacions, una “caixa negra” que duu a terme una tasca específica. La utilització de funcions en un shell script és una manera d’agrupar una serie d’ordres perquè puguin ser executades posteriorment utilitzant un sol nom.
Considerarem l’ús de funcions en els casos següents:
- Quan el programa de shell és complex, és recomanable fer una programació modular, és a dir, agrupar codi en funcions o mòduls per estructurar el programa.
- Sempre que tinguem codi repetitiu, és a dir, una part de codi que s’ha d’utilitzar més d’una vegada, cal considerar l’ús d’una funció.
Fitxers de funcions
Hi ha un munt de scripts en el sistema que utilitzen les funcions com una manera estructurada d’agrupar una sèrie d’ordres. En alguns sistemes Linux, per exemple, es troba l’arxiu /etc/rc.d/init.d/functions, un arxiu de definició de funcions que s’inclou al principi de tots els scripts d’inici. Usant aquest mètode, les tasques comunes, com la comprovació de si un procés s’executa, iniciar o aturar un dimoni, etc., només cal escriure-les una vegada.
Com a administradors del sistema, podeu crear el vostre propi fitxer de funcions, per exemple /usr/local/bin/funcions, amb totes les funcions que utilitzareu amb freqüència en els vostres guions de shell. Després, en cada guió que hagi d’utilitzar les funcions, només cal que escriviu la línia següent:
. /usr/local/bin/funcions
No deixeu de posar el punt i l’espai (equivalent a l’ordre source) abans del nom del fitxer que conté les funcions perquè el shell actual reconegui les funcions.
La sintaxi per definir una funció és:
function nom_de_la_funcio { codi de la funcio }
O bé:
nom_de_la_funcio () { codi de la funcio }
Cal tenir en compte les consideracions següents:
- El nom de la funció ha de ser únic en el shell o script.
- El caràcter { d’obertura de la funció pot estar a la segona línia.
- La definició de la funció ha d’estar feta abans de la primera crida a la funció que hi hagi al programa.
Es recomana fer totes les definicions de les funcions necessàries abans del codi principal del guió que s’executa, tret que hi hagi raons concretes per no fer-ho.
Això dóna una visió molt millor del programa i assegura que tots els noms de les funcions es coneixen abans de ser utilitzats. Generalment, l’estructura bàsica d’un guió de shell que implementa funcions és:
#!/bin/bash VARIABLES_CONFIGURACIÓ DEFINICIO_FUNCIONS CODI_PRINCIPAL
Per cridar una funció des del programa de shell, simplement posem el nom de la funció, igual com fem amb qualsevol ordre. Quan es crida la funció, la llista d’ordres relacionades amb el nom de la funció s’executa.
#!/bin/bash # funcs.sh # Guió de shell per provar les funcions # # Definició de funcions saludaUsuari() { echo "Execució de la funció $FUNCNAME..." echo Hola $USER!! } mostraData() { echo "Execució de la funció $FUNCNAME..." echo Avui és $(date) } ### Programa principal ### saludaUsuari mostraData
Paràmetres a les funcions
Les funcions accepten paràmetres de la mateixa manera que les ordres o que els shell scripts. Per passar paràmetres a una funció només cal que en el moment de la crida a la funció especifiquem al costat del nom els valors dels paràmetres separats per espais. Per exemple:
nom_funcio param1 param2
La funció referencia els paràmetres rebuts per la posició que ocupen, igual que els paràmetres posicionals: $1, $2, etc. Tots els paràmetres es passen per valor, és a dir, quan retornem de la funció el valor dels paràmetres no haurà canviat.
Vegeu la descripció dels paràmetres posicionals i les variables especials a l’apartat “Paràmetres i variables especials” d’aquesta unitat.
Exemple de funció amb paràmetres
Feu una funció que faci la suma de dos números rebuts per paràmetre i en mostri el resultat. El programa principal ha de demanar els números per teclat.
#!/bin/bash # suma.sh # Demana dos números per teclat i en mostra la suma # # Definició de funcions suma() { (( TOTAL = $1 + $2 )) echo "La suma de $1 i $2 és: $TOTAL" } ### Programa principal ### echo -n "Introdueix un número: " read X echo -n "Introdueix un altre número: " read Y # Crida a la funció suma() amb paràmetres suma $X $Y
En executar una funció, els paràmetres passats a la funció esdevenen els paràmetres posicionals durant l’execució de la funció. Les variables especials *, @ i # també s’actualitzen per reflectir els canvis. La variable especial 0, que té el nom del guió de shell, no canvia. La variable del Bash anomenada FUNCNAME s’estableix amb el nom de la funció mentre dura l’execució de la funció.
Quan es completa l’execució de la funció, els valors dels paràmetres posicionals i de les variables especials *, @ i # es restauren als valors que tenien abans de l’execució de la funció. Per tant, si necessitem accedir des d’una funció als valors dels paràmetres posicionals i de les variables especials *, @ i # del programa que crida a la funció, haurem de guardar-los prèviament a la crida de la funció.
Exemple de funció amb paràmetres
Un exemple típic de funció amb paràmetres és una funció per mostrar els missatges d’error d’un guió de shell. Imagineu que esteu fent un guió de shell i que teniu múltiples punts on el programa dóna missatges d’error:
if [ -z "$DIR" ] ; then echo "$0: especifiqueu el nom del directori." exit 1 fi if [ ! -d $DIR ] ; then echo "$0: el directori no existeix." exit 1 fi
Quan això succeeix, és millor que creeu una funció per a aquest propòsit, per exemple:
mostraError() { echo "$0: $*" exit 1 } ... if [ -z "$DIR" ] ; then mostraError "especifiqueu el nom del directori." fi if [ ! -d "$DIR" ] ; then mostraError "el directori no existeix." fi
Noteu que la variable especial $0 ha mantingut el valor dins de la funció i que la variable $* ha agafat el valor de la llista de paràmetres passats a la funció.
Codis de retorn
Les funcions sempre tornen un valor al shell que les crida anomenat codi de retorn, que és anàleg al codi de sortida de les ordres. El codi de retorn d’una funció pot ser especificat de manera explícita amb l’ordre return; altrament, el valor retornat per la funció és el valor del codi de sortida de l’última ordre executada. El codi de retorn de la funció pot utilitzar-se en el shell script mitjançant $?, de la mateixa manera que el codi de sortida de qualsevol altra ordre.
Ordre return
L’ordre return interromp l’execució d’una funció. La sintaxi de l’ordre és:
return [n]
De manera opcional accepta un nombre enter com a paràmetre que és retornat al guió de shell que fa la crida com el codi de retorn de la funció i és assignat a la variable $?. Si no indiquem paràmetre, el valor retornat és el valor del codi de retorn de l’última ordre executada abans del return. L’ordre return utilitzada fora del context d’una funció fa el mateix que l’ordre exit.
Exemple de funció amb codi de retorn
Modifiqueu la funció suma.sh perquè en lloc de mostrar el resultat de la suma dels dos números per pantalla, el retorni mitjançant l’ordre return. En el programa principal accediu al valor retornat per la funció amb la variable $?.
#!/bin/bash # suma.sh # Demana dos nombres per teclat i mostra la suma # # Definició de funcions # Rep dos nombres i retorna la suma suma() { (( TOTAL = $1 + $2 )) return $TOTAL } ### Programa principal ### echo -n "Introdueix un número: " read X echo -n "Introdueix un altre número: " read Y suma $X $Y RESULTAT=$? echo "La suma de $X i $Y és: $RESULTAT"





