Sistema Operatiu Linux
Tema: Filtres i encadenaments
INTRODUCCIÓ
-
Els comandaments del shell que prenen una entrada (com ara el teclat),
realitzen una transformació i escriuen un resultat en una sortida
(com ara per pantalla) s'anomenen filtres.
-
En UNIX els filtres es poden encadenar de forma que el resultat d'un filtre
sigui l'entrada d'un altre filtre. aixó ho indiquem amb l'operador
|
.
-
Per exemple, el filtre wc serveix per a contar les paraules d'un
text. L'entrada pot ser un fitxer de text. La sortida estàndar és
la pantalla:
wc text -----> conta les paraules del fitxer text
-
Podem encadenar el resultat del comandament cat amb el filtre wc
per obtenir el mateix resultat:
cat text | wc -----> conta les paraules del fitxer text
En aquest tema veurem alguns filtres i com a l'encadenar-los obtenim
nous comandaments que poden efectuar tasques de certa complexitat. També
estudiarem el filtre programable sed , que permet especificar les
accions a realitzar en un llenguatge simple de programació.
GREP COM A FILTRE
-
Ja sabem com funciona grep d'un tema anterior: busca una cadena
en un fitxer. Ara veurem que el podem utilitzar com a filtre en encadenaments.
Per exemple:
grep -n total programa.c
mostra les líneas numerades (paràmetre -n) del fitxer
programa.c
que tenen la paraula total.
-
Podem fer el mateix amb un encadenament:
cat programa.c | grep -n total
-
Més exemples:
-
Per veure els subdirectoris del directori actual busquem una 'd'
al principi de cada línea retornada per el comandament ls -l
:
ls -l | grep '^d'
-
Per veure els fitxers sobre els qual el grup 'others' poden
llegir i escriure usem el caràcter '.' que actúa com
a "comodín" per a grep, i encadenem amb el resultat de ls:
ls -l | grep '.......rw'
-
Per comprobar si l'usuari joan està connectat:
who | grep joan
-
Exercici: proveu els comandaments anteriors i expliqueu com funcionen.
-
Quan utilitzem els operadors de grep per formar patrons de cerca
(que s'anomenen expresions regulars), grep funciona com un
filtre programable.
-
A més, si l'expresió regular és llarga, la podem posar
dins d'un fitxer de text i utilitzar el paràmetre -f de grep:
grep -f fitxer_patro fitxer
Buscarà en el text fixer segons l'expressió regular
continguda en fitxer_patro.
-
Com a exemple més complex, anem a utilitzar grep per a buscar
paraules en un diccionari. En el directori /usr/dict tenim el diccionari
words
que conté entre 40 i 50.000 paraules en anglés. Considereu
els següents exemples d'utilització de grep:
grep ^[aeiou] /usr/dict/word --> paraules
que comencin per vocal
grep ^[^aeiou] /usr/dict/word --> paraules
que no comencin per vocal
grep ^[^aeiou]a /usr/dict/word --> ídem amb
una 'a' en 2ª posició
grep ^[^aeiou]* /usr/dict/word --> mostra tot el
diccionari (perquè?)
grep ^[^aeiou]*a /usr/dict/word --> imposa una 'a' com
a primera vocal
-
Si heu entés l'última expressió regular, considereu
que tenim un fitxer de text patron amb la següent expressió:
^[^aeiou]*a[^aeiou]*e[^aeiou]*i[^aeiou]*o[^aeiou]*u
Llavors l'ordre:
grep -f patron /usr/dict/word
mostrarà les paraules del diccionari que contenen les cinc vocals
en ordre alfabètic, com per exemple:
abstemious arsenious bacterious caesious
-
Un altre exemple: utilitzant un encadenament podem trobar les paraules
de 9 lletres que tinguin les cinc vocals en ordre alfabètic:
grep -f patron /usr/dict/word | grep '.........'
Si escribiu un programa en 'C' que realitzi la mateixa tasca que aquesta
instrucció, comprobareu que calen bastantes més instruccions.
Aixó justifica el perquè UNIX ofereix un shell programable:
podem realitzar tasques complexes amb un mínim d'operacions.
El filtre SORT
-
Aquest filtre clasifica alfabèticament la seva entrada i la mostra
per pantalla.
-
L'opció -f no distingueix entre majúscules i minúscules.
-
L'opció -d només té en compte les lletres,
dígits i blancs i ignora la resta de caràcters.
-
L'opció -r classifica en ordre descendent.
-
Exemples:
$ sort text
-> mostra el text classificat per pantalla
$ ls | sort -f
-> llistat de fitxers classificat
$ cat text | sort -dr -> mostra el text classificat en
ordre descendent
-
Per defecte sort classifica la línea sencera que pren de
l'entrada. Si el fitxer de text està estructurat segons camps (columnes
separades per tabuladors) aleshores podrem classificar segons camps específics.
-
Per exemple, si tenim el seguent fitxer agenda en el qual s'han
separat les columnes amb tabuladors per distingir el nom, la ciutat i el
telèfon:
Joan Reus
977-45-67-89
Pere Tarragona 977-89-00-00
Marta Flix
977-90-09-09
Manel Reus
977-77-45-12
llavors l'ordre sort +1 donarà el següent resultat:
Marta Flix
977-90-09-09
Joan Reus
977-45-67-89
Manel Reus
977-77-45-12
Pere Tarragona
977-89-00-00
La notació +n significa que no utilitzi els n
primers camps. Per tant ha ordenat començant per el segon camp (ciutat).
El filtre UNIQ
-
Aquest filtre pren un texte com a entrada i elimina en la sortida les líneas
contígues que siguin duplicades, deixant només una.
-
Per exemple si el fitxer text conté líneas en blanc
contígues, uniq les eliminarà:
$ cat text
Prova del
comandament
uniq
$ uniq text
Prova del
comandament
uniq
El filtre tr
-
Aquest filtre transforma els caràcters de la seva entrada segons
l'opció que usem. En general transforma tots els caràcters
que trobi en un conjunt1 en el seus corresponents caràcters en un
conjunt2:
tr conjunt1 conjunt2
-
Per exemple per convertir de majúscules a minúscules i a
l'inreves:
$ cat text | tr A-Z a-z --> majúscules
a minúscules
$ cat text | tr a-z A-Z --> minúscules a majúscules
-
Si volem també transformar lletres accentuades (no existeixen en
anglés) podem incloure-les en les cadenes de conversió. Per
exemple per incloure les lletres àèíòù:
$ cat text | tr a-zàèíòù
A-ZÀÈÍÒÙ
-
Podem incloure en la transformació caràcters especials. Per
exemple, el següent comandament mostra el text amb una paraula
en cada línea, doncs sustitueix cada blanc entre paraules per un
salt de línia (caràcter especial \n):
$ cat text | tr ' ' '\n'
-
Aquest altre exemple efectúa la mateixa transformació amb
els fitxers que tenen camps separats per tabuladors (caràcter \t)en
comptes d'espais, com ara el fitxer agenda:
$ cat agenda | tr '\t' '\n'
-
Si tenim més d'un tabulador de separació entre dos camps
llavors quedarà un salt de varies líneas entre paraules.
Per evitar-ho tenim el paràmetre -s que substitueix una o
més d'una ocurrència del conjunt1 de caràcters per
només una ocurrència del conjunt2:
tr -s conjunt1 conjunt2
$ cat agenda | tr -s '\t' '\n'
-
El paràmetre -c cambia tot el que no coincideix amb
el conjunt1 per el conjunt2. Per exemple per cambiar qualsevol caràcter
que no sigui una lletra o un apòstrof per un únic salt
de línea:
$ cat agenda | tr -sc A-Za-záéíóúàèù\'
'\n'
Fixeu-vos que l'apóstrof va precedit d'una barra invertida
(\) per evitar que s'interpreti com unes cometes.
-
El paràmetre -d elimina de la sortida els caràcters
que coincideixen amb el conjunt1. En aquest cas no cal indicar el conjunt2.
-
Per exemple, el següent comandament mostra els caràcters no
alfabètics del fitxer text:
$ cat text | tr -d A-Za-záéíóúàèòù
ENCADENAMENTS DE FILTRES
-
El caràcter d'encadenament | pot usarse en el shell per continuar
un encadenament en vàries líneas.
-
Per exemple:
$ cat text |
tr -d A-Za-záéíóúàèòù
-
Podem també definir nous comandaments escribint encadenaments en
fitxers i després donant els permisos d'execució.
-
Veiem un exemple complert. Si seguim les següents passes obtindrem
un comandament anomenat trenca que pren un text qualsevol enmagatzemat
en un fitxer com a entrada i mostra les seves paraules línea a línea:
$ cat > trenca
cat $1 |
tr -sc \A-Za-záéíóúàèù'
'\n'
<CTRL> + <D>
$ chmod +x trenca
$ mv trenca bin
-
Per probar-lo amb el fitxer agenda:
$ trenca agenda
PRÀCTICA: filtres i encadenaments.
Un programa de recerca de paraules en el diccionari
-
El fitxer words conté entre 40 i 50.000 paraules en anglés,
una per línia. El propietari d'aquest fitxer és root,
la resta d'usuaris només tenen permís per a llegir-lo. L'utilitzarem
per als següents exemples.
-
La següent sentència busca el patrò 'bottle'
dins del diccionari:
grep bottle /usr/dict/words
El resultat serà quelcom com aixó:
bottle
bottled
bottleneck
bottlenecks
bottler
bottlers
bottles
-
Si volem que només trobi la paraula 'bottle' i no pas qualsevol
paraula que la contingui, haurem d'especificar que no han d'haber-hi caràcters
ni abans (com ara 'unbottle' )ni després de la paraula (com
ara 'bottled'). Ho podem fer usant els caràcters especials
^
(principi de línia) i $ (final de línia) de grep:
grep '^bottle$' /usr/dict/words
Ara el resultat serà:
bottle
-
Convertim ara aquesta senténcia en un ordre d´ús general
per a buscar paraules en el diccionari. Només cal susbtituïr
la paraula 'bottle' per el paràmetre $1:
grep '^'$1'$' /usr/dict/words
Observeu l'ús que fem de les cometes per delimitar els caràcters
especials de forma que no hi hagi confusió amb el paràmetre
$1.
Exercici: Amb l'editor vi creeu un comandament anomenat
verif
per buscar una paraula en el
diccionari. Proveu-lo tant amb paraules que existeixin en el diccionari
com que no existeixin.
L'EDITOR PROGRAMABLE SED
-
Potser l'editor de text més antic d'UNIX és l'ed.
Es tracta d'un editor de línias, que no presenta el text en tota
la pantalla (com vi) sinó línia a línia. Per
els que conegueu el sistema MS-DOS, ed és semblant a l'editor
vi
del DOS. Existeix una versió programable de l'ed anomenada
sed que permet efectuar el procés d'un text directament des
del prompt de Linux. La sintaxi és:
$ sed 'comandaments' fitxer_de_text
sed llegeix el fitxer línia a línia, aplica
els comandaments al text i dóna el resultat per pantalla.
Per exemple, per buscar i sustituir la paraula UNIX per la paraula
Linux en un fitxer anomenat prova i veure el resultat en
pantalla:
$ sed 's/UNIX/Linux/g' prova
Quan ho proveu, fixeu-vos en que sed no cambia realment
el fitxer, sinó que mostra el resultat de la transformació
per pantalla. El comandament s (substitució) té
un sintaxi semblant a vi. El paràmetre g indica que
la substitució sigui global, per a totes les ocurrències
de la paraula UNIX.
-
Per fer-ho, hem substituit el següent patró per un blanc: un
blanc seguit d'un caràcter (comodín .) seguit de qualsevol
nombre de caràcters (comodín *) seguit d'un altre blanc.
Exercici:
raonar perquè aquest patrò funciona.
-
En aquest altre exemple creem un comandament que retorna el nostre nom
d'usuari:
$ cat > meunom
who am i | sed 's/ .*//'
-
També podem especificar que els comandaments de sed
afectin només a unes quantes línies i no a tot el fitxer.
Altres exemples que utilitzen comandaments de sed limitant el nombre
de línies a les que afecten:
$ sed -n '10,20p' text --> mostrar línies
de la 10 a la 20 de text
$ sed '1,5d' text --> elimina
les línies de la ú fins la cinc
$ sed '$d'
--> salta fins l'ùltima línia i l'esborra
-
Un altre exemple: per llegir i mostrar un fitxer de text que conté
una paraula per línia (com el diccionari del sistema) fins la línia
que conté la paraula Linux:
$ sed '/^'Linux'$/q' fitxer_de_text
-
Hem afegit els símbols ^ i $ per delimitar la paraula, i van entre
cometes per evitar que el shell els interpreti com a caràcters normals.
L'ordre és: sortir (q) quan trobi la paraula Linux.
Mentre no la trobi, anirà escribint línia a línia
el text per pantalla.
-
Anem a crear una versió més el.laborada del comandament verif
utilitzant grep, sed i encadenaments: buscarà en el
diccionari totes les paraules d'un text, el qual està en un fitxer.
Anomenarem a aquest comandament verif2. Procedirem al seu disseny
pas a pas.
-
PAS1: Crear el fitxer de text en anglés.
$ cat > text_angles
This is a text for prove the command verif2
<CTRL> + <D>
-
PAS 2: Per buscar les paraules del text en el diccionari, cal que estiguin
separades (una paraula per línia). Tenim el comandament trenca
per fer-ho:
$ trenca text_angles > fitxer_trencat
-
PAS 3: Per a buscar cada paraula hem vist abans que hem de formar un patró
amb els símbols ^ (inici paraula) i $ (fi de paraula):
'^paraula$'. Per tant hem d'agafar el fitxer_trencat i afegir
a cada paraula aquest símbols. Per afegir el símbol $
al final de cada línia:
$ cat fitxer_trencat | sed 's/$/\$/' > patron1
-
Substituim el principi de cada línia per el símbol '$'.
Hem d'incloure la barra inversa (\$) per evitar que sed interpreti
el segon $ com a principi de línia. Per afegir el símbol
^
al principi de cada línia:
$ cat patron1 | sed 's/^/\^/' > patron2
Per comprovar el resultat feu un cat patron2.
-
PAS 3: Encadenem tots els comandaments anteriors per obtenir un únic
comandament, eliminant així els fitxers auxiliars fitxer_trencat,
patron1:
$ trenca text_angles |
sed 's/$/\$/' |
sed 's/^/\^/' > patron2
-
PAS 4: Buscar amb grep les paraules del patron2 dins del
diccionari:
$ grep -f patron2 /usr/dict/words
Exercici: crea el comandament patron que prepara el fitxer
patron2
a partir d'un fitxer de text qualsevol. Prova a buscar les seves paraules
en el diccionari del sistema. Per exemple pots usar el següent text
(de traducció inverosímil):
This is a zoom in the garden of zooms and a zulu walking
in it.
Qué pasa amb les paraules del text que no es troven en el
diccionari? Quines limitacions hi troves en aquest rudimentari verificador
ortogràfic?
PRÀCTICA: L'editor programable sed