offline
- Pridružio: 25 Maj 2005
- Poruke: 1482
- Gde živiš: Gracanica, Kosovo
|
Vi koji koristite *nix sisteme ste verovatno vec videli da postoji tzv. Linux recnik za Srpsko-Engleski (i obratno):
http://www.mycity.rs/Linux-Download/Srpski-Recnik-za-Linux.html
Ovde cemo probati da napisemo skript koji ce raditi prakticno isto - pronaci zeljenu rec i ispisati je na ekranu. Jedina razlika je sto cemo ovde koristiti awk umesto C jezika. Cilj je prikazivanje koriscenja awk jezika za pretragu fajlova i formatiranje izlaza.
Za pracenje je dovoljno osnovno poznavanje awk jezika, tako da se necemo mnogo osvrtati na upoznavanje sa samim jezikom.
Odmah na rad, pocecemo sa jednostavnim pretrazivanjem fajla:
awk '/greska/' reci.dat
(Ovo inace predstavlja 'awk idiom', tj. awk nacin za skracivanje komandi. Gornja komanda je isto sto i: awk '{if($0 ~ /greska/) print}')
Izlaz komande je:
$ awk '/greska/' reci.dat
En#Delinquency|Greska-Krivica-Zlocin-Propust-Pogreska-Prestup
En#Error|Greska-Pogreska-Zabluda-Neispravnost-Greh
En#Lapse|Prestajanje-Gasenje-Odstupanje-Pogreska-Zaostati-Otpasti-Nestati-Proticanje-Grska-Greh-Omaska-Propadanje-Propustanje
En#Mistake|Zabluda-Zabuniti Se-Propust-Prevariti Se-Pobrkati-Pogresiti-Greska-Nesporazum-Pogreska-Rdjavo Suditi-Zabuna
Sr#Pogreska|Error-Lapse-Delinquency-Mistake
Koristimo istu bazu reci kao i serbdict Linux recnik (obican tekstualni fajl), i format je sledeci:
Jezik Rec Granicnik Prevod reci
Eng# Error | Error-Lapse-Delinquency-Mistake
Prvo uocavamo da se prevod reci "Error" pojavljuje dva puta, jednom kao prevod reci sa engleskog, i drugi put u samom prevodu. U awk-u podrazumevani razdvajac izmedju polja je ' ', tj. prazno polje.
Izmenom podrazumevanog razdvajaca (FS - field separator), opcijom "-F" u komandnoj liniji ili menjanjem FS varijable u BEGIN bloku, vrsimo pretragu reci po prvom polju i postizemo sledece:
$ awk -F "|" '$1 ~ /greska/' reci.dat
Sr#Pogreska|Error-Lapse-Delinquency-Mistake
Sada ce awk traziti rec samo u prvom delu, a ne u prevodu. Ali hajde da malo "ulepsamo" izlaz komande i dodamo par korisnih informacija. Posto dodajemo vise komandi, zbog preglednosti stavljamo sve u jedan fajl, a kasnije pozivamo taj fajl koristeci "-f" opciju.
Iskoristicemo i mogucnost da se navede vise podrazumevanih razdvajaca i tako uklonimo Eng# i Sr# sa pocetka stringa.
BEGIN {
FS = "[#|]"
}
{
if($2 ~ /greska/) {
printf("%-48s %-50s\n", $2, $3)
}
}
U awk-u, BEGIN blok se izvrsava pre nego se ucita ijedan fajl. Postoji i END blok koji se izvrsava jednom kad se zavrsi sa obradom svih unosa i njegovo koriscenje cemo videti kasnije. Primeticete da sada pretrazujemo po drugom polju, to je sbog toga sto sada awk "lomi" svaki unos na 3 polja - to je zato sto smo ubacili dva podrazumevana razdvajaca. Awk vidi unos ovako:
razdvajac: FS FS
string: Eng#Error|Error-Lapse-Delinquency-Mistake
\_/ \___/ \___________________________/
polja: $1 $2 $3
Sa ovom awk skriptom imamo sledeci izlaz:
$ awk -f test.awk reci.dat
Pogreska Error-Lapse-Delinquency-Mistake
Sledece, recimo da zelimo da izbrojimo koliko prevoda za odabranu rec smo pronasli. Potrebne su nam samo manje izmene:
BEGIN {
FS = "[#|]"
}
{
if($2 ~ /Room/) {
printf("%-48s %-50s\n", $2, $3)
c++
}
}
END {
printf("\n\tNadjeno je: %d prevoda.\n", c)
}
$ awk -f test.awk reci.dat
Ante Room Predsoblje
Ante-Room Predsoblje
Bed-Room Spavaca Soba
... skracen izlaz zbog citljivosti ...
Smoking-Room Soba Za Pusace
Spare Room Gostinska Soba
State Room Svecana Prosto.
State-Room Svecana Prosto.
Strong Room Trezor
Waiting Room Cekaonica
Ward Room Biraliste
Nadjeno je: 40 prevoda.
Dalje mozemo malo ulepsati formatiranje dodavanjem linija izmedju prevoda, i ispisivanjem hedera na pocetku:
BEGIN {
FS = "[#|]"
printf "\n"
print "========================================================================================================="
printf("| %-48s | %-50s | \n", "Trazena rec", "Prevod reci")
print "========================================================================================================="
}
{
if($2 ~ /Room/) {
printf("| %-48s | %-50s |\n", $2, $3)
print "---------------------------------------------------------------------------------------------------------"
c++
}
}
END {
printf("\n\tNadjeno je: %d prevoda.\n", c)
}
$ awk -f test.awk reci.dat
=========================================================================================================
| Trazena rec | Prevod reci |
=========================================================================================================
| Ante Room | Predsoblje |
---------------------------------------------------------------------------------------------------------
| Ante-Room | Predsoblje |
---------------------------------------------------------------------------------------------------------
| Bed-Room | Spavaca Soba |
---------------------------------------------------------------------------------------------------------
... skracen izlaz zbog citljivosti ...
---------------------------------------------------------------------------------------------------------
| Waiting Room | Cekaonica |
---------------------------------------------------------------------------------------------------------
| Ward Room | Biraliste |
---------------------------------------------------------------------------------------------------------
Nadjeno je: 40 prevoda.
Sada dolazimo do malog problema, posto smo ceo kod prebacili u skriptu, svaki put kada zelimo da pronadjemo neku rec moramo da menjamo skriptu. Mozemo da awk skriptu "zavijemo" u shell skriptu, ili da probamo da menjamo argumente koje zadajemo awk skripti. Skoljke ionako ne volim da jedem pa idemo da drugu opciju. U pomoc nam priskacu awk ugradjene varijable ARGC i ARGV (onima koji poznaju recimo C ce ovo biti poznato). ARGC varijabla sadrzi broj argumenata sa kojima je awk pokrenut, a ARGV niz sadrzi argumente. ARGV[0] je uvek ime awk programa, ili ime skripte, u zavisnosti kako je skripta pokrenuta, i od toga na kom se sistemu koristi. Mali primer kako bi ovo bilo jasnije:
$ cat test.awk
BEGIN {
printf "Broj argumenata: %d\n\n", ARGC
for(i=0; i<ARGC; i++)
printf "ARGV[%d] => %s\n", i, ARGV[i]
}
$ awk -f test.awk reci.dat
Broj argumenata: 2
ARGV[0] => awk
ARGV[1] => reci.dat
$ awk -f test.awk reci.dat jedan dva tri
Broj argumenata: 5
ARGV[0] => awk
ARGV[1] => reci.dat
ARGV[2] => jedan
ARGV[3] => dva
ARGV[4] => tri
$ sed -i '1i\#!/usr/bin/awk -f' test.awk
$ chmod 0777 test.awk
$ ./test.awk
Broj argumenata: 1
ARGV[0] => gawk
$ ./test.awk jedan dva
Broj argumenata: 3
ARGV[0] => /usr/bin/awk
ARGV[1] => jedan
ARGV[2] => dva
Sa ovim znanjem, i pretpostavkom da je kokoska starija od jajeta, mozemo izmeniti awk skriptu tako da prvi argument bude trazena rec, i unecemo putanju do baze reci u samu skriptu. Iskoristicemo i BEGIN blok kako bi kreirali sve "crtice, prazna polja i zezancije" kako bi ucinili skriptu malo citljivijom, i odvojicemo prevode za reci sa Srpskog i Engleskog, kao i uvesti posebne brojace za oba.
BEGIN{
FS = "[#|]"
findMe = ARGV[1]
ARGC--
ARGV[ARGC++] = "./reci.dat"
}
{ if($2 ~ findMe) {
if($0 ~ /^Sr#/){
csr++
ser[$2] = $3
}else{
cen++
eng[$2] = $3
}
}
}
function PrintHeader(){
print "\n"
print "========================================================================================================="
printf("| %-48s | %-50s | \n", "Trazena rec", "Prevod reci")
print "========================================================================================================="
}
END{
if(!cen)
print "\n\tNema reci na engleskom."
else {
PrintHeader()
for(i in eng) {
gsub(/-/, ", ", eng[i])
printf("| %-48s | %-50s |\n", i, eng[i])
print "---------------------------------------------------------------------------------------------------------"
}
print "\n"
printf("\tNadjeno: %d prevoda sa engleskog.\n", cen)
}
if(!csr)
print "\n\tNema reci na srpskom."
else {
PrintHeader()
for(j in ser){
gsub(/-/, ", ", ser[j])
printf("| %-48s | %-50s |\n", j, ser[j])
print "---------------------------------------------------------------------------------------------------------"
}
print "\n"
printf("\tNadjeno: %d prevoda sa srpskog.\n", csr)
}
}
Hajde da proverimo sta smo postigli:
$ ./test.awk Kurs
Nema reci na engleskom.
=========================================================================================================
| Trazena rec | Prevod reci |
=========================================================================================================
| Vecernji Kursev | Night, School |
---------------------------------------------------------------------------------------------------------
| Kurs | Standard, Course, Quotation, Market, Policy, Route, Rate |
---------------------------------------------------------------------------------------------------------
| Obracunski Kurs | Parity |
---------------------------------------------------------------------------------------------------------
| Kurs (Broda) | Course |
---------------------------------------------------------------------------------------------------------
| Kursum | Bullet |
---------------------------------------------------------------------------------------------------------
Nadjeno: 5 prevoda sa srpskog.
U gornjoj skirpti mogli smo jednostavno napisati:
Umesto:
findMe = ARGV[1]
ARGC--
ARGV[ARGC++] = "./reci.dat"
Stavljamo:
findMe = ARGV[1]
ARGV[1] = "./reci.dat"
To smo jednostavno uradili (dobro, ja sam uradio) kako bi pokazali da su manipulazije, mahinacije i malverzacije sa ARGC i ARGV u BEGIN bloku dozvoljene.
Ako se sama skripta pokrene bez argumenata, tj. zadate reci, nista se nece desiti. Kako bi smo ovo sprecili na pocetku skripte dodajemo proveru dali postoji zadata rec, i onda nastavljamo. Za to ce nam posluziti ARGC varijabla, koja, secate se, sadrzi broj argumenata sa kojima je skripta pokrenuta (od koje je sam program/skripta jedan od njih, samim tim ARGC je uvek minimalno 1). Ako nema argumenata, ispisujemo "upozorenje", pokazujemo zuti karton i prekidamo skriptu. Linija koja nam je potrebna u BEGIN bloku je:
if(ARGC != 2){ print "Unesite trazenu rec"; exit }
Zasto bas u BEGIN bloku? Zato jer trebamo izvrsiti proveru pre bilo kakvog rada nad unosom.
Ako pokusamo da pokrenemo skriptu bez zadate reci, desava se sledece:
$ ./test.awk
Unesite trazenu rec!
Nema reci na engleskom.
Nema reci na srpskom.
WTF?! Zasto se skripta odmah ne prekida nego imamo poruku da nema trazenih reci u recniku?! Aha, END blok se izvrsava nakon obrade unosa, BEZ obzira na to dali je ista nadjeno! Zato nam se ispisuju dve printf() linije. Uvodimo jos jednu varijablu inspirativnog naziva, i nakon toga proveravamo vrednost te varijable u END bloku; ako je 0 nastavljamo, ako je 1 prekidamo skriptu odmah i sad.
Citaoci sa boljim vidom ce verovatno primetiti da ce odredjeni prevodi "strciti", kao u gornjem primeru sa rec "Kurs". To jos i nije problem, prevod za rec "Account" ima 252 karaktera ... Problem mozemo resiti na dva nacina;
jedan je da jednostavno ne obracamo paznju na tu anomaliju,
a drugi je da napisemo funkciju koja ce meriti duzinu prevoda, i ako je duzina veca od 50 karaktera onda prelamamo prevod i ispisujemo je u novom redu. Iz nepoznatih razloga odlucujemo se za drugu opciju:
function ckL(word){
if(length(word)<=50)
return word substr(spaces, length(word))
else{
wordPart = substr(word,51)
return substr(word,1,50)" | \n| " spaces "| " ckL(wordPart)
}
}
Znaci, ako je duzina stringa manja od, ili tacno 50 karaktera, vracamo string i tacno koliko nam je praznih polja potrebno (oduzimamo duzinu stringa od praznih polja).
Ako je string duzi od 50 karaktera, odsecamo prvih 50 karaktera i cuvamo ostatak u varijabli wordPart. Onda vracamo prvih 50 karaktera stringa, novu liniju, prazna polja i rekurzivno proveravamo ostatak stringa.
Sta jos mozemo uciniti kako bi skripta bila malo citljivija? Ove crtice su simpa na izlazu, ali u skripti izgledaju bezMeze. Oki, probacemo da iskoristimo sprintf() kako bi smanjili broj potrebnih karaktera, i uz pomoc gsub() promenimo prazna polja u potrebne znakove:
spaces = sprintf("%48s", "");
dashes = dashes2 = sprintf("%105s", "")
gsub(/ /, "-", dashes); gsub(/ /, "=", dashes2)
Ako sada dodamo proveru verijable kako bi smo onemogucili stampanje iz END bloka, i funkciju za proveru duzine prevoda, dobijamo sledecu skriptu u konacnom obliku (ah, napokon!):
#!/bin/awk -f
BEGIN{
if(ARGC != 2){ print "\nUnesite trazenu rec!\n"; qrac=1; exit }
FS = "[#|]"
spaces = sprintf("%49s", "");
dashes = dashes2 = sprintf("%105s", "")
gsub(/ /, "-", dashes); gsub(/ /, "=", dashes2)
findMe = ARGV[1]
ARGV[1] = "./reci.dat"
}
{ if($2 ~ findMe) {
if($0 ~ /^Sr#/){
csr++
ser[$2] = $3
}else{
cen++
eng[$2] = $3
}
}
}
function PrintHeader(){
print "\n"
print dashes2
printf("| %-48s | %-50s | \n", "Trazena rec", "Prevod reci")
print dashes2
}
function ckL(word){
if(length(word)<=50)
return word substr(spaces, length(word))
else{
wordPart = substr(word,51)
return substr(word,1,50)" | \n| " spaces "| " ckL(wordPart)
}
}
END{
if(!qrac){
if(!cen)
print "\n\tNema reci na engleskom."
else {
PrintHeader()
for(i in eng) {
gsub(/-/, ", ", eng[i])
printf("| %-48s | %-48s |\n", i, ckL(eng[i]))
print dashes
}
print "\n"
printf("\tNadjeno: %d prevoda sa engleskog.\n", cen)
}
if(!csr)
print "\n\tNema reci na srpskom."
else {
PrintHeader()
for(j in ser){
gsub(/-/, ", ", ser[j])
printf("| %-48s | %-48s |\n", j, ckL(ser[j]))
print dashes
}
print "\n"
printf("\tNadjeno: %d prevoda sa srpskog.\n", csr)
}
} print "\n"
}
I primer za primer:
$ ./test.awk Slucaj
Nema reci na engleskom.
=========================================================================================================
| Trazena rec | Prevod reci |
=========================================================================================================
| U Svakom Slucaju | Anyhow |
---------------------------------------------------------------------------------------------------------
| Srecan Slucaj | Squeak |
---------------------------------------------------------------------------------------------------------
| U Slucaju Da | In The Event, Provided That |
---------------------------------------------------------------------------------------------------------
| Slucaj | Hap, Accident, Occurence, Precendent, Hit, Inciden |
| | t, Occasion, Occurrence, Emergency, Random, Contin |
| | gency, Circumstance, Event, Chance, Case, Accidenc |
| | e, Casualty, Haphazard, Incidence, Luck, Precedent |
---------------------------------------------------------------------------------------------------------
| Slucajnost | Randomness, Contingency, Fluke, Chance |
---------------------------------------------------------------------------------------------------------
| Smrtni Slucaj | Death |
---------------------------------------------------------------------------------------------------------
| Nesrecan Slucaj | Misadventure, Casualty, Mishap |
---------------------------------------------------------------------------------------------------------
| Hitan Slucaj | Emergency |
---------------------------------------------------------------------------------------------------------
| U Slucaju Ako | Provided |
---------------------------------------------------------------------------------------------------------
| Slucajan | Casual, Promiscuous, Accidental, Odd, Occasional, |
| | Haphazard, Incident, Incidental, Accessory, Advent |
| | itious, Fortuitous, Fortutous, Random, Scratch, Pa |
| | tchy, Chance |
---------------------------------------------------------------------------------------------------------
| Nesretan Slucaj | Accident |
---------------------------------------------------------------------------------------------------------
| Slucajno | Perchance, Occasionally, Randomly, Haphazard |
---------------------------------------------------------------------------------------------------------
| Slucajno Cuti | Overhear |
---------------------------------------------------------------------------------------------------------
| Cudan Slucaj | Oddity |
---------------------------------------------------------------------------------------------------------
Nadjeno: 14 prevoda sa srpskog.
Kao sto vidite, u prevodu se moze desiti da se rec "prelomi" na sledeci red, a i reci pretrazujemo sa prvim velikim slovom (zato sto su tako unesene u tekstualni fajl). Drugi problem je trivijalno resiti, a i prvi se moze izmozgati.
Ali, to ostavljamo kao domaci zadatak za one koji su hrabri i avanturistickog duha.
Uzdravlje
|