2. Comencem amb el Sha-Bang¶
Shell programming is a 1950s juke box . . .
—Larry Wall
En el cas més simple, un guió no és més que una llista de comandes del sistema emmagatzemades a un fitxer de text. I ja això és útil, si més no per evitar-nos teclejar una i altra vegada la mateixa seqüència de comandes.
Exemple 1. *cleanup*: Un script per netejar els fitxers de log de /var/log
1 2 3 4 5 6 7 | # Cleanup
# A executar com a root, per suposat
cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Fitxers de Log netejats."
|
No hi ha res inusual aquí. Es tracta d’una seqüència de comandes que podrien haver estat invocades una darrera de l’altre des de la línia de comandes en una consola o terminal. Els avantatges de col·locar aquestes comandes en un guió van més enllà de simplement no haver de tornar a reescriure-les. El guió es converteix en un programa, una eina, i com a tal, pot ser fàcilment modificada o adaptada a requeriments particulars.
Exemple 2. *cleanup*: versió millorada del guió de neteja.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #!/bin/bash
# Una capçalera adequada per un guió Bash
# Cleanup, versió 2
# A executar com a root, per suposat
# Mostra un missatge d'error i sorti si no s'executa com a root.
LOG_DIR=/var/log
# És millor fer servir variables que els valors directament.
cd $LOG_DIR
cat /dev/null > messages
cat /dev/null > wtmp
echo "Fitxers de Log netejats."
exit # La manera adequada de sortir d'un guió.
# Un "exit" sense paràmetres retorna el resultat de sortida de
# la comanda anterior.
|
Ara si que comença a semblar un guió real! Però encara podem anar-hi més lluny...
Exemple 3. *cleanup*: Una versió ampliada i generalitzada dels guions anteriors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | #!/bin/bash
# Cleanup, versió 3
# Atenció:
# -------
# Aquest guió fa servir força característiques que seran explicades
# més endavant.
# Quan ja entenguis la primera meitat d'aquest llibre, no hauries de
# trobar res misterios aquí.
LOG_DIR=/var/log
ROOT_UID=0 # Només l'usuari amb $UID 0 té privilegis de root
LINIES=50 # El nombre de línies guardades per defecte
E_XCD=86 # Error: no es pot canviar de directori
E_NOTROOT=87 # Error: No és root
# A executar com a root, per suposat
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Cal ser executat per root"
exit $E_NOTROOT
fi
if [ -n "$1" ]
# Comprova si se li ha passat al menys un argument per línia de comandes
then
linies=$1
else
linies=$LINIES # Si no ha estat especificat el nombre de línies, es
# considera el valor per defecte
fi
# Stephane Chazelas suggereix la següent manera de comprovar els
# arguments de línia de comandes, encara que és una mica avançat pel
# nivell actual d'aquest llibre.
#
# E_WRONGARGS=85 # Error: argument no numèric (format d'argument
# incorrecte)
#
# case "$1" in
# "" ) linies=50;;
# *[!0-9]*) echo "Ús: `basename $0` linies-a-netejar";
# exit $E_WRONGARGS;;
# * ) linies=$1;;
# esac
#
# Salta a la secció sobre bucles per desxifrar-ho.
cd $LOG_DIR
if [ `pwd` != "$LOG_DIR" ] # o bé if [ "$PWD" != "$LOG_DIR" ]
# que vol dir si no es troba a /var/log?
then
echo "No es pot canviar al directori $LOG_DIR."
exit $E_XCD
fi # Torna a comprovar si es troba en el directori adequat abans de
# començar a trastejar amb el fitxer de log.
# Seria molt més eficient de la següent manera:
#
# cd /var/log || {
# echo "No es pot canviar al directori corresponent." >&2
# exit $E_XCD;
# }
tail -n $linies messages > mesg.temp # Guarda la darrera secció del
# fitxer de missatges de log.
mv mesg.temp messages # Reanomena el fitxer com a log
# del sistema.
# cat /dev/null > messages
# Ja no cal, doncs el mètode anterior és més segur.
cat /dev/null > wtmp # ': > wtmp' i '> wtmp' produeixen el mateix efecte.
echo "Fitxers de Log netejats."
# Fixat que la resta de fitxers de log que es trobin a /var/log no
# es toquen.
exit 0
# El valor zero de retorn, indica a la shell que el guió ha
# finalitzat sense errors.
|
Donat que podria ser que no ens interessés eliminar el log de sistema completament, aquesta versió del guió conserva la darrera secció dels missatges de log. Constantment descobrirem maneres d’ajustar els nostres guions per millorar l’efectivitat.
El sha-bang ( #!)[1]
a l’inici d’un fitxer permet al sistema saber que el contingut d’aquest fitxer ha de ser processat per un determinat intèrpret.
De fet, #! és un número màgic de dos bytes[2], una marca especial que
designa el tipus de fitxer. En aquest cas indica que és un executable.
Trobaràs més informació sobre aquest fascinant tema escrivint man magic
a la teva consola.
Tot just després del sha-bang apareix el programa que
interpreta el contingut del fitxer, ja sigui comandes de la shell,
d’un altre llenguatge de programació (ex. Python) o qualsevol altra
utilitat.
El que apareix a continuació del sha-bang és interpretat per a aquest
programa[3].
Considera les següents capçaleres:
#!/bin/sh
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/tcl
#!/bin/sed -f
#!/bin/awk -f
Cadascuna d’aquestes línies defineixen un intèrpret diferent per processar el contingut del fitxer.[4]
/bin/sh
és generalment la shell per defecte en la majoria de les
variants comercials de UNIX (no així en sistemes Linux que fan servir
bash). Si ens interessa molt la portabilitat en màquines no Linux,
podem fer servir sh, tot i que sacrificarem les característiques
específiques de Bash. Amb tot, un guió que s’executi amb sh respectarà
l’estàndard POSIX[5]. Per més informació, mira
XXX 36.9. Portability Issues i XXX Chapter 34. Gotchas.
Fixat que el camí que s’indica al sha-bang ha de ser correcte, altrament generarà un missatge d’error (normalment No es troba la comanda) com a única resposta d’executar el guió [#envline].
Si el nostre guió consisteix únicament en una llista de comandes i no
inclou cap directiva interna de la shell (com ara una assignació a una
variable de l’estil linies=50
), podem ometre #!.
De fet, si Bash és la teva shell per defecte, #! no és en realitat
necessària a l’inici del guió. Amb tot, la necessitaràs si finalment
et cal executar el guió des d’alguna altra shell com ara tcsh.
Nota
En aquest llibre, se’t proposa que construeixis els teus guions de manera modular. Guarda peces de codi que creguis que poden ser-te d’utilitat en el futur. Amb el temps, disposaràs d’una biblioteca extensa d’utilitats. Per exemple, considera el següent guió que comprova si ha estat cridat amb un cert nombre d’arguments:
1 2 3 4 5 6 7 8 9 10 | E_WRONG_ARGS=85 # resultat de retorn en cas d'error
parametres_valids="-a -h -m -z"
# -a = all, -h = help, etc.
if [ $# -ne $nr_args_esperats ]
then
echo "Ús: `basename $0` $parametres_valids"
# `basename $0` és el nom d'aquest fitxer
exit $E_WRONG_ARGS
fi
|
Anotacions
[1] | En la literatura es sol trobar com she-bang o sh-bang. El nom està format per les inicials dels mots amb els que sovint s’anomenen els símbols que el composen: sharp (#) i bang (!). |
[2] | Algunes versions de UNIX (aquelles basades en 4.2 BSD)
suposadament prenen un nombre màgic de quatre bytes, en requerir un
espai en blanc després de l’exclamació (bang). Per exemple #!
/bin/sh . Probablement no sigui més que un mite segons Sven
Mascheck. |
[3] | El sha-bang en un guió de shell serà la primera línia que veurà l’intèrpret de comandes (sh o bash per exemple). Donat que la línia comença amb #, serà interpretat correctament com un comentari. La línia ja haurà fet la seva feina, permetent escollir l’intèrpret. De fet, si el guió inclou una línia extra amb #!, bash la interpretarà com un comentari.
|
[4] | El sha-bang permet fer alguns trucs interessants, com per exemple:
També pots intentar afegir a un fitxer de text, com ara |
[5] | (P)ortable (O)perating (S)ystem (I)nterface (POSIX) és un intent d’estandardització dels sistemes operatius “tipus” UNIX. Les especificacions POSIX apareixen llistades a la web del Open Group. |
[6] | Per evitar aquests problemes, els guions poden començar
amb #!/bin/env bash . En el cas de les màquines UNIX, això pot
ser especialment útil, donat que bash no es troba a /bin |