Razvan Deaconescu
20 noiembrie 2005
Scripturile shell sunt folosite deseori pentru prelucrarea output-ului unei comenzi . Aceasta sarcina este deseori destul de simpla: afisarea unei parti a output-ului prin filtrarea catorva linii. Alteori, insa, este necesara o procesare mult mai sofisticata.
Comenzi folosite ca si filtre de text sunt head, tail, grep, sort, uniq, tr.
Cele doua comenzi sunt folosite pentru afisarea numai a primelor sau a ultimelor linii de text din cadrul unui fisier. Sintaxa lor este asemanatoare:
head [-n lines] files tail [-n lines] files
Primul argument, daca exista, afiseaza primele, respectiv ultimele n linii din text. Lipsa acestuia impune n = 10.
Exemple de comenzi sunt:
razvan@ragnarok:~/cfiles/solab/labs$ ls -l total 44 drwxr-xr-x 2 razvan razvan 4096 Nov 16 00:01 lab1 drwxr-xr-x 3 razvan razvan 4096 Oct 12 00:02 lab2 -rwxr-xr-x 1 razvan razvan 937 Oct 19 00:53 lab2html.sh drwxr-xr-x 3 razvan razvan 4096 Oct 19 00:50 lab3 drwxr-xr-x 3 razvan razvan 4096 Oct 26 01:09 lab4 drwxr-xr-x 3 razvan razvan 4096 Nov 2 09:47 lab5 drwxr-xr-x 3 razvan razvan 4096 Nov 12 19:56 lab6 drwxr-xr-x 2 razvan razvan 4096 Nov 16 12:43 lab7 drwxr-xr-x 2 razvan razvan 4096 Nov 20 12:34 lab8 drwxr-xr-x 5 razvan razvan 4096 Oct 17 19:52 solab drwxr-xr-x 2 razvan razvan 4096 Nov 12 20:06 tema1 razvan@ragnarok:~/cfiles/solab/labs$ ls -l | head -3 total 44 drwxr-xr-x 2 razvan razvan 4096 Nov 16 00:01 lab1 drwxr-xr-x 3 razvan razvan 4096 Oct 12 00:02 lab2 razvan@ragnarok:~/cfiles/solab/labs$ ls -l | tail -4 drwxr-xr-x 2 razvan razvan 4096 Nov 16 12:43 lab7 drwxr-xr-x 2 razvan razvan 4096 Nov 20 12:34 lab8 drwxr-xr-x 5 razvan razvan 4096 Oct 17 19:52 solab drwxr-xr-x 2 razvan razvan 4096 Nov 12 20:06 tema1 razvan@ragnarok:~/cfiles/solab/labs$
In cazul comenzi tail o optiune utila este -f care mentine fisierul de vizualizat deschis pe masura ce programele scriu in el, spre exemplu pentru a vizualiza un fisier de log pe masura ce informatiile sunt scrise in el:
$ tail -f /var/log/apache/access_log
grep
Comanda grep permite localizarea liniilor intr-un fisier care contine o expresie cautata. Sintaxa de baza este
$ grep word file
file este numele fisierului in care vrem sa gasim cuvantul word. Un exemplu de utilizare este:
razvan@ragnarok:~/cfiles/solab/labs$ grep latex lab2html.sh latex2html -split 0 -nosubdir -show_section_numbers "$BASE.tex" 2>&1 > /dev/nulllatex2html -split 0 -nosubdir -show_section_numbers "$BASE-teme.tex" 2>&1 > /dev/null latex2html "$BASE.tex" 2>&1 > /dev/null latex "$BASE.tex" 2>&1 > /dev/null
De multe ori dorim sa realizam cautarea in mod case-insensitive (adica sa nu conteze faptul ca se cauta UNIX sau unix). Pentru aceasta folosim optiunea -i.
Cand nu se precizeaza un fisier se foloseste intrarea standard, grep devenind ideal pentru lucrul cu pipes. De exemplu:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ last | head -100 | grep tty root tty1 Wed Nov 16 11:00 - down (00:00) razvan tty1 Wed Nov 16 00:07 - 00:08 (00:01)
O alta optiune utila este -v. Aceasta permite cautarea acelor linii care NU contin cuvantul transmis ca parametru. Spre exemplu, daca dorim afisarea utilizatorilor care nu au ca si director de baza un director de forma /home/nume, vom folosi comanda:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ grep -v /home /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh .......
Un alt exemplu de folosire este parsarea output-ului comenzii ps:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ ps -af | grep lyx razvan 3767 3719 0 12:19 pts/0 00:00:05 lyx razvan 4219 3719 0 12:53 pts/0 00:00:00 grep lyx razvan@ragnarok:~/cfiles/solab/labs/lab8$ ps -af | grep lyx | grep -v grep razvan 3767 3719 0 12:19 pts/0 00:00:05 lyx
Optiunea -n permite afisarea numarului liniei care continea cuvantul cautat.
Putem de asemenea sa afisam numai fisierele care contin acel cuvant (fara afisarea liniilor). Pentru aceasta folosim optiunea -l (listare). De obicei este folosita in conjunctie cu optiunea -r pentru cautarea recursiva in cadrul unei structuri de directoare:
razvan@ragnarok:~/cfiles/solab/labs$ grep -r -l "#include <sys/wait.h>" . ./lab2/lab2.lyx ./lab2/lab2.tex ./lab2/wait_zombie.c ./lab2/simple_exec.c ./solab/lab2/lab2.lyx ./solab/lab2/lab2.tex ./solab/lab2/wait_zombie.c ./solab/lab2/simple_exec.c ./lab6/lab6.lyx ./lab6/mmap.c ./lab6/lab6.tex
Comanda tr (transliterate) este folosita pentru a translata caracterele dintr-un set de caractere intr-un alt set de caractere. Sintaxa de baza este:
tr 'set1' 'set2'
Spre exemplu, daca am dori sa aflam numarul de cuvinte dintr-un text, am translata toate caracterele speciale in spatii cu o comanda de forma:
$ tr '!?":'\[\]{}(),.' ' ' < file
Caracterele [ si ] au fost escapate folosind \. Daca dorim sa translatam literele mari in litere mici, folosim:
$ tr 'A-Z' 'a-z' < file
In cazul in care setul set2 are mai putine caractere decat setul set1 acestea vor fi multiplicate pentru a ajunge la aceeasi dimensiune.
Urmatorul pas ar fi eliminarea spatiilor redundante. Optiunea -s (squeeze) inlocuieste o succesiune de doua sau mai multe caractere cu unul singur. Un exemplu este:
razvan@ragnarok:~/cfiles/solab/labs$ echo shell programming | tr -s 'lm' shel programing
Comanda sort este utilizata pentru sortarea liniilor alfabetic. Un exemplu de utilizare este:
razvan@ragnarok:~/cfiles/solab/labs$ echo -en "alfa\nomega\ngamma\nbeta\n" alfa omega gamma beta razvan@ragnarok:~/cfiles/solab/labs$ echo -en "alfa\nomega\ngamma\nbeta\n" | sort alfa beta gamma omega
O optiune utila in cazul sort este sortarea dupa valoarea numerica a sirurilor. Pentru aceasta folosim -n. De exemplu:
razvan@ragnarok:~/cfiles/solab/labs$ echo -en "10\n43\n4\n9\n123\n5\n" | sort 10 123 4 43 5 9 razvan@ragnarok:~/cfiles/solab/labs$ echo -en "10\n43\n4\n9\n123\n5\n" | sort -n 4 5 9 10 43 123
De multe ori output-ul apare intr-o forma in care elementele de sortat sunt intr-o alta coloana (nu prima, cea folosita implicit de sort). Pentru aceasta se poate folosi optiunea -k (key). Sintaxa, in cazul folosirii acestei optiuni, este:
sort -k start, end file
Aici start este coloana de start a cheii, iar end este coloana de stop a cheii. Prima coloana este cea dupa care se face sortarea, iar, in cazul in care sunt doua sau mai multe elemente egale, se face deosebirea intre acestea folosind coloana urmatoare din cheie, etc.
Exemplu de utilizare este:
sort -rn -k 2,2 file ; sorteaza dupa a doua coloana
Se pot elimina duplicatele folosind optiunea -u la sort. Totusi, de multe ori este util sa afisam de cate ori apare un cuvant. Pentru aceasta vom folosi comanda uniq cu optiunea -c. Atentie! uniq face eliminarea duplicatelor numai daca liniile sunt sortate. Exemple de utilizare:
razvan@ragnarok:~/cfiles/solab/labs$ echo -en "alfa\nbeta\nbeta\nalfa\nbeta\ngamma\nalfa\n" | uniq alfa beta alfa beta gamma alfa razvan@ragnarok:~/cfiles/solab/labs$ echo -en "alfa\nbeta\nbeta\nalfa\nbeta\ngamma\nalfa\n" | sort | uniq alfa beta gamma razvan@ragnarok:~/cfiles/solab/labs$ echo -en "alfa\nbeta\nbeta\nalfa\nbeta\ngamma\nalfa\n" | sort | uniq -c 3 alfa 3 beta 1 gamma
Comanda wc (word count) este folosita pentru a contoriza numarul de linii, de cuvinte sau de caractere dintr-un text sau de la intrarea standard. Pentru aceasta i se pot specifica optiunile -c, -w, -l.
Comanda cut selecteaza numai anumite parti ale fisierului de intrare sau ale intrarii standard. Sintaxa cea mai folosita este:
cut -d delim -f fields
Folosindu-se delimitatorul delim se vor selecta numai campurile fields. Exemple de utilizare sunt:
razvan@ragnarok:~/cfiles/solab/labs$ ls -l total 44 drwxr-xr-x 2 razvan razvan 4096 Nov 16 00:01 lab1 drwxr-xr-x 3 razvan razvan 4096 Oct 12 00:02 lab2 -rwxr-xr-x 1 razvan razvan 937 Oct 19 00:53 lab2html.sh drwxr-xr-x 3 razvan razvan 4096 Oct 19 00:50 lab3 drwxr-xr-x 3 razvan razvan 4096 Oct 26 01:09 lab4 drwxr-xr-x 3 razvan razvan 4096 Nov 2 09:47 lab5 drwxr-xr-x 3 razvan razvan 4096 Nov 12 19:56 lab6 drwxr-xr-x 2 razvan razvan 4096 Nov 16 12:43 lab7 drwxr-xr-x 2 razvan razvan 4096 Nov 20 14:38 lab8 drwxr-xr-x 5 razvan razvan 4096 Oct 17 19:52 solab drwxr-xr-x 2 razvan razvan 4096 Nov 12 20:06 tema1 razvan@ragnarok:~/cfiles/solab/labs$ ls -l | cut -d ' ' -f 1,9 total drwxr-xr-x 00:01 drwxr-xr-x 00:02 -rwxr-xr-x 19 drwxr-xr-x 00:50 drwxr-xr-x 01:09 drwxr-xr-x 2 drwxr-xr-x 19:56 drwxr-xr-x 12:43 drwxr-xr-x 14:38 drwxr-xr-x 19:52 drwxr-xr-x 20:06 razvan@ragnarok:~/cfiles/solab/labs$ ls -l | tr -s ' ' | cut -d ' ' -f 1,9 total drwxr-xr-x lab1 drwxr-xr-x lab2 -rwxr-xr-x lab2html.sh drwxr-xr-x lab3 drwxr-xr-x lab4 drwxr-xr-x lab5 drwxr-xr-x lab6 drwxr-xr-x lab7 drwxr-xr-x lab8 drwxr-xr-x solab drwxr-xr-x tema1
Comenzi din aceeasi categorie sunt paste si join.
Cele mai puternice utilitare pentru filtrarea textului in lumea UNIX sunt sed si awk. Acestea permit utilizatorilor sa editeze fisierele text si sa filtreze iesirea altor comenzi folosind expresii regulate.
Exista multe similaritati intre sed si awk:
Sintaxa folosita pentru invocarea awk sau sed este:
command 'script' filenames
Aici command este awk sau sed, script este o insiruire de comenzi intelese de awk sau sed, iar filenames este lista de fisiere asupra carora se actioneaza. Daca nu se specifica nici un fisier, se foloseste intrarea standard. In acest fel putem utiliza awk si sed ca si filtre pentru iesirea altor comenzi.
Cand awk sau sed ruleaza, vor realiza urmatoarele operatii:
Structura unui script este de forma
/pattern/ action
Aici pattern este o expresie regulata, iar action este actiunea pe care fie awk, fie sed va trebui sa o execute cand se intalneste sablonul. Caracterele / din jurul expresiei regulate sunt folosite ca delimitatori.
Cand awk sau sed executa un script, foloseste urmatoarea procedura pentru fiecare inrregistrare:
Elementele de baza ale unei expresii regulate sunt:
Metacaractere sunt:
Fie pattern-ul
/peach/
Daca expresia este utilizata in sed sau awk atunci orice linie care contine acest sir este selectata. Dintre liniile care pot fi selectate, putem avea
we have a peach tree in the backyard i prefer peaches to plums
Pattern-ul
/a.c/
se potriveste cu orice linie care contine siruri precum a+c, a-c, abc, match, a3c, in vreme ce pattern-ul
/a*c/
se potriveste cu sirurile de mai sus si, in plus, cu ace, yacc, arctic. De asemea se potriveste si cu linia
close the window
Se observa ca nu exista litera a in propozitie; aceasta din cauza ca * se potriveste cu 0 sau mai multe aparitii ale lui a.
Se observa ca expresiile regulate folosite in cadrul sed si awk sunt similare cu cele folosite de utilitarul lex (flex).
sed inseamna stream editor. Citeste fiecare linie de la intrare si realizeaza un set de actiuni. Sintaxa de baza pentru sed este:
sed 'script' files
script este o secventa de comenzi de forma
/pattern/ action
pattern este o expresie regulata iar action poate avea una din formele de mai jos:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ sed '/0\.[0-9][0-9]$/p' fruit_prices.txt fruit price banana 0.89 banana 0.89 peach 0.79 peach 0.79 kiwi 1.50 pineapple 1.29 apple 0.99 apple 0.99 mango 2.20 razvan@ragnarok:~/cfiles/solab/labs/lab8$ sed -n '/0\.[0-9][0-9]$/p' fruit_prices.txt banana 0.89 peach 0.79 apple 0.99
Se observa necesitatea folosirii optiunii -n. In mod obisnuit, sed afiseaza toate liniile de la intrare la iesire.
Daca dorim sa eliminam intrarea despre un fruct vom folosi comanda:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ sed '/^[Mm]ango/d' fruit_prices.txt fruit price banana 0.89 peach 0.79 kiwi 1.50 pineapple 1.29 apple 0.99
Se observa ca in cazul folosirii actiunii d nu mai este nevoie sa precizam optiunea -n.
Pentru substitutie de expresii regulate, sintaxa este
/pattern/s/pattern1/pattern2/
In acest caz, pattern1 este inlocuit cu pattern2 pentru toate liniile care se potrivesc cu pattern. Foarte frecvent pattern este omis.
Sa consideram urmatorul exemplu in care dorim sa inlocuim sirul eqal cu equal din cauza unor greseli de editare. Setul de comenzi de rulat este:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ cat nash.txt things that are eqal to the same thing are eqal to each other razvan@ragnarok:~/cfiles/solab/labs/lab8$ sed 's/eqal/equal/' nash.txt things that are equal to the same thing are eqal to each other razvan@ragnarok:~/cfiles/solab/labs/lab8$ sed 's/eqal/equal/g' nash.txt things that are equal to the same thing are equal to each other
Se observa ca in prima faza se producea inlocuirea numai a primei aparitii a lui pattern1. Pentru a se realiza inlocuirea tuturor aparitiilor s-a folosit optiune /g (global).
De multe ori va trebui sa folosim comenzi sed multiple (de forma /patttern/ action). Pentru aceasta folosim optiunea -e in forma
sed -e 'command1' -e 'command2' ... -e 'commandN' files
Din cauza ca sed citeste de la intrarea standard in lipsa unui fisier, poate fi folosit ca si filtru.
awk este un limbaj de programare care permite cautarea in fisiere si modificarea inregistrarilor din aceste fisiere prin intermediul sabloanelor. Numele awk vine de la numele creatorilor sai: Alfred Aho, Peter Weinberger si Brian Kernighan.
Ca si la sed, sintaxa de baza este de forma:
awk 'script' files
script este o succesiune de comenzi de forma:
/pattern/ { actions }
pattern este o expresie regulata, iar actions reprezinta una sau mai multe comenzi care sunt acoperite in acest capitol. Daca se omite pattern, se aplica actiunile asupra fiecarei linii.
Spre exemplu, o implementare a lui cat folosind awk ar fi:
$ awk '{ print; }' file
Cand se citeste o linie, awk plaseaza campurile care le-a parsat in variabila 1 pentru primul camp, variabila 2 pentru al doilea camp si asa mai departe. Pentru a accesa un camp, vom folosi operatorul $. Astfel primul camp este $1.
Un exemplu de utilizare este:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ awk '{ print $1 $3 ; }' fruit_prices.txt fruitquantity banana100 peach65 kiwi22 pineapple35 apple78 mango34 razvan@ragnarok:~/cfiles/solab/labs/lab8$ awk '{ print $1 , $3 ; }' fruit_prices.txt fruit quantity banana 100 peach 65 kiwi 22 pineapple 35 apple 78 mango 34
Putem formata iesirea folosind comanda printf:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ awk '{ printf "%-15s %s\n", $1 , $3 ; }' fruit_prices.txt fruit quantity banana 100 peach 65 kiwi 22 pineapple 35 apple 78 mango 34
Dorim sa indicam acele fructe care costa mai mult de 1 prin asezarea unei stelute la sfarsitul liniei. Avem urmatorul script:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ cat highlight.sh #!/bin/sh awk ' / *[1-9][0-9]*\.[0-9][0-9] */ { print $1, $2, $3, "*"; } / *0\.[0-9][0-9] */ { print ; } ' fruit_prices.txt razvan@ragnarok:~/cfiles/solab/labs/lab8$ ./highlight.sh banana 0.89 100 peach 0.79 65 kiwi 1.50 22 * pineapple 1.29 35 * apple 0.99 78 mango 2.20 34 *
Se observa ca avem o afisare necorespunzatoare. Pentru a evita aceasta situatie vom folosi variabila 0 care este folosita pentru a stoca intreaga linie asa cum fusese ea citita. Scriptul va arata in felul urmator:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ cat highlight.sh #!/bin/sh awk ' / *[1-9][0-9]*\.[0-9][0-9] */ { print $0, "*"; } / *0\.[0-9][0-9] */ { print ; } ' fruit_prices.txt razvan@ragnarok:~/cfiles/solab/labs/lab8$ ./highlight.sh banana 0.89 100 peach 0.79 65 kiwi 1.50 22 * pineapple 1.29 35 * apple 0.99 78 mango 2.20 34 *
In lipsa unui fisier, awk foloseste intrarea standard si poate fi folosit ca si filtru. Daca dorim sa afisam numai numele si dimensiunea unui fisier vom folosi urmatoarea comanda.
Operatorul !~ este un operator boolean si intoarce true daca valoarea precizata (in acest caz primul camp) nu se potriveste cu pattern-ul (in acest caz total).
Definirea si utilizarea variabilelor in awk este similara cu cea din shell. Totusi, aici se face distinctia intre variabile numerice si siruri de caractere. Astfel, secventa de instructiuni:
a=1 b=a+1
Operatorii aritmetici sunt +, -, *, /, %, ^. La fel ca si in C, este permisa folosirea operatorului compus de forma +=, -=, *=, etc.
Daca dorim sa realizam anumite actiuni numai la inceputul sau la sfarsitul programului putem utiliza pattern-urile speciale BEGIN si END. In acest caz, forma generala pentru o comanda awk devine:
awk ' BEGIN { actions } /pattern/ { actions } /pattern/ { actions } END { actions } ' files
Cand se precizeaza BEGIN, actiunile asociate acestuia vor fi executate inaintea citirii oricarei linii. Cand se precizeaza END, actiunile asociate vor fi executate inainte de iesire.
awk predefineste un set de variabile care pot fi folosite de utilizator. Acestea sunt:
$ awk 'BEGIN { FS='':'' ; } { print $1, $6 ; }' /etc/passwd $ awk -F':'
Exista trei forme principale de controlul fluxului if, for, while.
Sintaxa de baza pentru if:
if (expression) { action1 else { action2 }
Pentru while este:
while (expression) actions }
Pentru for:
for (initialize_counter; test_counter; increment_counter) { action }
Sintaxa pentru toate aceste comenzi este identica cu cea din C si permite o foarte mare flexibilitate utilizatorului.
De multe ori este util sa realizam un fisier separat pentru utilizarea unor comenzi awk. Pentru aceasta in loc de script in sintaxa de baza pentru awk vom folosi acel nume de fisier. Spre exemplu in loc de:
razvan@ragnarok:~/cfiles/solab/labs/lab8$ awk -F':' ' BEGIN { num = 0; } /home/ { num += 1; } END { print "Numarul total de utilizatori care au directorul de baza in /home este:", num; } ' /etc/passwd Numarul total de utilizatori care au directorul de baza in /home este: 9
putem utiliza
razvan@ragnarok:~/cfiles/solab/labs/lab8$ cat passwd.awk BEGIN { num = 0; } /home/ { num += 1; } END { print "Numarul de utilizatori care au directorul de baza in /home este ", num; } razvan@ragnarok:~/cfiles/solab/labs/lab8$ awk -F':' -f passwd.awk /etc/passwd Numarul de utilizatori care au directorul de baza in /home este 9
This document was generated using the LaTeX2HTML translator Version 2002-2-1 (1.71)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -split 0 -nosubdir -show_section_numbers lab8.tex
The translation was initiated by Razvan Adrian Deaconescu on 2005-11-20