grep
-komento ja säännölliset lausekkeetGrep -komennolla tulostetaan tiedostosta rivit, joista löytyy jokin
hahmo. "Hahmo" on yksinkertaisimmillaan pelkkä kirjainjono,
mutta se voi sisältää myös tietyllä tavalla muodostettuja monimutkaisempia
rakenteita. Esimerkiksi grep 'kissa' teksti.txt
tulostaa
tiedostosta teksti.txt ne rivit joissa esiintyy merkkijono "kissa", ja
grep 'kis\+a' teksti.txt
tulostaa rivit, joissa
esiintyy "kisa", "kissa", "kisssa" jne.
Grep on hyvin monipuolinen ohjelma. Sillä voidaan hakea vaikka rivejä joissa on peräkkäin 'x', 'y', neljästä kuuteen vokaalia ja 'z', tai rivejä joissa on sana "kissa" erotuksena merkkijonoon "kissa" (vrt. sana "takissani") tai joissa "kissa" on rivin alussa tai lopussa. Grepillä voi hakea yhdellä kertaa useammasta tiedostosta, ja tarvittaessa näyttää vain nimet niistä tiedostoista joissa hakusana esiintyy. Voidaan myös vain laskea monellako rivillä haettava merkkijono on, tai hakea ne rivit joissa haettavaa merkkijonoa ei ole. Haettavat merkkijonotkin voidaan lukea tiedostosta. Voidaan myös laskea monellako rivillä on vain sana kissa eikä mitään muuta. Tarvittaessa voidaan tulostaa haluttu määrä rivejä ennen ja jälkeen osumakohdan.
Tämän sivun on tarkoitus kertoa kaikki grep-komennon käytöstä. Sivulla on ensin taulukko optioista ja sitten selitystä optioista, jotka ensimmäisellä lukukerralla kannattaa silmäillä hyvin lyhyesti läpi ja kokeilla jotakin itse. Kokeilun jälkeen on parasta palata optioihin uudelleen ja kokeilla niitä järjestyksessä. Tämän jälkeen seuraa säännölliset lausekkeet, eli kerrotaan miten hahmoja muodostetaan.
Sivulla esitellään GNU-versiota grep-komennosta. Tämä grep-versio tulee tavallisten Linux-jakelupakettien mukana (Esim. Fedora ja Debian), ja voidaan asentaa muihinkin Unix-tyyppisiin järjestelmiin. Muut grepin versiot ovat yleensä suppeampia kuin tässä esiteltävä GNU-grep.
Kuten muillakin GNU-komennoilla, on myös grepillä useimmista optioista pitkät ja lyhyet muodot. Ne toimivat täsmälleen samalla tavalla. Kirjoittamisen vaivaa säästää käyttää lyhyitä muotoja, mutta toisaalta monimutkainen komento voi olla muillekin ymmärrettävä jos on käytetty pidempiä muotoja.
Optio | Pitkä muoto | Merkitys |
---|---|---|
Ohjeet ja versionumero | ||
-V | --version | Tulosta grep-komennon versionumero. |
--help | Tulosta grep-komennon ohje. | |
Osumakohdan konteksti | ||
-B N | --before-context=N | Tulosta N riviä ennen osumakohtaa. |
-A N | --after-context=N | Tulosta N riviä osumakohdan jälkeen. |
-C N | --context=N | Tulosta N riviä ennen ja jälkeen osumakohdan. Myös pelkkä -N, jossa N jokin luku, tekee saman asian. |
Osumakohdan sijainti | ||
-n | --line-number | Tulosta rivinumerot ennen osumarivejä. |
-b | --byte-offset | Tulosta ennen löydettyä riviä monennestako tiedoston merkistä rivi alkaa. |
Binäärimössön käsittely | ||
--binary-files=TYYPPI | Kertoo mitä tehdään jos tiedosto näyttää olevan binäärimössöä eikä tekstiä. (Oletus: tulosta "binary file XX matches".) | |
-a | --text | Älä tarkista onko kyse tekstitiedostosta: tulosta aina kahden rivinvaihtomerkin välinen osa, kun osuma löytyy. (Lyhempi tapa sanoa --binary-files=text.) |
-I | Jätä huomiotta binääritiedostot. (Lyhyempi tapa sanoa --binary-files=without-match.) | |
Haku alihakemistoista | ||
-d MITATEHDA | --directories=MITATEHDA | Kertoo mitä hakemistoille tehdään. (Oletus: "read", joka on käytännössä sama kuin "skip".) |
-r -R | --recurse | Käy läpi kaikki alihakemistojen tiedostot. (Lyhyempi tapa sanoa --directories=recurse.) |
Haun rajoittaminen tiedoston nimen perusteella | ||
--include=NIMIMALLI | Käy läpi vain ne tiedostot, joiden nimi sopii nimimalliin NIMIMALLI. | |
--exclude=NIMIMALLI | Käy läpi vain ne tiedostot, joiden nimi ei sovi nimimalliin NIMIMALLI. | |
--exclude-from=TIEDOSTO | Käy läpi vain ne alihakemistot, joiden nimi ei sovi mihinkään niistä nimimalleista jotka ovat tiedostossa TIEDOSTO. | |
Hahmon käsittely | ||
-v | --invert-match | Käänteinen haku, tulosta ne rivit joista hahmoa ei löydy. |
-m N | --max-count=N | Lopeta, kun N osumariviä on löydetty. Yhdessä --count -option kanssa tulostaa enintään luvun N. Yhdessä --invert-match -option kanssa tulostaa enintään N riviä joissa osumaa ei löydy. |
-w | --word-regexp | Hae vain rivit, joissa hahmo esiintyy yhtenä sanana, ei sanan osana. |
-x | --line-regexp | Hae vain rivit, jotka täsmäävät täysin hahmoon, ei rivejä joissa hahmo esiintyy osana. |
-i -y | --ignore-case | Pidä isoja ja pieniä kirjaimia samana. |
-F | --fixed-strings | Hahmo käsitellään kiinteänä merkkijonona, ts. hahmon kaikki erikoismerkit menettävät merkityksensä. |
-G | --basic-regexp | Käsitellään haettava hahmo ns. perussyntaksin mukaisesti. |
-E | --extended-regexp | Käsitellään haettava hahmo laajennetun syntaksin mukaisesti. |
-P | --perl-regexp | Käsitellään hahmo ilmaisuvoimaisempana perl-tyylin säännöllisenä lausekkeena. |
Muuta osuman tulostusta | ||
-q | --quiet --silent | Älä tulosta mitään, palauta vain paluuarvo sen mukaan löytyikö osuma vai ei. |
-l | --files-with-matches | Älä tulosta löytyneitä rivejä, vaan niiden tiedostojen nimet joista hahmo löytyi. |
-L | --files-without-match | Älä tulosta löytyneitä rivejä, vaan niiden tiedostojen nimet joista ei löytynyt haettavaa hahmoa. |
-c | --count | Älä tulosta löytyneitä rivejä, vaan niiden lukumäärä |
-o | --only-matching | Älä tulosta koko riviä vaan vain se osa, joka täsmää hahmoon. |
-H | --with-filename | Tulosta aina sen tiedoston nimi, josta osuma löytyi. (Oletuksena tulostetaan, jos haetaan useammasta kuin yhdestä tiedostosta kerralla.) |
-h | --no-filename | Älä koskaan tulosta niiden tiedostojen nimeä, joista osuma löytyy. (Oletuksena tulostetaan, jos haetaan useammasta kuin yhdestä tiedostosta kerralla.) |
--color=VARITETAANKO --colour=VARITETAANKO | Määrittelee väritetäänkö osumakohta aina (always), ei koskaan (never) vai silloin kun tulostus on ohjattu näytölle (auto). | |
DOS-rivinvaihtoihin liittyvää | ||
-U | --binary | Älä muuta rivinvaihtoja Unix-tyylisiksi ennen hakua, vaikka tiedosto näyttäisi sisältävän DOS-tyyppisiä rivinvaihtoja. |
-u | --unix-byte-offsets | Näytä --byte-offset -option tuloste niin, että ensin muutetaan rivinvaihdot DOS-tyylisistä Unix-tyylisiksi. |
Teknistä sälää | ||
--line-buffered | Tulosta aina rivi kerrallaan, älä puskuroi normaalisti. (Oletuksena puskuroidaan, jos syöte ohjautuu muualle kuin näytölle.) | |
--mmap | Käytä mmap-systeemikutsua eli mappaa tiedosto muistiin. Joskus nopeampi, mutta voi sekoilla tai kaatuakin jos tiedostoa muutetaan kesken greppauksen. | |
Sekalaiset | ||
-f TIEDOSTO | --file=TIEDOSTO | Hae sovitettavat hahmot tiedostosta. |
-s | --no-messages | Älä tulosta mitään virheilmoituksia puuttuvista tiedostoista tai lukuoikeuksien puuttumisesta. |
-D MITATEHDA | --devices=MITATEHDA | Kertoo mitä tehdään jos tiedosto ei olekaan tavanomainen tiedosto vaan laitetiedosto, nimetty putki tai soketti. |
-e HAKULAUSEKE | --regexp==HAKULAUSEKE | Määrittelee haettavan hahmon. Tarvitaan, jos haettava hahmo alkaa viivalla, tai jos haetaan kerralla useampaa hahmoa. |
--label=NIMI | Näytä kantasyötevirrasta tuleva syöte kuten se tulisi tiedostosta nimeltä NIMI. | |
-Z | --null | Tulosta tiedostonimen jälkeen nollatavu, eikä kaksoispistettä (kuten normaalisti) eikä rivinvaihtoa (kuten --files-with-matches ja --files-without-matches -optioiden kanssa normaalisti). |
Melkein kaikissa GNU-työkaluissa on optiot --help ja
--version. Jos kysyt miksi grep ei toimi, liitä mukaan
komennon grep --version
tuloste, se helpottaa vastaamista.
Ennen kysymystä olet tietysti tarkistanut josko grep --help
auttaisi.
Optioiden --after-context (-A), --before-context (-B), --context (-C)
ja --line-number (-n)
toiminta lienee itsestäänselvää. --byte-offset (-b) sen sijaan toimii hieman
yllättävästi: grep 'x' tiedosto.txt
ei kerro monesko
merkki 'x' on tiedostossa, vaan monesko merkki aloittaa sen rivin, jossa
'x' esiintyy. Lisäksi täytyy huomata, että rivien numerointi alkaa yhdestä,
mutta merkkien numerointi alkaa nollasta.
Ennen wanhaan unixin hakemisto oli sisäisesti
likimain tekstitiedosto, niin että
grep 'abc' hakemisto
teki suunnilleen saman asian kuin
ls hakemisto | grep 'abc'
. Muistona
tästä voi grepille antaa optiot --directories=read ja --directories=skip,
joista ensinmainittu on oletusarvo ja viimeksimainittu ohittaa hakemistot.
Koska Linuxissa (ja uudemmissa Unixeissa muutenkin) hakemisto on toteutettu
teknisesti toisin, toimii --directories=read todellisuudessa kuten
--directories=skip.
Mielenkiintoisempi on --directories==recurse eli lyhyemmin --recursive (-r).
Se käsittelee hakemistot rekursiivisesti, eli käytännössä
grep --recursive 'kissa' *
etsii
"kissa"-merkkijonoa kaikista työhakemiston alla olevista tiedostoista. Tämä
on joskus kätevää, mutta usein joutuu käyttämään find-xargs -komentoparia
monimutkaisempien hakujen toteuttamiseen. Huomaa, että esimerkiksi
grep --recursive 'class' *cpp
ei luultavasti tee
mitään: se etsii rekursiivisesti merkkijonoa "class" kaikista niistä
työhakemiston alihakemistoista, joiden nimi päättyy "cpp".
Lähinnä rekursiivisen haun kanssa käyttökelpoisia ovat --include ja
--exclude. Ensinmainittu käsittee vain ne tiedostot, joiden nimeen
annettu nimimalli sopii, ja toinen jättää näistä pois ne, joiden nimeen
sen hahmo sopii. "Nimimalli" on tässä suunnilleen shellin
käyttämä tapa kuvata tiedostonimiä, ei grepin säännöllisten lausekkeiden
mukainen kuvaus. Toiminta selviää parhaiten esimerkistä:
grep --recursive --include='*.h' --exclude='_*' class source
tutkii hakemiston "source" alla olevat tiedostot, joiden pääte on ".h" ja jotka
eivät ala alaviivalla, ja etsii niistä rivit joissa on sana "class".
Edellämainittuja optioita voi käyttää moneen kertaan. Jos usein joutuu kirjoittamaan monta samaa --exclude -optiota, voi käyttää --exclude-from=TIEDOSTO -optiota. Silloin jätetään tutkimatta ne tiedostot, joiden nimi täsmää tiedoston TIEDOSTO jollain rivillä olevaan nimimalliin.
--invert-match (-v) ja --line-regexp (-x) lienevät itsestäänselviä: ensimmäinen tulostaa ne rivit, joihin hahmo ei sovi, ja toinen vain rivit joihin hahmo sopii kokonaisuudessaan. Yhdessä ne tulostavat rivit, jotka kokonaisuudessaan eivät osu haettavaan hahmoon.
--word-regexp (-w) on periaatteessa selvä: haetaan vain kokonaista sanaa, johon hahmo sopii. Mikä sitten on sana? Sana koostuu yhdestä tai useammasta peräkkäisestä sanamerkistä, jota edeltää rivin alku tai muu kuin sanamerkki, ja jonka jälkeen on rivin loppu tai muu kuin sanamerkki. Sanamerkki taas on kirjain, numero tai alaviiva. Mikä sitten on kirjain, ja ovatko ää ja öö kirjaimia? Se riippuu locale-asetuksesta, katso tarkemmin kohdasta mikä on kirjain.
Myös --ignore-case (-i) on periaatteessa selvä: pidetään isoja ja pieniä kirjaimia samanarvoisena. Mistä kone sitten tietää, että 'a' ja 'A' ovat saman kirjaimen eri kirjasinkokoja? Sekin riippuu localesta, ks. kohta mikä on kirjain.
--max-count (-m) on myös ymmärrettävä: haetaan korkeintaan tietty määrä
osumarivejä. Myös yhdessä --only-matching -option kanssa se käsittelee
tietys määrän rivejä, ei osumia:
echo "kissassa" | grep --max-count=1 --only-matching ss
tulostaa kahteen kertaan "ss". --max-count jättää tiedosto-osoittimen
juuri osumarivin jälkeen, minkä vuoksi voit esimerkiksi etsiä ensimmäisen rivin
jossa on merkkijono "kissa" ja sen jälkeen tulevat rivit joissa on merkkijono
"koira":
(grep --max-count=1 'kissa' ; grep 'koira') < teksti.txt
(Tuon tarkempi ymmärtäminen edellyttää komentotulkin toiminnan tuntemusta.)
--fixed-strings (-F) käsittelee hahmon vain ja ainoastaan peräkkäisinä merkkeinä. Tämä on usein kätevää, jos haettava merkkijono on kiinteä, ja sisältää pisteen tai muita grepin käyttämiä erikoismerkkejä.
--basic-regexp (-G) ja --extended-regexp (-E) määrittävät käytetäänkö hahmojen esittämiseen perussyntaksia vai laajennettua syntaksia. --perl-regexp (-P) ottaa käyttöön hieman laajemman valikoiman keinoja koota säännöllisiä lausekkeita hahmoihin. Ne on selitetty alla kohdassa säännölliset lauseet.
Kaikkein yksinkertaisin tapa muuttaa tulostusta on --quiet (-q), joka
poistaa kaiken tulostuksen. Ideana on hyödyntää paluuarvoa: grep
palauttaa nollan jos yksikin osuma löytyi, ykkösen muuten (ja kakkosen,
jos tuli ongelmia). Tätä voi käyttää vaikka näin:
grep --quiet kissa teksti.txt && echo 'Kissa löytyi' || echo 'Ei kissaa'
Oletusarvoisesti grep tulostaa tiedoston nimen ennen osumaa, jos haetaan kerralla useammasta kuin yhdestä tiedostosta. Jos nimiä ei tarvita, voi käyttää optiota --no-filename (-h), ja jos päinvastoin halutaan aina tiedostonnimi mukaan, oikea optio on --with-filename (-H).
Jos halutaan tulostaa osumarivien määrä eikä itse rivejä, voi käyttää
--count (-c) -optiota. Se tulostaa nimenomaan rivien määrän, vaikka jollakin
rivillä olisi useampi osuma: kokeile esimerkiksi
echo "hei hei" | grep --count hei
--files-with-matches (-l) ja --files-without-match (-L) tekevät mitä voisi olettaa: tulostavat vain niiden tiedostojen nimet, joista löytyy yksikin osuma tai ei yhtäkään osumaa.
--only-matching (-o) tulostaa vain osuman,
ei osuman sisältävää riviä. Tästä on iloa, jos käyttettävä hahmo on
monimutkaisempi kuin pelkkä merkkijono. Jos osumia löytyy useampia,
tulostetaan jokainen omalle rivilleen. Esimerkki:
echo "abc acb adb" | grep -o -w "a.b"
Normaalisti grep ei tulosta osumaa värillisenä,
ja jos tämän haluaa varmistaa voi antaa option --color=never.
--color=always tulostaa osumakohdan värillisenä, mutta
yleensä halutaan värillinen tulostus vain näytölle, ei silloin kun
tulostus on ohjattu tiedostoon. Tämä onnistuu sanomalla --color=auto,
tai lyhyemmin pelkästään --color . Tätä voit testata näillä viidellä
komennolla:
echo takissani | grep --color=auto kissa
echo takissani | grep --color=always kissa > t1
echo takissani | grep --color=auto kissa > t2
cat t1
cat t2
Värien näyttäminen perustuu ANSI-koodeihin, eli tiettyihin merkkeihin
jotka saavat konsolin esimerkiksi vaihtamaan tulostusväriä. Värit toimivat
joillakin SSH-ohjelmilla ja toisilla eivät, ja saattavat joskus toimia
väärinkin. Voit kokeilla omaa ohjelmaasi komennolla
echo "abc"$'\x1b'"[44mdef"$'\x1b'"[0mghi"
joka tulostaa kirjaimet def sinisellä pohjalla.
--color -optio saa ennen osumakohtaa tulostumaan
GREP_COLOR-ympäristömuuttujan arvon ja m-kirjaimen, ja osuman jälkeen
ESC-merkin ja merkit "[0m". ESC-hakasulkuauki-nolla-m on merkintä joka
palauttaa tulostuksen normaaliksi, ja jos GREP_COLOR -ympäristömuuttujan
arvo on vaikkapa ESC ja "[44", on tuloksena sininen osumakohta. Tätä
voi kokeilla komennoilla
export GREP_COLOR=$'\x1b'"[44"
echo abcdefghi | grep --color=auto def
--regexp (-e) -optiolla annetaan haettava hahmo.
grep a b c
tulkitaan niin, että tiedostoista b ja c haetaan
hahmoa a. Komento grep -e a -e b c
taas hakee sekä hahmoa a että
hahmoa b tiedostosta c. Valitettavasti grepissä (versio 2.5.1) on bugi tässä,
joten
echo abc | grep --color -e a -e c
toimii mutta
echo abc | grep --color -e c -e a
ei toimi.
--mmap -optio ohjaa grepin käyttämään toista, joskus nopeampaa mutta ei aivan aina toimivaa, tapaa tiedoston lukemiseen. Älä käytä.
--no-messages -optio poistaa valitukset tiedostoista, joita ei saa avattua esimerkiksi puuttuvien oikeuksien vuoksi. Tämä on käyttökelpoinen lähinnä kun tiedostot annetaan jokerimerkeillä, ja mukaan tulee muita kuin omia tiedostoja.
grep --file=hahmot.dat juttu.txt
tulostaa tiedostosta juttu.txt ne rivit, jotka täsmäävät johonkin
tiedoston hahmot.dat rivillä olevaan hahmoon. Tätä tarvitaan ainakin
silloin, kun halutaan selvittää mitkä kahden tiedoston rivit löytyvät
molemmista tiedostoista ja mitkä vain toisesta. Komennot
grep --line-regexp --fixed-strings --file=eka toka
grep --line-regexp --fixed-strings --invert-match --file=eka toka
grep --line-regexp --fixed-strings --invert-match --file=toka eka
tulostavat molemmissa tiedostoissa olevat rivit, vain tiedostossa
toka ja vain tiedostossa eka olevat rivit.
Sekä grep-komennon argumenttina oleva tiedoston nimi että --file -option
arvo voi olla pelkkä viiva, jolloin syöte luetaan kantasyötevirrasta.
Tämän ansiosta voidaan tutkia vaikka ketkä tietystä käyttäjäjoukosta,
jonka käyttäjätunnukset on listattu tiedoston kayttajat.txt riveillä,
ovat parhaillaan kirjautuneena koneelle näin:
w -h | cut -f 1 -d ' ' | grep --file=kayttajat.txt -
tai ketkä eivät ole:
w -h | cut -f 1 -d ' ' | grep --invert-match --file=- kayttajat.txt
Grep arvaa tiedoston alun perusteella onko kyseessä tekstitiedosto vai ei-tekstiedosto. Jälkimmäisessä tapauksessa tulostetaan vain "Binary file XX matches" jos osuma löytyy. Tämä oletusarvo on järkevä, mutta joskus halutaan ohittaa binääritiedostot sanomalla --binary-files=without-match (sama lyhyesti: -I). Optiolla --binary-files=text (sama kuin --text tai -a) on käyttöä, jos tiedät tiedoston alun olevan binäärimössöä mutta lopusta löytyvän tekstiä. Esimerkiksi Microsoft Word -ohjelman .doc -tiedostot ovat yleensä tällaisia.
--devices
Unixissa "kaikki on tiedostoja", joten esimerkiksi normaalin korpun
saa kopioitua antamalla ensin komennon
cp /dev/fd0 /joku/polku/tiedosto
ja sitten levynvaihdon jälkeen
cp /joku/polku/tiedosto /dev/fd0
. Normaalisti grep ei
mitenkään erityisesti selvitä onko tutkittava tiedosto "normaali" vai
laitetiedosto (tai jokin muu unix-erikoisuus, kuten nimetty putki). Jos
haluaa, voi antaa option --devices=skip (-D skip), jolloin tällaiset erikoiset
tiedostot ohitetaan.
Unix-järjestelmissä, myös Linuxissa (ja mm. Amigoissa) on perinteisesti käytetty ascii -koodia 10 rivinvaihdon merkkinä, kun taas Macintoshien (ja mm. Commodore 64:n) valinta samaan tarkoitukseen on ollut numero 13. DOS- ja Windows-käyttöjärjestelmissä puhdas teksti taas ei sisällä rivinvaihtoja, vaan rivi-alas ja rivin-alkuun -merkit, joista ensinmainitun koodi on 10 ja viimeksimainitun 13. On kenties mahdollista, että asia olisi saatu vielä jollain tavalla sekavammaksi, mutta ainakaan tämän kirjoittajalle ei sellaista tapaa tule mieleen.
Jos grep ajetaan DOS- tai Windows-käyttöjärjestelmässä, se arvaa ensin tiedoston alun perusteella onko tutkittava tiedosto tekstimuotoista, ja jos on, niin muuttaa rivinvaihdot unix-muotoon ennen varsinaista käsittelyä. --binary (-U) poistaa tämän arvauksen käytöstä. Optiolla ei ole mitään vaikutusta, jos sitä käytetään Linuxissa tai muussa unix-tyyppisessä järjestelmässä.
Jos tiedostossa on merkit 'a', 'b', 'c', rivinvaihto ja 'x', on x-kirjain unixissa neljäs merkki ja DOSissa tai Windowsissa viides merkki. --unix-byte-offsets (-u) -optio muuttaa --byte-offset -option toimintaa niin, että osumakohta tulostetaan kuten rivinvaihdot olisivat olleet unix-tyyppisiä. --unix-byte-offset -optiolla on vaikutusta vain DOS- tai Windows-järjestelmissä.
Asiasta kiinnostuneet voivat lukea Jukka Korpelan kirjoituksen Rivinvaihdot ja kappaleet datan käsittelyssä.
On nopeampaa kirjoittaa tiedostoon iso lohko, esimerkiksi 16 kilotavua, kerrallaan. Grep selvittää parhaansa mukaan onko tulostus menossa näytölle vai ohjattu jonnekin, ja sen mukaan päättää puskuroiko tulostusta rivi vai isompi lohko kerrallaan. Eräissä erikoisissa tilanteissa tämä arvaus menee pieleen.
Komennolla tail -f /var/log/messages
näkee ylläpitäjä
tiedoston /var/log/messages lopun reaaliajassa, ts. kun lokiin kirjoittuu
uusi merkintä, tulee se heti näkyviin. Jos tästä halutaan poimia kiinnostavia
rivejä, voidaan sanoa vaikkapa
tail -f /var/log/messages | grep 'FAILED LOGIN'
, mikä
vielä toimii. Mutta jos otetaan toinen grep perään, esimerkiksi tutkitaan
vain Villen epäonnistuneita logineja sanomalla
tail -f /var/log/messages | grep 'FAILED LOGIN' | grep ville
ei homma enää toimi. Tähän auttaa --line-buffered, pitää siis sanoa
tail -f /var/log/messages | grep --line-buffered 'FAILED LOGIN' | grep ville
Voit kokeilla tätä kahden konsolin avulla. Tee ensin komennolla
touch esimerkki
tyhjä tiedosto. Kirjoita sitten
tail -f jokutiedosto | grep a | tr 'a-z' 'A-Z'
ja toisessa konsolissa sano echo kissa >> jokutiedosto
. Nyt
ensimmäisessä konsolissa ei tapahdu mitään. Sen sijaan lisäämällä
--line-buffered -option saat tulostuksen heti.
--null
: erottimeksi nollatavu eikä
rivinvaihtoTiedostojen nimissä voi olla myös välilyöntejä ja rivinvaihtoja.
Tällöin esimerkiksi
grep --files-with-matches 'moi' *
voi tulostaa "kirje liisalle.txt", ja komento
grep --files-with-matches 'moi' * | xargs cat
yrittää tulostaa ensin tiedoston "kirje" ja sitten tiedoston "liisalle.txt".
Ongelma korjautuu käskemällä ensiksi grep erottamaan tiedostonnimet
nollatavulla käyttäen --null (-z) -optiota, ja toiseksi laittamalla
xargs käyttämään nollatavua:
grep --files-with-matches --null 'moi' * | xargs -0 cat
--label
- täh???--label -option merkitys voi aiheuttaa hämmennystä henkilössä.
Miksi kukaan sanoisi "cat tiedosto | grep --label=tiedosto 'kissa'" kun riittää
sanoa "grep --with-filenames 'kissa' tiedosto"? Arvoituksen ratkaisu on
komentojonoissa: yksinkertaisin esimerkki on komentojono (olkoon sen
nimi vaikkapa zgrep) jonka ainoa rivi on
zcat $1 | grep --label=$1 $2
Tuota käytetään sanomalla "zgrep kissa tiedosto.gz", ja se toimii niin että
zcat-komento purkaa pakatun tiedoston ja tulostaa sen, ja grep poimii tästä
halutut rivit. Koska putkesta tuleva data pitää saada näyttämään siltä että
se tulee tiedostosta, tarvitaan --label -optiota.
Oikeasti zgrep-niminen komentojono on jo tehty valmiiksi, ja se on paljon ylläolevaa monimutkaisempi.
grep --before-context=3 --after-context=1 'kissa' teksti.txt
etsii tiedostosta teksti.txt rivit joissa on merkkijono "kissa", ja tulostaa
löytyneitä rivejä edeltävät kolme riviä sekä yhden seuraavan rivin.
grep -B 3 -A 1 'kissa' teksti.txt
tekee saman asian lyhyitä optioita käyttäen.
(grep --before-context=1 --max-count=1 'kissa' ; cat) < teksti.txt
tulostaa tiedoston alkaen merkkijonon "kissa" sisältävää riviä edeltävältä
riviltä loppuun saakka. (Ensin haetaan grep
illä yksi osuma,
sen jälkeen cat
tulostaa tiedoston loppuun saakka.)
grep --word-regexp --line-number 'kana' teksti.txt
tulostaa rivit, joissa on sana "kana" (eikä esim. sanaa "lakana"),
ja näyttää monenneltako riviltä sana löytyy.
grep --line-number '' teksti.txt
tulostaa koko tiedoston rivinumeroituna. (Tyhjä hahmo osuu mihin vain.)
grep --recurse --files-with-matches 'localhost' /etc
Etsii hakemiston /etc
alta tiedostot, joissa on merkkijono
"localhost".
grep --exclude='*~' --files-without-match 'a' tekstit/*
tutkii hakemiston "tekstit" kaikki tiedostot poislukien ~-merkkiin
loppuvat, ja tulostaa nimet niistä tiedostoista joissa ei ole
yhtäkään a-kirjainta.
grep --ignore-case --quiet 'kissa' teksti.txt && echo 'Kissa löytyi'
tulostaa "Kissa löytyi", jos tiedosto teksti.txt sisältää merkkijonon
"kissa" kirjainkoosta riippumatta (siis "kissa", "KISSA", "KiSsA" jne.
kelpaavat).
grep --only-matching 'a.a' teksti.txt
tulostaa jokaisen sellaisen kohdan, jossa kahden a-kirjaimen välissä on
mikä tahansa muu merkki (se saa olla myös a-kirjain, välilyönti tms.).
echo 'karamelli' | grep --color 'a.a'
tulostaa sanan "karamelli" niin että "ara" on väritetty punaiseksi.
grep -e '--binary' -- --mmap
etsii tiedostosta nimeltä "--mmap" merkkijonoa "--binary".
Säännölliset lausekkeet (engl. "regular expression", usein vain "regexp")
ovat tapa kuvata joitakin mahdollisesti äärettömän pitkiä merkkijonoja.
Esimerkiksi "ensin A-kirjain, sitten mielivaltainen määrä
BCD-kirjainkolmikkoja ja EF-kirjainpareja sekaisin ja lopuksi G-kirjain"
on eräs säännöllinen lauseke.
Se kuvattaisiin grep-komennon laajennetulla syntaksilla näin:
A((BCD)|(EF))*G
Syntaksi on epäolennainen: tärkeämpää kuin muistaa mikä merkki
tarkoittaa mitäkin, on ymmärtää mitä säännöllisillä lausekkeilla voi
tehdä ja mitä ei.
Kaikkia mekaanisia sääntöjä ei voi ilmaista säännöllisillä lauseilla: esimerkiksi "Ensin A-kirjaimia ja sitten B-kirjaimia miten monta vain, mutta kumpaakin kirjainta yhtä monta" ei ole kuvattavissa säännöllisenä lauseena.
Säännöllisten lauseiden ilmaisuvoima on helpointa ymmärtää katsomalla äärellisiä automaatteja. Äärellisellä automaatilla on tiloja ja niiden välillä siirtymiä. Ainakin yksi tila on alkutila ja ainakin yksi tila (mahdollisesti sama kuin alkutila) on lopputila. Jokaista siirtymää vastaa yksi merkki, ja automaatin hyväksymä kieli on sellainen merkkijono, joka muodostaa siirtymäpolun alkutilasta lopputilaan.
![]() |
Automaatti hyväksyy vain merkkijonon "ABC" |
![]() |
Automaatti hyväksyy kielen "A((BCD)|(EF))*G". |
Toinen tapa ymmärtää säännölliset lausekkeet on verrata niitä
laskutoimituksiin. Ala-asteen matematiikassa on lukuja ja niitä
käsittelevät yhteen-, vähennys-, kerto- ja jako-operaattorit.
Säännöllisessä lausekkeessa lukuja vastaavat atomit ja
operaattoreilla voidaan esittää peräkkäisyys, toisto tai vaihtoehtoisuus.
1*2+3*4
on lauseke, jossa plus-operaattori yhdistää kaksi
kerto-operaattorin atomeista tuottamaa tulosta;
(A|B)(C|D)
on lauseke jossa peräkkäisyys (jolle ei ole omaa
operaattoria) yhdistää kaksi vaihtoehto-operaattorin atomeista kokoamaa
lauseketta. Esimerkin säännöllinen lauseke tarkoittaa "ensin A tai B
ja sitten C tai D".
Yllä kerrottu --fixed-strings
-optio tarkoittaa, että
hahmo tulkitaan pelkäksi kiinteäksi merkkijonoksi. Helpompi tapa sanoa
sama asia on käyttää komentoa fgrep
. Eli kun
grep 'a.b'
tarkoittaa "tulosta rivit, joilla on a, mikä
tahansa merkki (pisteen erikoismerkitys) ja b", tarkoittaa
fgrep 'a.b'
nimenomaan "tulosta rivit, joilla on a, piste
ja b".
Joissakin grep-ohjelman versioissa on olemassa erikseen perushaku
ja ilmaisuvoimaisempi laajennettu haku, joista viimeksimainittu otetaan
käyttöön optiolla --extended-regexp
tai käyttämällä komentoa
egrep
. GNU-grepin kanssa näin ei ole, vaan siinä
egrep
(tai vastaava optio) ottaa käyttöön laajennetun
syntaksin. Näissä esimerkeissä käytetään nimenomaan laajennettua
syntaksia, perussyntaksissa merkkien '?', '+', '{', '(', ')' ja '|'
eteen pitäisi kirjoittaa '\'. (Esimerkki: rivit, joissa on joko
merkkijono "kissa" tai "koira" etsitään joko komennolla
grep 'kissa\|koira'
tai
egrep 'kissa|koira'
tai
grep --extended-regexp 'kissa|koira'
.)
Merkkien '*' ja '.' merkitys on sama perussyntaksissa ja laajennetussa
syntaksissa. Niinpä perussyntaksia käyttäen pitäisi muistaa kirjoittaa
esimerkiksi grep 'ab*c'
mutta toisaalta
grep 'ab\+c'
. Tämä on hankala muistaa.
Käytännössä kannattaa aina käyttää joko fgrep
-komentoa tai laajennettua syntaksia, eikä koskaan perussyntaksia.
Ilmaisuvoimaa voidaan laajentaa ottamalla käyttöön ns. perl-tyylin
säännölliset lausekkeet optiolla --perl-regexp
.
(Komento nimeltä pgrep
on olemassa, mutta se tarkoittaa
aivan muuta; pcregrep
taas ei tue kaikkia niitä optioita,
joita grep ja egrep tukevat). Tästä kerrotaan lisää alempana.
Merkki, jolla ei ole erikoismerkitystä, tarkoittaa säännöllisessä lauseessa itseään; eli "kissa" on säännöllinen lauseke joka osuu vain peräkkäisiin merkkeihin 'k', 'i', 's', 's' ja 'a'. Jos halutaan erikoismerkki mukaan hakulausekkeeseen, sitä pitää edeltää kenoviiva. Kaksi kenoviivaa peräkkäin tarkoittaa yhtä kirjaimellista kenoviivaa.
Toisto-operaattoreita on selkeyden vuoksi seitsemän:
A* | Nolla tai useampi A-kirjainta |
A+ | Yksi tai useampi A-kirjainta |
A? | Nolla tai yksi A-kirjainta |
A{N} | N A-kirjainta |
A{N,} | N tai useampi A-kirjain |
A{,M} | Enintään M A-kirjainta |
A{N,M} | N-M A-kirjainta |
Valinnan osalta riittää yksi merkki: "A|B" tarkoittaa joko A- tai B-kirjainta.
Merkkejä voi niputtaa sulkeilla, ja tällöin perässä oleva toisto-operaattori viittaa sulkeiden sisältöön: "(AB)+" tarkoittaa merkkijonoja "AB", "ABAB", "ABABAB" jne. Sulkeita saa olla sisäkkäin, esimerkiksi "((AB){1,2}(CD){1,2}){1,2}" kuvaa merkkijonoja "ABCD", "ABABCD", "ABCDCD", "ABABCDCD", "ABCDABCD", "ABCDABABCD", "ABCDABCDCD", "ABCDABABCDCD" "ABABCDABCD", "ABABCDABABCD", "ABABCDABCDCD", "ABABCDABABCDCD", "ABCDCDABCD", "ABCDCDABABCD", "ABCDCDABCDCD", "ABCDCDABABCDCD", "ABABCDCDABCD", "ABABCDCDABABCD", "ABABCDCDABCDCD" ja "ABABCDCDABABCDCD". Hahmo "((AB|C)*(D|E)+)*" sopii esimerkiksi merkkijonoon "ABDEEDABECABCCABE". (Testikysymys: pitääkö hahmoon sopivan merkkijonon aina loppua D- tai E-kirjaimeen?)
(A|B|C|D|E) on hankala kirjoittaa, ja se voidaan ilmaista lyhyemmin [ABCDE] tai vielä kätevämmin [A-E]. Jos haetaan merkkiä joka ei kuulu joukkoon, voidaan sanoa [^A-E]. Hakasulkumerkinnän tarkka merkitys riippuu käytettävistä kieliasetuksista, eli [A-Ö] voi tehdä mitä haluat tai sitten ei. Merkkivälejä hakasuluissa voidaan antaa enemmänkin, eli esim. [A-CK-N] tarkoittaa merkkejä A, B, C, K, L, M ja N.
Lopuksi on vielä joukko merkintöjä yleisille merkkiluokille.
[:lower:] | pieni kirjain |
[:upper:] | iso kirjain |
[:alpha:] | kirjain, iso tai pieni |
[:digit:] | numero |
[:alnum:] | kirjain tai numero |
[:blank:] | välilyönti ja tabulaattori |
[:graph:] | "näkyvä merkki", eli kirjain, numero tai välimerkki |
[:cntrl:] | kontrollikoodi |
[:print:] | tulostettavissa oleva eli kirjain, numero, välimerkki tai välilyönti |
[:punct:] | "välimerkki", oikeastaan näkyvä merkki joka ei ole kirjain eikä numero |
[:space:] | "tyhjä merkki", eli mm. välilyönti tai form feed |
[:xdigit:] | heksadesimaalinumero, siis numero tai kirjain a-f isona tai pienenä |
Hakasulut ovat osa merkintää, ja nämä toimivat ainoastaan hakasulkujen
sisällä osana (mahdollisesti ainoana osana) vaihtoehtoisia merkkejä,
mikä on lievästi sanottuna yllättävää. Esimerkiksi isoista kirjaimista
koostuvat merkkijonot etsitään komennolla
egrep --only-matching '[[:upper:]]+'
ja vain isoja kirjaimia,
numeroita ja pilkkuja sisältävät merkkijonot löytyvät näin:
egrep --only-matching '[[:upper:][:digit:],]+'
. Merkkijonot,
joissa ei ole isoja kirjaimia, löytyvät komennolla
egrep --only-matching '[^[:upper:]]+'
.
Toisten hakasulkujen unohtaminen tekee muuta kuin voisi kuvitella:
egrep '[:alpha:]'
etsii ne rivit, joissa on jokin merkeistä
kaksoispiste, 'a', 'l', 'p' tai 'h'. Eli älä unohda toisia hakasulkeita!
Rivin alkua kuvataan hattumerkillä '^' ja rivin loppua dollarimerkillä
'$'. Esimerkiksi grep -x kissa
vastaa samaa kuin
egrep '^kissa$'
.
Sanan alku kuvataan merkinnällä '\<' ja sanan loppu vastaavasti
merkinnällä '\>'. Esimerkiksi grep -w kissa
tekee saman kuin
egrep '\<kissa\>'
.
\b osuu ikäänkuin nollan merkin pituiseen merkkijonoon sanan rajalla,
ja \B taas kaikkeen muuhun paitsi sanan rajaan. \b voidaan aina korvata
merkinällä \< tai \>, mutta \B -merkintää ei helposti esitä toisin:
egrep 'a\B'
etsii rivit joissa on a-kirjain, joka ei lopeta sanaa.
(Mitä tekee egrep 'a[^\>]'
?)
Näitä ei suoraan voi kuvata äärellisellä automaatilla. Voi kuitenkin kuvitella, miten käsiteltävä teksti käydään ensin läpi ja merkitään mukaan rivien ja sanojen aloitus- ja lopetusmerkit.
Pisteen erikoismerkitys on "mitä tahansa", joten esimerkiksi 'a..a' etsii rivit joissa on kaksi a-kirjainta joiden välissä on kaksi merkkiä. Tämä löytää myös rivit joissa on neljä peräkkäistä a-kirjainta. (Miksi?)
\w vastaa kirjaimia ja numeroita, \W taas vastaa kaikkia muita merkkejä; eli ne ovat synonyymeja merkinnöille [[:alnum:]] ja [^[:alnum:]].
Ei-negatiivinen desimaaliluku on helppo esimerkki melko monimutkaisen lausekkeen koostamisesta pala kerrallaan. Oletetaan, että alku- ja loppunollia ei sallita, siis 1.23 on OK, mutta 01.23 ja 1.230 eivät ole. Tehdään ensin vaiheittain luonnollisen luvun tunnistava säännöllinen lauseke:
Sitten desimaaliosa:
Lopuksi pitää vain yhdistää osat, ja vielä muistaa että desimaaliosa
kokonaisuudessaan ei ole pakollinen. Lopputulos näyttää näin kauniilta:
'(0|([1-9][0-9]*))(\.[0-9]*[1-9])?'.
Tuota voi testata helpoiten kirjoittamalla joitakin sallittuja ja joitakin ei-sallittuja rivejä tiedostoon, ja käyttämällä optiota --line-regexp grepille. Huomaa, että rivistä "0034" kyllä löytyy osumia, eli merkit "0", toinen "0" ja merkkipari "34" osuvat lausekkeeseen.
Otetaan tehtäväksi etsiä merkkijono, joka on erotettu lainausmerkeillä. Vaikeutetaan tehtävää vielä sallimalla \" -merkintä merkkijonoon kuuluvan lainausmerkin merkkinä.
Ensimmäinen idea on yksinkertainen: '".*"'. Tuo hakee kaksi heittomerkkiä ja niiden välissä saa olla mitä tahansa merkkejä. Ei käy, sillä nyt 'Tässä on "eka" ja "toka" osuma' antaa tulokseksi '"eka" ja "toka"', koska "mitä tahansa" tarkoittaa myös heittomerkkiä. Pitää siis olla "mitä tahansa muuta paitsi heittomerkki": '"[^"]"'. Nyt toimii, ja osumat löytyvät.
Entä miten lisätään heittomerkin eskapointi? Yksinkertaista:
'"(([^"])|(\\.))*"'. Siis alussa on heittomerkki ja lopussa myös, välissä
taas on joko mitä tahansa merkkejä paitsi heittomerkkejä tai
kenoviiva-mikätahansa -pareja. Esimerkki on helpompi ymmärtää kun katsoo
viereisestä kuvasta vastaavaa äärellistä automaattia. Automaatilla on
eräänlainen perustila, johon se pääsee heittomerkillä ja josta se samalla
merkillä poistuu lopputilaan. Automaatti menee "seuraava on eskapoitu"
-tilaan kenoviivan syödessään, ja siitä se palaa perustilaan millä tahansa
merkillä.
Miten haetaan vastaavasti "<B>" ja "</B>" -tageilla erotettu teksti? Nyt automaatin pitää pysyä perustilassa aina kun se syö muun kuin <-merkin. <-merkin jälkeen joko palataan perustilaan, tai jos sattuu tulemaan vastaan kauttaviiva, niin siirrytään eteenpäin. Tästä edelleen palataan perustilaan, paitsi jos vastaan tuleekin B-merkki, ja seuraava tila tietenkin palauttaa perustilaan paitsi jos seuraava merkki on >-merkki. Hakulauseke on '<B>(([^<])|(<[^/])|(</[^B])|(</B[^>]))*</B>'. Tästä kannattaa piirtää automaatti, jotta ajatus selviää.
Edellä kuvattuihin sännöllisiin lausekkeisiin on olemassa laajennuksia, jotka lisäävät niiden ilmaisuvoimaa. Näitä ei voi enää kuvata äärellisten automaattien avulla.
Sulkeilla on paitsi ryhmittelytehtävä, myös "kaappausominaisuus",
jonka ansiosta niiden sisältöön voi myöhemmin viitata. Yksinkertaisimmillaan
egrep '(.)\1'
etsii kahta peräkkäistä merkkiä, ja
egrep '(.)(.)\2\1'
etsii neljän merkin "palindromeja".
Jokainen avaava sulku - siitä riippumatta onko sulkuja kenties sisäkkäin -
aloittaa uuden kaappauksen, ja ne numeroidaan ykkösestä yhdeksään.
Esimerkiksi
egrep '(([[:lower:]])\2\2) \1'
tulostaa rivit, joissa on kolme samaa pikkukirjainta, välilyönti ja samat
pikkukirjaimet. Saman hahmon voisi kirjoittaa myös '([[:lower:]])\1\1 \1\1\1'.
Miten haetaan vaikkapa mielivaltainen HTML-elementti, jonka
sisällä saa olla muita elementtejä? Näin onnistuu melkein:
egrep '<([^>]+)>.*</\1>'
Melkein siksi, että nyt taas tulee ongelmaksi kahden osuman yhdistyminen,
kun merkkijono josta haetaan on mallia
"a<TAGI>b</TAGI>c<TAGI>d</TAGI>e".
Tästä eteenpäin selitetään hahmoja, jotka vaativat
--perl-regexp
eli -P
-option toimiakseen.
Seuraajavaatimus tarkoittaa, että hahmoa pitää seurata jokin
toinen hahmo, mutta osumaksi valitaan vain ensimmäinen hahmo.
A-kirjain, jota pitää seurata B-kirjain, haetaan hahmolla
'A(?=B)'. Tällä on merkitystä vain, kun osuma väritetään --color
-optiolla tai tulostetaan vain osumakohta/kohdat --only-matching
-optiolla; muussa tapauksessa voi yhtä hyvin hakea hahmoa 'AB'.
Esimerkki selventänee:
echo 'ABBA' | grep --perl-regexp --only-matching 'A(?=B)'
echo 'ABBA' | grep --perl-regexp --only-matching 'AB'
.
Seuraajavaatimus voi olla mielivaltainen hahmo, esimerkiksi '[[:digit:]]{3,5}(?=AB+C)' etsii 3-5 peräkkäistä numeroa, joita seuraa A, ainakin yksi B ja C.
Seuraajakielto toimii juuri päinvastoin: se etsii hahmon jota ei
saa seurata jokin toinen hahmo. Esimerkiksi komennolla
grep --perl-regexp 'A(?!B)'
etsitään kaikki A-kirjaimet
joita ei seuraa B-kirjain. Myös seuraajakiellossa hahmo saa olla
mielivaltaisen monimutkainen. Mitä tekee
grep --perl-regexp 'A(?!B(?!C))'
?
Edeltäjävaatimus ja edeltäjäkielto toimivat muuten samalla tavoin mutta edeltävän hahmon on oltava määräpituinen. Hahmo '(?<=A)B' etsii B-kirjaimet joita edeltää A-kirjain, ja hahmo '(?<!A)B' etsii B-kirjaimet joita ei edellä A-kirjainta. Määräpituisuuden vaatimuksesta seuraa, että esimerkiksi '(?<=A{3}B{4})C' on sallittu hahmo, mutta '(?<=AB+C)D' ei ole.
Säännöllinen lauseke sovitetaan yleensä kahta sääntöä noudattaen:
ensiksi pyritään saamaan osuma alkamaan niin vasemmalta kuin mahdollista,
ja toiseksi pyritään saamaan niin pitkä osuma kuin mahdollista, ts.
ollaan ahneita ja yritetään saada syötettä sisään niin paljon kuin
mahdollista. Tämän näkee helposti sanomalla
echo 'BAAAAAAB' | egrep --only-matching 'A{2,3}'
jolloin tuloksena on kaksi kolmen A-kirjaimen riviä. Haku voidaan
muuttaa ahneesta kitsaaksi toisto-operaattorin perässä olevalla
kysymysmerkillä:
echo 'BAAAAAAB' | grep --perl-regexp --only-matching 'A{2,3}?'
Nyt haetaan mahdollisimman vähän, eli käytännössä kaksi A-kirjainta
kerrallaan.
Edellä huomattiin, että "<B>" ja "</B>" -tagien välisen tekstin haku
oli hankalaa, ja mielivaltaisen elementin tunnistus mahdotonta, jos samalla
rivillä oli useampia elementtejä. Tähän auttaa kitsas haku:
grep --perl-regexp --only-matching '<B>.*?</B>'
tulostaa nätisti molemmat B-elementit. Tätä voidaan vielä parantaa
edeltäjä- ja seuraajavaatimuksilla:
grep --perl-regexp --only-matching '(?<=<B>)(.+?)(?=</B>)'
(Ei toimi, jos etsitään merkkijonosta "<B></B> <B>sana</B>".
Miksi ei?)
Vastaavasti mielivaltaisen elementin tapauksessa toimii hahmo
'<([^>]+)>.*?</\1>', joka on yksinkertaisempi kuin seuraajakiellon
avulla toteutettu '<([^>]+)>(.(?!</\1>))*.</\1>'.
--ignore-case
toimii ongelmitta, mutta joskus halutaan
vain osa hahmosta tulkita kirjainkoko unohtaen. Tämä ei ole kovin vaikeaa:
grep --perl-regexp 'Velli(?i)kellon(?-i)soitto'
etsii rivit joissa on ensin merkkijono "Velli" täsmälleen noin eli isolla
alkukirjaimella, sitten "kellon" kirjasinkoon ollessa merkityksetön, ja
sitten "soitto" pienellä kirjoitettuna.
Yleisesti voidaan hakulausekkeen keskelle kirjoittaa muuntimia malliin '(?ab-cd)', jolloin ominaisuudet a ja b otetaan käyttöön ja siitä eteenpäin ja ominaisuudet c ja d taas poistetaan käytöstä.
Normaalisti grep käsittelee aina yhtä riviä kerrallaan, ts. sillä ei
voi esimerkiksi hakea merkkijonoa A-rivinvaihto-B, eikä etsiä tiedostoja
joissa on ensin A-kirjain ja sen jälkeen mahdollisesti eri rivillä B-kirjain.
Ensimmäisen ongelman ratkaisu on seuraava:
echo -e 'A\nB' | grep -P '(?s)A.B'
Muunnin s on lyhenne sanasta "single line", minkä nimityksen on ollut tarkoitus selittää, että tekstipätkä josta hahmoa haetaan tulkitaan yhdeksi riviksi. Käytännössä piste osuu tällöin myös rivinvaihtomerkkiin.
Toinen vastaava muunnin on m ("multiple line"), joka taas muuttaa
^- ja $-merkit osumaan vain koko tekstin alku- ja loppukohtaan yhden
rivin alku- ja loppukohtien sijaan. Eli
grep --perl-regexp '(?m)(^A)|(B)' teksti.txt
tulostaa jokaisen rivin, jossa on B-kirjain, sekä ensimmäisen rivin
jos se alkaa A-kirjaimella. (Tai palauttaisi, jos toimisi. Tässä
grep bugaa eikä toimi.)
Tietokone käsittelee numeroita. "Hae merkkijonoa ABC" kääntyy koneessa muotoon "hae peräkkäisiä numeroita 65, 66 ja 67". Numeroiden ja kirjainten - tai oikeastaan merkkien - välinen yhteys on tietenkin mielivaltainen, mutta kaikkien pitäisi olla samaa mieltä siitä mitä numerokoodausta käytetään. (Lisätietoa saat Jukka Korpelan kirjoituksesta Mikä on merkki)
Kauan sitten luotiin koodaus nimeltä ASCII, jolla englanninkielistä perustekstiä sai kirjoitettua. Ääkkösten ja muiden kielten erikoisempien kirjainten vuoksi ascii-koodausta laajennettiin, ja tuloksena oli suunnilleen suomenkielinen koodaus, tanskankielinen koodaus jne. Tämä oli ongelma ja on sitä usein edelleen.
Lisäksi tietokoneella halutaan usein saada sanoja aakkosjärjestykseen, joka sekin on kieliriippuvaista. Grepin kannalta tällä on merkitystä vain hakasulkumerkinnöissä, siis siinä miten [x-y] -tyyppiset merkinnät käsitellään.
Oikein asennetussa järjestelmässä pitäisi kielen määrittelyyn riittää
yksi komento, esimerkiksi Fedora Core 2:ssa
export LANG=finnish
Muissa järjestelmissä komento voi olla
export LANG=fi_FI
tai, jos komentotulkkisi on tcsh, vaikkapa
setenv LANG fi_FI
Sopivan locale-asetuksen löydät luultavasti komennolla
locale -a | fgrep 'fi'
Näet kielimäärittelyn eron esimerkiksi komennoilla
export LANG=C
echo 'abcdABCD' | egrep -o '[b-c]'
export LANG=finnish
echo 'abcdABCD' | egrep -o '[b-c]'
Lisätietoa locale-asetuksista tarjoaa Ari Mäkelän kirjoittama Finnish-HOWTO, joka nimestään huolimatta on suomenkielinen.
Kirjoittanut Jori Mäntysalo, email jori.mantysalo@uta.fi. Viimeksi päivitetty 2004-07-29.