dimarts, 7 de febrer del 2012

Linux: Programació del Shell

En aquest tercer lliurament d'apunts de Linux antics que vaig trobar en un CD, veiem una introducció a la programació de la shell.

Contingut:
1- Creació de nous comandaments
2 - Paràmetres en els comandaments
3 - Utilitzar la sortida de programes com a paràmetres del nostre programa
4 - Variables de l'entorn

 
1- Creació de nous comandaments
  • Quan tenim una sèrie de comandaments que utilitzem sovint es convenient agrupar los sota un nom apropiat de forma que obtenim un nou comandament.
  • Per exemple, si sovint volem contar els usuaris que estan connectats escriurem:
$ who | wc -l
 
  • Per crear un nou comandament equivalent primer de tot crearem un fitxer de text anomenat cu (de Contar Usuaris) que contingui l'ordre anterior:
$ echo 'who | wc -l' > cu
 
  • Observeu que les cometes són obligatòries. Què obtindríem si no les posem? En el fitxer cu quedaria el nombre d'usuaris connectats en comptes de l'ordre de comptar usuaris.
  • Recordem ara que el shell és un programa que s'està executant, i el seu nom és bash (shell per defecte). Com és un programa, es pot executar i redireccionar la seva entrada.
  • Per exemple, podem executar el shell redireccionant la seva entrada (que per defecte és el teclat) per tal de que sigui el fitxer cu:
$ bash < cu
El resultat serà el mateix que si haguéssim teclejat who | wc -l des del terminal.
  • D'altre banda el shell admet paràmetres, igual que ho fan molts comandaments. Si escrivim:
$ bash cu
    obtindrem el mateix resultat, doncs bash pren la seva entrada d'un fitxer si se'l passa com a paràmetre.
  • Per executar un fitxer de text que conté comandaments, com ara cu, tenim un altre alternativa que és convertir-lo en executable:
$ chmod +x cu
  • Un fitxer executable que conté comandaments del shell s'anomena fitxer d'script del shell o abreviada ment script. Aleshores el podrem executar directament:
$ cu
  • Tal com ho tenim ara cu funcionarà només si està situat en el directori de l'usuari i si aquest directori està en la variable d'entorn PATH. Per fer que funcioni de forma general podem moure'l al nostre directori privat bin i després afegir el camí a la variable PATH:
$ cd                                                       ens situem al nostre directori personal
$ mv cu bin                                            movem cu al nostre directori bin
$ PATH=$PATH:/home/usuari/bin      afegim el nostre bin al camí de cerca
$ echo $PATH                                       comprovem que s'ha afegit correctament
  • Els usuaris d'un script l'executen de la mateixa forma que els altres comandaments i per tant no tenen forma de saber que només conté una simple instrucció, en comptes de ser un complicat programa escrit en 'C'.
  • La forma en que el shell executa un script és creant un nou procés de shell (un subshell) exactament igual que si escrivim $bash cu
  • Per tant bash cu no és el mateix que bash < cu, malgrat el resultat sí ho sigui. En el segon cas no es crea cap subshell.

 

2 - Paràmetres en els comandaments
  • La majoria de comandaments dels shell admeten paràmetres i opcions. Es interessant que els comandaments que nosaltres creem tinguin també aquesta possibilitat.
  • Per exemple volem crear un comandament anomenat fx per fer que un fitxer sigui executable, de forma que sigui equivalent escriure:
$ fx cu
a escriure:
$ chmod +x cu
 
  • Necessitem un fitxer de text que contingui l'ordre chmod. Ja sabem com fer-ho això. El que no sabem és com indicar a fx quin serà el nom del fitxer que farem executable, doncs aquest serà diferent cada cop que executem fx.
  • Quan shell executa un fitxer de comandaments, cada ocurrència de $1 es reemplaça per el primer paràmetre, cada ocurrència de $2 es reemplaça per el segon paràmetre, i així successivament fins el paràmetre $9.
  • Per tant escriurem en el fitxer fx el text chmod +x $1. Veiem el següent exemple:
$ cd; cd bin                                        ens situem en el nostre directori bin personal
$ echo 'chmod +x $1' > fx                 crea el comandament fx
$ bash fx fx                                         el fa executable
$ echo echo "Hola mon" >hola         creem el comandament de proves hola
$ hola                                                  provem hola: no funciona encara
   hola: command not found
$ fx hola                                              fem que hola sigui executable
$ hola                                                  tornem a provar-lo
 
  • Hi tenim també casos en que el nombre de paràmetres és variable, és a dir que de vegades no donarem cap paràmetre en utilitzar el comandament i d'altres en donarem un o més d'un. Per aquests casos tenim l'abreviatura $* que significa "tots els paràmetres". Podríem definir aleshores el comandament fx així:
chmod +x $*
 
  • Exemple: definim el comandament cl per comptar les linies d'un  o més d'un fitxer:
$ echo "#cl: compta el nombre de línies en els fitxers especificats" > cl
$ echo 'wc -l $*' >>cl
$ fx cl
$ cl /home/usuari/bin/*
  • La primera sentencia crea una línia de comentari en el fitxer cl, identificada per el prefix #. En la segona línia afegim (símbol >>) la instrucció de comptar línies. En la tercera donem drets d'execució al fitxer cl i en l'última el provem comptant les líniesde tots els fitxer del nostre directori bin. També funcionarà el següent exemple en el qual no usem cap paràmetre:
$ ls /home/usuari/bin | cl
En aquest cas comptarà quants fitxers hi han en el directori /home/usuari/bin, doncs ls mostra els fitxers que hi han, els quals es prenen com a entrada del programa cl, el qual compta el nombre de lineas del fitxer d'entrada, és a dir, el nombre de fitxers que proporciona ls.
  • El símbol $0 està reservat per el propi nom del programa en execució. Per exemple si afegim la següent línia al fitxer cl:
echo echo === fi execució de $0 === >>cl
i l'executem, veurem que surt al final del nombre de linies del fitxer la frase:
==== fi execució de cl ====
 
  • També podem passar paràmetres que no siguin noms de fitxers. Per exemple considerem el fitxer agenda:
Pere Pages       977-33-44-00
Joan Pages       977-33-50-00
Marta Pages     977-58-00-00
  • Podem usar grep per buscar en ell.  Creem un comandament busca que cerca qualsevol nombre de textos en aquest fitxer i provem-lo:
$ echo 'grep $* /home/usuari/agenda' >busca
$ fx busca
$ busca Pere
Pere Pages       977-33-44-00
$ busca Pages
Pere Pages       977-33-44-00
Joan Pages       977-33-50-00
Marta Pages     977-58-00-00
$ busca 'Pere Pages'
grep: can't open Pages
 
  • Ens trobem un problema a l'executar $ busca 'Pere Pages'  i es que s'interpreta com si haguéssim escrit $ grep Pere Pages /home/usuari/agenda  que està malament doncs pren Pages com un nom de fitxer. Això és així degut a l'espai en blanc que conté la cadena que busquem. Hauríem de passar tota la cadena 'Pere Pages'  com un únic paràmetre per a grep. La solució serà modificar el comandament busca:
$ echo 'grep "$*" /home/usuari/agenda' >busca
  • Ara el $* serà reemplaçat per els paràmetres com abans, però aquests es passaran a grep com un únic paràmetre malgrat tinguin espais en blanc.

 
3 - Utilitzar la sortida de programes com a paràmetres del nostre programa
  • La sortida de qualsevol programa pot utilitzar-se en un comandament tancant el programa amb cometes invertides (l'accent obert català: ` `).
  • Exemple:
$ echo Data i hora actual `date`
Data i hora actual Sun Dec 31 12:00:00 CET 2000
  • Les cometes invertides poden anar també incloses dintre de les cometes dobles:
$ echo "Data i hora actual `date`"
  • Un altre exemple: volem enviar un mail a una llista d'usuaris els noms dels quals estan en un fitxer de text anomenat llista. El text del mail està en un altre fitxer anomenat carta. Podem fer-ho de la següent forma:
$ cat llista                                    comprovem el contingut del fitxer llista
nom1
nom2
nom3
$ mail `cat llista` < carta          enviem la carta als usuaris de la llista
 
cat mostra el contingut del fitxer llista, és a dir la llista de noms, que es converteixen en paràmetres per a mail gràcies a l'ús de les cometes ``. Es com si haguéssim escrit:
$mail nom1 nom2 nom3 <carta
  • En efecte, quan el shell interpreta la sortida en cometes inverses com a paràmetres tracta als salts de línia com a separadors de paraules.





 

4 - Variables d'entorn

  • El shell proporciona el manteniment del que s'anomena variables de l'entorn
  • Són cadenes de caràcters que representen valors que defineixen l'entorn de treball
  • Hi han variables d'entorn predefinides del sistema i pròpies de l'usuari
  • Podem modificar els valors de qualsevol d'elles (en algunes cal tenir prou privilegis)
  • Existeixen moltes variables predefinides que depenen de cada sistema
  • Les variables s'indiquen de la següent forma (sense espais):
        VARIABLE=valor
  • El nom de la variable pot ser qualsevol cadena de caràcters que no comenci per un número ni contingui el caràcter $ ni espais en blanc.
  • El valor pot ser qualsevol cadena, incloent espais. Si conté espais cal tancar la cadena entre cometes dobles ("")
  • Exemples:
$MIVAR="prova variables"
  • Assigna la cadena "prova variables" a la variable MIVAR. Normalment posem el nom de les variables d'entorn en majúscules per distinguir-les dels noms de fitxers i comandaments.
  • Podem veure el contingut de la variable amb:
$echo $MIVAR
  • Mostrarà el contingut de la variable MIVAR. És obligatori col·locar el signe $ davant del nom.
  • Més exemples:
$XYZ="cat fitxer"
$ $XYZ
  • Aquesta seqüència fa que assigni una ordre a una variable. Després s'executa invocant el nom de la variable. Podem utilitzar-ho per abreviar ordres llargues i d'ús freqüent.

export
 
  • Cada variable es defineix en el Shell actiu. Si canviem de Shell (per exemple creant un subshell) la variable deixa d'existir. Podem provar-ho amb la variable XYZ:

$sh                                            arranca un subshell
$echo $XYZ                               no mostra res
 
  • En aquesta seqüència creem un subshell (sh) i cridem a la variable XYZ que ara estarà buida. Per tornar a l'anterior shell:
$exit
$echo $XYZ
 
  • Per poder disposar de la variable en tots els shells, cal exportar-la:
$export XYZ
$sh
$echo $XYZ
  • Aquest exemple d'exportació només és vàlid si es fa des del shell principal. No podem exportar des d'un subshell al seu superior (proveu-ho).
env
 
  • Mostra la llista completa de les variables d'entorn actualment definides.Algunes variables bàsiques són: PATH, LOGNAME, etc.

set
  • Permet també veure les variables d'entorn fins i tot algunes que no apareixen amb env perquè són utilitzades per el propi shell. Per exemple la variable PS1 que representa el "prompt".
$PS1="Linux:"
  • Farà aparèixer el prompt Linux:


Acotació d'arguments en les variables d'entorn
 
  • El Shell interpreta una cadena de caràcters que contingui espais en blanc com múltiples arguments. Per exemple:
$NOMVAR=prova variables
  • Donarà error perquè interpreta dos arguments: prova queda assignat a la variable d'entorn i el segon l'intenta executar com si sigues un comandament.
  • Això ho sol ventem acotant la cadena entre cometes dobles:
$NOMVAR="prova variables"
 
  • No obstant les variables d'entorn contingudes dintre de les cadenes acotades també són interpretades per el shell. Per exemple:
$MEUNOM="El meu nom d'usuari es $LOGNAME"
$echo $MEUNOM
  • Resultat: El meu nom d'usuari és a0alumne
  • Si volem que el shell no interpreti la variable cal acotar la cadena amb cometes simples:
$MEUNOM='El meu nom d'usuari és $LOGNAME'
$echo $MEUNOM


Més coses sobre variables
 
  • Si volem afegir més camins de cerca a la variable PATH:
$PATH=$PATH:nous camins
  • Per combinar el contingut d'una variable amb constants:
$VAR=hola
$echo ${VAR}s
  • Resultat: holas, mostra el contingut de la variable seguida de la constant. Es diferent que fer:
$echo $VARs
    que mostraria el valor de la variable VARs, si existeix.
     
  • Podem avaluar el valor d'una variable i prendre una decisió segons el valor:
$echo ${BUSTIA:-valor2}
 
  • Si la variable BUSTIA existeix mostrarà el seu valor. En cas contrari mostra valor2 sense assignar res a la variable.
$echo ${BUSTIA:=valor2}
  • Si la variable BUSTIA existeix mostrarà el seu valor. En cas contrari mostra valor2 i aquest valor s'assignarà  a la variable.
unset
 
  • Serveix per esborrar el valor d'una variable:
$unset NOMVAR
  • Equivalentment podem fer:
$NOMVAR=
 
 

Utilitzar variables dintre de fitxers script: pas per valor i per referència
  • Hem vist que el valor d'una variable no es passa automàticament als subshells. Això implica que un comandament creat per nosaltres no podrà canviar el valor d'una variable doncs els fitxers scripts són executats per un subshell.
  • Exemple:
$ echo ´X="Adeu"        Creem un fitxer script de dues lineas per a definir i mostrar X
> echo $X´ >setx
$ cat setx                   Comprovem el contingut
$ X=Hola                   Definim la variable X
$ bash setx                 Executem l'script
Adeu                           X conté Adeu en el subshell
$ echo $X
Hola                           X continua valent Hola en el shell original
 
  • Quan passem variables de l'entorn a un fitxer script ho podem fer de dues formes:
    1. Passar només el valor de la variable, però sense que l'script pugui modificar la variable. Diem que el pas de la variable es fa per valor.
    2. Donar a l'script l'accés complert sobre la variable. Diem que passem la variable per referència.
  • Quan exportem (amb export variable) les variables que l'script necessita estem fent un pas de variables per valor. Exemple:
$ export X                    donem accés al valor d'X per a tots els subshells
$ bash                           arranquem un subshell
$ echo $X                     mostrem el valor d'X
Hola
$ X=Adeu                    modifiquem en el subshell el valor d'X
$ echo $X                    mostrem el nou valor en el subshell
Adeu
$ exit                            tornem al shell original
$ echo $X                    mostrem de nou X
Hola                            el valor torna a ser l'original, no s'ha canviat
  • El shell proporciona el comandament ' . ' (un punt) que força l'execució de l'script en el propi shell en comptes de pasar-ho a un subshell. Això equival a donar a l'script l'accés total sobre la variable (pas per referència). Exemple:
$ . setx                        Executem l'script setx en el shell actual
Adeu                           X conté Adeu durant l'execució de l'script
$ echo $X
Adeu                           X ha estat modificat per l'script
 
  • Quan utilitzem el punt per executar un script el que realment succeeix es que l'entrada estàndard del shell (el teclat) es redirecciona temporalment per prendre-la del fitxer script. Realment l'script no s'està executant, i per tant no necessita tenir permisos d'execució.
  • Hi ha un altre forma de passar només el valor d'una variable del shell a un script (pas per valor) a banda d'usar export que es assignar-li el valor en la mateixa línia en la que cridem a l'script. Per exemple:
$ echo 'echo $X' > veureX     Creem un script
$ chmod +x veureX                Donem permís d'execució a l'script
$ X=Hola  veureX                  Inicialitzem X i cridem a l'script en una mateixa línia d'ordres
Hola                                       La variable X val Hola durant l'execució sense haber-la exportat
 
  • Utilitzarem l'execució amb el punt quan volem modificar permanentment el valor d'una variable i utilitzarem les assignacions en la mateixa línia que l'script o bé l'exportació quan les modificacions siguin temporals (només tenen efecte dins de l'script).
Exercicis


Pràctica: Creació de comandaments i pas de paràmetres

1. Crear un comandament anomenat nfind en el vostre directori personal que executa l'ordre
find . -name fitxer -print
on fitxer és un paràmetre. L'execució del nou comandament tindrà la següent sintaxi:
nfind fitxer
Proveu el comandament amb algun fitxer existent en el vostre directori personal redireccionant l'entrada estàndard del shell.
 
 
 
2. Si el comandament nfind funciona correctament, moveu-lo al vostre directori bin i doneu-li drets d'execució. Torneu-lo a provar però ara com a executable. Recordeu que heu de tenir afegit el vostre directori bin al camí de cerca PATH.
 
 
3. Modifiqueu el programa nfind per tal de que accepti múltiples paràmetres. Per exemple:
nfind fitxer1 fitxer2 fitxer3
 
 
4. Modifiqueu el programa nfind per tal de que abans d'executar l'ordre find mostri les següents línies:
echo "nfind: busca i mostra noms de fitxers en el directori actual"
echo "=========================================="
Utilitzeu el comandament cp (copia de fitxers) i el redireccionament >> per afegir aquestes lineas en el fitxer nfind sense necessitat d'usar un procés de text o rescriure'l.

Pràctica: Variables d'entorn. La sortida de programes com a paràmetres.


1. Anem a modificar el "prompt" del sistema utilitzant la variable
d'entorn PS1. Segueix les següents passes:
1.1 Mostra el contingut actual de la variable PS1
1.2 Fes una còpia del contingut de PS1 en la variable PS
1.3 Modifica PS1 per tal de que mostri el teu nom seguit d'un ">".
Per exemple: Pere>
1.4 Comprova que ha variat el prompt del sistema
1.5 Restaura el prompt al seu valor original, utilitzant la variable PS

2. Obre un subshell amb  $ bash. Defineix la variable PROVA=proves . Exporta-la.
Torna al shell original. Existeix la variable? Perquè?
3. Visualitza la variable PATH. Fes una cópia de seguretat en la variable
PATHC. Esborra la variable PATH. Comproba que està buida.
4. Prova de llistar el teu subdirectori. Què passa? Perquè?
5. Prova de llistar el teu directori indicant el camí complert per l'ordre:
/bin/ls . Què passa? Perquè?
6. Restaura el contingut de la variable PATH.
7. Què val la variable HOME? Què passa si fem $ cd $HOME  ?
8. Comprovar el resultat dels seguents comandaments i explicar-los:
$ RUTA = "El meu directori és $HOME"      ; echo $RUTA
$ RUTA = 'El meu directori és $HOME'      ; echo $RUTA
$ RUTA = 'El meu directori és "$HOME" '   ; echo $RUTA
$ RUTA = "El meu directori és '$HOME' "   ; echo $RUTA
$ RUTA = "El meu directori és \$HOME"     ; echo $RUTA

9. Crea una variable d'entorn anomenada BIN que conté la mateixa ruta de la
variable HOME seguida de "/bin" utilitzant els símbols { } per a concatenar.
Què passa si fem $ cd $BIN?
10. Amb una única ordre, fes un $ cd $BIN tenint en compte que si la variable
BIN no existeix, aleshores el comandament cd ha d'anar al teu directori bin
particular. Utilitza els operadors { } per avaluar el valor de la variable BIN
i prendre una decissió segons el valor. Esborra BIN i comproba que el comandament continúa portant-te al teu directori bin.
11. Fes com en l'exercici anterior peró ara si la variable BIN no existeix, el
comandament li assigna el valor correcte (el camí del teu directori bin
 personal). Utilitza el que has fet en els exercicis 9 i 10.
12. Crea un comandament anomenat lloc que mostra uns missatges informant de
l'usuari amb el que estem connectats i el directori actual. Per saber l'usuari
utilitzeu la variable d'entorn USER i per el directori el comandament pwd.
Exemple d'execució:
 
$ lloc
Ets l'usuari a0sanz
Estas en el directori /treball/home/asi20001/a0sanz/docs

Doneu drets d'execució per tothom sobre el fitxer lloc i proveu_lo.
13. Demaneu a un company que provi el vostre programa lloc. Què passa? Com
solucionar-ho?
 

Cap comentari:

Publica un comentari a l'entrada

Gestió d'usuaris i grups en Linux

Usuaris i grups Linux  Els comptes de Linux són com els comptes de Windows o MacOS; però els detalls no, així que cal explicar alguns detall...