Nyolcadik lecke

Verzió: 1.0

Keresés szövegfájlokban

A legeslegfontosabb szuro a grep, a Unix parancsok népszeruségi listáján mindjárt az ls után következik. A grep arra való, hogy segítségével megadott feltételeknek eleget tevo szavakat tartalmazó sorokat keressünk a szövegfájlokban. A szövegfájlokról annyit kell tudnunk, hogy sorokból állnak, a sorok pedig szavakból vagy más néven mezokbol. A sorok végén sorvége jel van, a szavakat (mezoket) pedig szóközök választják el egymástól.

A grep kimenetén kiírja az összes "találatot" - vagyis azokat a sorokat, amelyek tartalmaznak legalább egy, a feltételt kielégíto szót. A szintaxis nagyon egyszeru, meg kell adni, hogy mit hol keressen:

grep mit_keressen hol_keresse

Például: barátaink és üzletfeleink nevet és e-mail címét egy .addressbook nevu szöveges fájlban tartjuk, és szeretnénk megnézni Kovacs nevu barátunk (vagy üzletfelünk) e-mail címét. A megoldás: "kigrepeljük" a szövegfájlból azokat a sorokat, amelyekben elofordul a "Kovacs" név.

orlando% grep Kovacs .addressbook
Kovacs Istvan pistike@badguys.hell.gov
Kovacsuzem h06789@kalapacs.uzem.com
orlando%

A nagy kezdobetus Kovacs - a Unixtól megszokott módon - nem azonos a nemecsekernos kovacs névvel. Ha azt szeretnénk, hogy a grep a keresés során ne különböztesse meg a kis- és nagybetuket, akkor a -i kapcsolót kell használnunk. A kapcsolókat a parancssorban az elso argumentum (keresési minta) elott kell megadni. A fenti példában a grep két sort írt ki, hiszen a "Kovacs" szó a "Kovacsuzem"-nek is része. Ha ezt el akarjuk kerülni, mert azt akarjuk, hogy a grep csak a teljes szavakat találja meg, akkor használjuk eloszeretettel a -x kapcsolót.

Két további hasznos kapcsoló: az egyik (-n) hatására a grep a megtalált sorok elé kiírja a sorszámot is, a másikkal (-v) pedig fordított muködésre lehet kapcsolni. Ilyenkor azokat a sorokat írja ki, amelyek NEM tartalmazzák a megadott mintát, a többit pedig lenyeli.

orlando% w | grep -v gyevi_biro

A fenti példa kilistázza az összes bejelentkezett felhasználót (w parancs), kivéve a gyevi_biro-t. A példán az is látszik, hogyan használhatjuk a grepet egy másik parancs kimenetének megszurésére.

A hol_keresse mezoben használhatjuk a *, ?, stb karaktereket, azaz a grep több fájlban is kutathat a feltételnek megfelelo sorok után. A félreértések elkerülése végett a sorok elé kiírja azt is, hogy melyik fájlban találta oket. (Vigyázat! A grep nem rekurzív, tehát nem nézi meg azokat a fájlokat, amelyek a megadott könyvtárból nyíló alkönyvtárakban vannak.)

Reguláris kifejezések

Jó, jó, de hogy kell leírni azokat a bizonyos "megadott feltételek"-et, amelyek alapján a grep a keresést végzi? A Unix tervezoi erre a célra alkották meg a reguláris kifejezéseket (regular expressions).

A dolog nagyon hasonló a * és ? karaktereket tartalmazó fájlnevekhez. A reguláris kifejezés egy olyan különleges karaktersorozat, amit a grep (és számos más) parancs mintaként értelmez. Ha egy szó megfelel a mintának, azt mondjuk rá, hogy "illeszkedik a reguláris kifejezésre".

A legegyszerubb eset, mikor a reguláris kifejezés nem tartalmaz semmilyen speciális karaktert. Az ilyen kifejezés csak önmagára illeszkedik. Vegyük példaként Csocsi és Vonyi egyik hal(l)hatatlan kétsorosát:

Romeo Julian topreng:
Fivere venne csak zokon a romancot!
Leverne rolam e rokon a zomancot.

Ezek után a

orlando% grep zokon romeo

Csak az elso sort találja meg. Az alábbi parancs viszont mindkét sort kiírja:

orlando% grep '[zr]okon' romeo

mert a [zr]okon reguláris kifejezésre mind a rokon, mint a zokon szó illeszkedik. Ezek után lássuk, milyen feltételek szerepelhetnek a reguláris kifejezésekben:

c
Bármely közönséges karakter illeszkedik saját magára.
\c
Kikapcsolja a c speciális karakter speciális jelentését. Akkor használatos, ha történetesen épp speciális karaktereket szeretnénk keresni.
^
A mintát a sor elejére igazítja. Csak azok a sorok illeszkednek rá, amelyek a ^ jel utáni reguláris kifejezésre illeszkedo szóval kezdodnek.
$
Ugyanazt csinálja, mint az elozo, annyi különbséggel, hogy a mintát a sor végére igazítja.
.
Az újsor kivételével minden karakter illeszkedik rá.
[...]
A szögletes zárójelek közé zárt karakterek bármelyike illeszkedik rá.
[^...]
A szögletes zárójelek közé zárt karakterek KIVÉTELÉVEL bármelyik karakter illeszkedik rá.
[n-n]
A megadott tartományon belül eso karakterek bármelyike illeszkedik rá.
*
A csillag elott álló karakter akárhány elofordulása (nulla is!) illeszkedik rá.

Nézzünk pár példát! A reguláris kifejezéseket idézojelek közé kell tenni; ennek magyarázatát a példák után találjuk.

orlando% grep '[tf]arka'

Kiírja az összes olyan sort, amelyben a tarka, vagy a farka szó elofordul.

orlando% grep '^\^' kalap

Kiírja az összes olyan sort a kalap nevu fájlból, amely ^ jellel kezdodik. (Figyeljük meg a ^ jel használatát! Az elso jelenti azt, hogy az utána következo kifejezésnek a sor elején kell lennie, a második pedig maga a keresendo karakter, amelyet most \ jellel hatástalanítunk, hiszen különleges karakter.)

orlando% ls -l | grep '^d........x'

Ez egy bonyolult, de nagyon praktikus példa. Az ls -l parancs kimenetébol azokat a sorokat írjuk ki, amelyek eleget tesznek a következo feltételeknek:

- d betuvel kezdodnek
- második-kilencedik karakterük bármi lehet
- tizedik karakterük x

Könnyu rajönni, hogy így azon alkönyvtárak listáját kapjuk, amelyekbe mindenki beléphet.

orlando% ls -l | grep '[^.xdh]$'

Megint az ls parancs kimenetében keresgélünk; ezúttal azokat a fájlokat szurjük ki, amelyek NEM .xdh-ra végzodnek.

Idézojelek

Big problem: sajnos a reguláris kifejezések különleges karaktereit a shell is értelmezi, méghozzá a saját szabályai szerint. Ez alapjában véve hasznos tulajdonság, de nem most, ezért védekeznünk kell ellene. Ezt úgy tehetjük meg, hogy a reguláris kifejezést egyszeres normál idézojelek (') közé zárjuk. A shell ekkor a idézojelek közötti részt változatlan formában adja át a grep parancsnak.

Majdnem ugyanez történik, ha egyszeres idézojelek helyett kétszerest (") használunk. A különbség annyi, hogy a shell ilyenkor megnézi, hogy van-e a stringben hivatkozás shell változóra. Ha van, akkor annak az értékét behelyettesíti és úgy adja tovább a kifejezést a grepnek.

Van egy harmadik fajta idézojel is, a visszafele döntött idézojel (`). Az ilyen jelek közé zárt kifejezést a shell megpróbálja parancsként lefuttatni és a végrehajtás eredménye kerül át a grephez.

Ennek szellemében:

orlando% cat >animals
$eger (ez egy gazdag eger)
fakutya
vasmacska
Microsoft mouse
<Ctrl-d>

Ezzel létre is hoztuk az "adatbázist", amin most gyakorlatozni fogunk. A shell változók kezelésének kipróbálására hozzunk létre egy "eger" nevu változót:

orlando% set eger=mouse
orlando% grep '$eger' animals
$eger (ez egy gazdag eger)
orlando% grep "eger" animals
Microsoft mouse
orlando%

Látható, hogy míg az egyszeres idézojeleknél a grep a $eger reguláris kifejezést kapta meg, addig a kétszeres idézojelek használata esetén a shell változó értékét, azaz a mouse szót - ezért jelent meg a második grep parancs végrehajtása után a "Microsoft mouse" sor.

Nézzük most a ` jelet! A végrehajtandó parancs legyen az echo, írassuk ki vele a kutya szót, s ezt adjuk át a grepnek!

orlando% grep `echo kutya` animals
fakutya
orlando%

Fájlok keresése

Fájlok keresésére a Unixban find nevu program szolgál. Szintaxisa:

find keresési-útvonalak kifejezések

Nézzünk néhány példát! Tegyük fel, hogy egy valami.o nevu fájlt keresünk, amely valahol a home directorynkban, vagy az abból nyíló alkönyvtárak egyikében van. Ezt így találhatjuk meg:

orlando% find $HOME -name valami.o -print

A -name kapcsoló után kell megadni a keresett fájl nevét. Természetesen nem egyértelmu nevet is megadhatunk a * és a ? segítségével, de ilyenkor a nevet ' jelek közé kell tenni. A -print kapcsoló azt mondja meg a find programnak, hogy ha talált olyan fájlt, ami megfelel a keresési feltételek, akkor írja ki a nevét a teljes elérési útjával együtt.

A keresés helyeként megadhatunk több könyvtárat is: ilyenkor mindegyiket végignézi, az összes alkönyvtárával együtt.

Egyszerre több keresési feltételt is megadhatunk, ehhez azonban zárójeleket kell használnuk, amelyeket meg kell védenünk attól, hogy a shell saját belátása szerint értelmezze oket. A következo példa megkeresi az aktuális könyvtárban és az abból nyíló alkönyvtárakban található .c-re és .o-ra végzodo nevu fájlokat.

orlando% find . \( -name '/*.c' -o -name '*.o' \) -print

Az egész logikus, bár elso ránézésre kissé kuszának tunik. Derítsünk fényt a homályra: A pont (.) jelenti a keresési útvonalat, jelen esetben az aktuális könyvtárat. Több könyvtárat is megadhatunk, szóközökkel elválasztva. Ezután következik a zárójel, amit -a grepnél tanultak alapján- a \ jellel védünk meg a shelltol. A -name kapcsolók már ismertek. A -o mondja meg a findnak, hogy a két -name-val eloírt feltételt hozza vagy kapcsolatba; azaz keresse meg mindazon fájlokat, melyek vagy az egyik, vagy a másik (vagy mindkét) feltételnek eleget tesznek.

Nézzünk most egy fokkal bonyolultabb példát. A korábbi leckékbol már tudjuk, hogy a home directoryban lehet egy .plan nevu fájl, aminek tartalma megjelenik a képernyon, ha valaki lefingerel minket. Nézük végig, hogy kinek van ilyen .plan fájlja! A megtalált .plan fájlokat irassuk ki a képernyore!

orlando% find /usr1/public/users -name '.plan' -print -exec cat {} \;

Feltételezzük, hogy a felhasználók home könyvtárai a /usr1/public/users alkönyvtárból nyílnak. A .plan nevet idézojelek közé tettük, hogy a shell ne értse félre a pontot. Újdonság a -exec kapcsoló, az ez után megadott parancs hajtódik végre minden alkalommal, mikor a find talál valamit. Ebben az esetben minden megtalált .plan fájlnál a find átadja a .plan nevét elérési útvonalával együtt a cat parancsnak.

A paraméterlista a -exec kapcsolónál kezdodik és a pontosvesszonél (;) ér véget. A {} szimbolummal lehet hivatkozni a find által megtalált fájlra. A cat-nak jelen esetben nincs paramétere, de ha az rm parancsot hajtanánk végre, megadhatnánk a -i kapcsolót, amire a shell minden megtalált fájl törlése elott rákérdezne szándékunk komolyságára:

orlando% find /usr1/public/users -name '*.gif' -print -exec rm -i {} \;

Megjegyzés: e példához teljesen hasonló paraméterezésu find parancsot használnak a fasiszta tipusú rendszergazdák a felhasználók alkönyvtáraiban található több megabyte-os .gif kiterjesztésu (általában pucér lányokat ábrázoló) digitalizált képek automatikus törlésére.

A parancs végrehajtása során melléktermékként több oldal hibaüzenetet kapunk, mivel a find megpróbál minden alkönyvtárba belelépni, és ha ez nem sikerül neki (mert az alkönyvtár le van tiltva), akkor a "Permission denied" üzenettel szórakoztat minket. Szerencsére a standard error csatornát - s vele együtt a hibaüzeneteket is - át lehet irányítani. Erre a célra most a /dev/null egység látszik a legalkalmasabbnak, ez ugyanis nyomtalanul elnyeli a neki küldütt karaktereket. Keressuk meg a gépen található összes C programot! A megoldás sh-ban így néz ki (A 2-es azt jelenti, hogy most kivételesen nem a standard outputot (1), hanem a standard error (2) csatornát irányítjuk át):

orlando% sh
$ find / -name '*.c' -print 2>/dev/null
$ <Ctrl-d>
orlando%

Az sh-t itt csak a keresés idejére indítottuk el, mert nem biztos, hogy az alapértelmezett shellünkben ugyanígy kell átirányítani a standard error csatornát (próbáljuk ki! Ha nem muködik, nézzünk utána a manualban, hogyan kell csinálni!).

Keresés és csere

Nagyon gyakori muvelet, hogy egy szövegben valamilyen szót szeretnénk egy másikra cserélni. Ezt a leggyorsabban a sed nevu programmal hajthatjuk végre, az alábbi szintaxis szerint:

orlando% sed 's/mit/mire/g' hol >hova

A fenti parancs végignézi a "hol" fájlt, kicseréli benne az összes "mit" szót "mire"-re és az eredményt a "hova" nevu fájlba (átirányítás jel és fájlnev megadása nélkül a képernyore) írja. A sed egy nagytudású szövegszerkeszto, de sajnos szinte lehetetlen kezelni, ezért csak a legelvetemültebb buherátoroknak javasoljuk, hogy parancsait elsajátítsák. A mindennapi életben elég, ha a fenti példát megjegyezzuk, valamint azt, hogy "hol" és a "hova" fájlként SOHA ne adjuk meg ugyanazt a nevet!!

Mezok kiemelése a szövegfájl soraiból

A grep parancsnál említettük, hogy a szavakat mezoknek is hívjuk. Ha egy szövegfájlt táblázat szeru (mint amilyen az e-mail címeket tartalmazó .addressbook fájl), akkor megesik, hogy a sorokból csak bizonyos szavakat szeretnénk kiemelni. Erre az awk program használható. Az awk végigolvassa a megadott fájl sorait, és egy speciális programozási nyelven leírt muveleteket végez rajta. Ez nagyon misztikusan hangzik; itt csak azt mutatjuk meg, hogy hogyan lehet egy szövegfájl mezoit kinyomtatni. Íme:

orlando% awk '{print $1 $2}' .addressbook

Az idézojelek között található a "program", ami most egy print utasításból és két mezohivatkozásból áll. Az awk a kimenetén kiírja a .addressbook fájl minden sorának elso és második mezojét, más szóval a táblázat elso két oszlopát.

Az awk sokkal bonyolultabb, mint amire egy átlagos felhasználónak élete során szüksége van. A kiváncsi buherátor-jelölteknek ismét azt javasoljuk, hogy olvassák szorgalmasan az awk parancs man oldalát.

Feladatok

  1. Mit csinál a következo Unix parancsokból összerakott cso?
    % rm -rf `du -s * | sort -rn | head -1 | awk '{print $2}'`;
    
  2. A mail spooler fájlban a sor elején található "From" szó jelzi a levél elejét. Állapítsuk meg a grep és a wc segítségével, hogy hány darab levél van a postaládánkban!
  3. Mi történik, ha a sed-del végzett keresésnél ugyanazt a nevet adjuk meg "hol"-ként és "hova"-ként is? Miért?
  4. Írassuk ki a last parancs kimenetébol azoknak a felhasználóknak a username-jét és rendszerben eltoltött idejét, akik a tty1 terminálról jelentkeztek be! (Csak ezt a két adatot írassuk ki!)


*************************************************************************
*=                                                                     =*
*=                           SZERZOI JOGOK                             =*
*=                                                                     =*
*=   Ez  a dokumentum a Unix  operacios  rendszer  es a szamitogepes   =*
*=   halozatok elterjedeset  kivanja  elosegiteni, ezert dijmentesen   =*
*=   terjesztheto.  Nem szabad azonban a terjesztes soran a szoveget   =*
*=   megvaltoztatni,  barmilyen  modon  megcsonkitani  es a  szerzoi   =*
*=   jogokra vonatkozo megjegyzest eltavolitani!  Sem  a dokumentum,   =*
*=   sem annak barmely resze nem hasznalhato fel segedanyagkent vagy   =*
*=   tankonyvkent profitorientalt intezmenyekben vagy tanfolyamokon,   =*
*=   a szerzok elozetes irasbeli engedelye nelkul!                     =*
*=                                                                     =*
*=   (C) Csaky Istvan és Mork Peter         Miskolc, 1994. januar 19   =*
*=                                                                     =*
*************************************************************************