Monet Unix-komennot tarvitsevat yleistä merkkijonovertailua, keinoa kysyä ''kuuluuko merkkijono x joukkoon X?'' Tähän tarkoitukseen käytetään merkkijonomalleja, erikoismerkkejä sisältäviä merkkijonoja, jotka vastaavat tiettyjen sääntöjen mukaisesti joukkoa merkkijonoja. Yksi yleinen tällainen on regular expression, formaalien kielten teoriasta lainattu termi jolle ei ole oikein hyvää suomennosta.
Historiallisista syistä eri komennot käyttävät erilaisia ja yhteensopimattomia merkkijonomalleja:
Jotkin komennot käyttävät vielä edellisistä poikkeavia malleja, yleensä edellisiä jotenkin rajoitettuina, esim. tr. Huomionarvoinen on myös case-lauseen syntaksiin kuuluva |, joka olennaisesti laajentaa sen ymmärtämää GP:tä, vaikka muodollisesti onkin eri asia.
Lisäksi monet epästandardit komennot käyttävät omia versioitaan, lähinnä erilaisia laajennuksia edellisiin. Erityisesti ksh tuntee omat laajennuksensa GP:hen () ja Gnu-versiot standardikomennoista tuntevat erinäisiä regexp-laajennuksia, samoin perl.
Komento grep ja käsite regular expression liittyvät kiinteästi yhteen, sen nimikin on lyhenne ed-editorin syntaksista: Global/RegExp/Print. Sen syntaksi on seuraavanlainen:
grep [-E|-F] [-c|-l|-q] [-insvx] [[-e] pattern_list...] [-f pattern_file...] [file...]
ja se etsii tiedostosta annettua mallia (-eja) vastaavia rivejä. Oletuksena malli (pattern) on basic regular expression (BRE), mistä enemmän alla, ja (vain) mallia vastaavat rivit (rivit joilla esiintyy mallia vastaava merkkijono) tulostetaan.
Optiot:
Optioita -e ja -f voi antaa useita. Jos kumpaakaan ei ole annettu, ensimmäinen ei-optio argumentti tulkitaan malliksi. (Siten -e on tarpeen vain jos malleja halutaan antaa useita tai malli alkaa viivalla (vrt. --).) Kussakin tapauksessa useita malleja voidaan antaa erottamalla ne rivinvaihdolla (grep on tiukasti rivipohjainen, rivinvaihtoja sisältäviä malleja ei voi käyttää).
Jos tiedostoargumentteja on useita, grep tulostaa tiedostonimet kustakin tiedostosta löytyvien rivien (tai löydettyjen rivien lukumäärän optiolla -c) alkuun kaksoispisteellä erotettuna (optiolla -l aina vain tiedostonimet).
Exit status on 0 jos ainakin yksi mallia vastaava rivi löytyi, 1 jos ei, > 1 jos tapahtui virhe (paitsi optiolla -q nolla myös virheen tapahtuessa jos rivi löytyi).
Komennosta on historiallisesti myös versiot egrep ja fgrep, jotka toimivat kuten grep -E ja grep -F.
BRE:t tulkitaan seuraavien sääntöjen mukaisesti:
\{m[,n]\} tarkoittaa edeltävän merkin, osalausekkeen tai osalausekeviittauksen toistamista m-n kertaa (\{m,\} vähintään m, \{m\} tasan m).
Erikoismerkit tulkitaan seuraavan prioriteettijärjestyksen mukaisesti:
Useasta mahdollisesta osamerkkijonosta BRE vastaa ensimmäistä (vasemmanpuoleisinta), jos niitä on useita pisintä niistä. Tällä ei ole merkitystä grepin kanssa mutta esim. sedin ja exprin kanssa kylläkin.
Kenoviivan vaikutusta muiden kuin edellämainittujen merkkien edellä ei ole määritelty, ja se vaihtelee järjestelmästä toiseen.
Huom. Rivinvaihdolla ei ole mitään erityisasemaa RE:ssä sinänsä, vaikka monet komennot moisen rajoituksen asettavatkin (mutta esim. sed ei).
Kaiken mitä voi tehdä GP:llä voi tehdä BRE:lläkin, mutta ei päinvastoin. Seuraavassa taulukossa on esimerkkejä niiden eroista:
GP | BRE |
? | . |
* | .* |
a* | a.* |
a.* | a\..* tai a[.].* |
*[!0-9]* | .*[^ 0-9].* |
^ ?^ | \^ .^ |
\? | ? |
\*a | *a |
[0-9][0-9][0-9][0-9] | [0-9]\{4\} |
Seuraavassa taulukossa on BRE:llä ja ksh:n GP:llä esimerkkejä malleista joita ei perus-GP:llä voi tehdä:
BRE | ksh GP |
[-+]\{0,1}[0-9][0-9]* | ?([-+])+([0-9]) |
[A-Za-z][A-Za-z0-9_]* | [A-Za-z]*([A-Za-z0-9_]) |
[0-9]\{1,3\} | [0-9]?([0-9])?([0-9]) |
\([[:alpha:]]\)[[:blank:].,]\1 | (ei onnistu) |
(ei onnistu) | ?(Kalle|Pekka|Tapani) |
Viimemainittu ei siis onnistu BRE:llä lainkaan, seuraavassa esiteltävällä ERE:llä kylläkin. Myös case-lauseella se onnistuu helposti.
ERE:t tulkitaan muuten samoin kuin BRE:t paitsi:
Erikoismerkkien prioriteetti on soveltuvin osin sama:
BRE | ERE |
[-+]\{0,1\}[0-9][0-9]* | [-+]?[0-9]+ |
[-+]\{0,1\}[0-9]\{1,10\} | [-+]?[0-9]{1,10} |
\(/[a-zA-Z0-9]\{1,14\}\)\{1,\} | (/[a-zA-Z0-9]{1,14})+ |
\([[:alpha:]]\)[[:blank:].,]\1 | (ei onnistu) |
(ei onnistu) | Kalle|Pekka|Tapani |
Siis joitakin asioita voi tehdä vain BRE:llä ja joitakin vain ERE:llä. (Tosin esim. Gnu grep kyllä tuntee osalausekeviittauksen myös ERE:n kanssa.) Teoriassa ksh:n GP on yhtä ilmaisuvoimainen kuin ERE, käytännössä usein paljon hankalampi (erityisesti toisto-operaattorin puutteen vuoksi).
Toisinaan käytettävä komento ratkaisee millaista merkkijonomallia on käytettävä, mutta grep tuntee kaksi erilaista ja usein saman asian voi tehdä monella eri komennolla jotka käyttävät erilaisia malleja. Seuraavassa esimerkkejä valinnan avuksi.
case "$name" in *.c) ... ;; esac if [[ $name = *.c ]] # ksh then ... if expr "$name" : '.*\.c$' >/dev/null then ... if printf "%s\n" "$name" | grep -q '\.c$' then ... if printf "%s\n" "$name" | grep -q '[.]c$' then ...
case "$sana" in [!A-Z]*|?*[!a-z]*) echo BAD ;; *) echo OK ;; esac if [[ $sana = [!A-Z]* || $sana = ?*[!a-z]* ]] then echo BAD else echo OK fi if expr "$sana" : '[A-Z][a-z]*$' >&- then echo OK else echo BAD fi if printf "%s\n" "$sana" | grep -q '^[A-Z][a-z]*$' then echo OK else echo BAD fi if printf "%s\n" "$sana" | grep -qx '[A-Z][a-z]*' then echo OK else echo BAD fi
grep '^A' "$file" while read line do case "$line" in A*) printf "%s\n" "$line" ;; esac done <"$file"
grep -v '^$' "$file" grep . "$file"
grep 'Kalle Pekka Jussi' "$file" grep -e Kalle -e Pekka -e Jussi "$file" grep -E 'Kalle|Pekka|Jussi' "$file" while read line ;do case "$line" in Pekka|Kalle|Jussi) printf "%s\n" "$line" ;; esac done <"$file"
grep -e '*\*\.\[a]' -e 'a\[2\*3]+4' "$file" grep -F -e '**.?[a]' -e 'a[2*3]+4' "$file"
grep '[-+*/]' "$file" grep -F -e + -e - -e '*' -e / "$file"Huomaa merkkien järjestys hakasuluissa!
dirname=${name%/*} dirname=$(expr "$name" : '\(.*\)/[^/]*')
x=${mjono#?} x="${x%${x#?}}" x=$(expr "$mjono" : '.\(.\)') x=$(printf "%s\n" "$mjono" | cut -b2)
case "$nimi" in [kK][aA][lL][lL][eE]) echo "kalle se on" ;; esac [[ "$nimi" = [kK][aA][lL][lL][eE] ]] && echo "kalle se on" typeset -l tmp="$nimi" [[ "$tmp" = kalle ]] && echo "kalle se on" expr "$nimi" : "[kK][aA][lL][lL][eE]$" && echo "kalle se on" printf "%s\n" "$sana" | grep -iq '^kalle$' && echo "kalle se on"
case "$luku" in [!-+0-9]*|?*[!0-9]*|'') echo "not a number" ;; *) echo "good number!" ;; esac case "${luku#[-+]}" in *[!0-9]*|'') echo "not a number" ;; *) echo "good number!" ;; esac if expr "$luku" '[-+]\{0,1\}[0-9][0-9]*$' >&- then echo "good number!" else echo "not a number" fi if printf "%s\n" "$luku" | grep -q '^[-+]\{0,1\}[0-9][0-9]*$' then echo "good number!" else echo "not a number" fi if printf "%s\n" "$luku" | grep -qx '[-+]\{0,1\}[0-9]\{1,\}' then echo "good number!" else echo "not a number" fi if printf "%s\n" "$luku" | grep -Eqx '[-+]?[0-9]+' then echo "good number!" else echo "not a number" fi
expr "$luku" :\ '[-+]\{0,1\}[0-9]{\1,}.\{0,1}[0-9]*\([Ee][-+]\{0,1\}[0-9]\{1,\}\)\{0,1\}$' && echo "looks OK floating point number to me" printf "%s\n" "$luku" | grep -qix '[-+]\{0,1\}[0-9]{\1,}.\{0,1}[0-9]*\(e[-+]\{0,1\}[0-9]\{1,\}\)\{0,1\}' && echo "looks OK floating point number to me" printf "%s\n" "$luku" | grep -Eqix '[-+]?[0-9]+.?[0-9]*(e[-+]?[0-9]+)?' && echo "looks OK floating point number to me"
grep -i 'agoraphobia agorafobia sesquipedalophobia sesquipedalofobia seskipedalophobia seskipedalofobia' "$file" grep -Ei 'agora(ph|f)obia ses(qu|k)ipedalo(ph|f)obia' "$file" grep -Ei '(agora|ses(qu|k)ipedalo)(ph|f)obia' "$file"
grep -i '[^[:alpha:]]ei[^[:alpha:]] ^ei[^[:alpha:]] [^[:alpha:]]ei$ ^ei$' "$file" grep -iE '(^|[^[:alpha:]])ei([^[:alpha:]]|$)' "$file"
wc -l *.tex | while read lines file do if [ $lines -ge 250 ] printf "%s\n" "$lines $file" done wc -l *.tex | while read lines file do case "$lines" in [3-9][0-9][0-9]|2[5-9][0-9]|[0-9][0-9][0-9][0-9]*) printf "%s\n" "$lines $file" ;; esac done wc -l *.tex | grep -E '^ *([3-9][0-9]{2}|2[5-9][0-9]|[0-9]{4,}) ' wc -l *.tex | grep -E '[3-9][0-9]{2}|2[5-9][0-9]|[0-9]{4,}'Viimeinen vaihtoehto listaa myös tiedostot joiden nimessä on numero > 250, mutta kelpaa interaktiivisesti tai jos muuten tiedetään ettei sellaisia ole.
grep -x '\([a-zA-Z][a-zA-Z0-9_]*\)=\(["'\'']\)\1\2' "$file" while read line ;do expr "$line" : '\(\([a-zA-Z][a-zA-Z0-9_]*\)=\(["'\''\)\2\3\)$' done <"$file"
find . -type f | grep '\(\..*\)\1$' find . -type f | grep '\(\.[^.][^.]*\)\1$'Jälkimmäinen jättää pois tyhjät tarkentimet (kahteen pisteeseen päättyvät nimet).
FNAME=$(expr "$FNAME" : "\(.*\(\..*\)\)\2")
cut -d: -f3 /etc/group >groups cut -d: -f4 /etc/passwd | grep -vx -f groups cut -d: -f4 /etc/passwd | grep -vx "$(cut -d: -f3 /etc/group)"
Gnu-projektin komennot tuntevat joukon laajennuksia BRE/ERE-syntaksiin:
\ < , \ > , \b ja \B vastaavat nollamittaista merkkijonoa ao. paikassa, ts. toimivat ankkureina ^ :n ja $:n tapaan. Lisäksi osalausekeviittaukset toimivat myös ERE:ssä. (Siis Gnu ERE ja BRE ovat toiminnallisesti identtisiä, ainoa ero on syntaksissa.)
Gnu grep tuntee myös joukon epästandardeja optioita ja pitkiä synonyymejä standardioptioille. Niihin ei tässä puututa sen enempää.
Seuraava: 7. sed
Edellinen: 5.44-72 Standardikomennot N-Z