GNU/Linux - 3. deo – Napredne komande
Uvod
Temeljno poznavanje osnovnih komandi je važan preduslov za efikasno obavljanje poslova koji su vezani za razvoj softvera ili projektovanje web sajtova u nekom od UNIX-olikih okruženja, međutim, u praksi je više nego preporučljivo biti upoznat i sa izvesnim brojem naprednijih programa za obradu teksta u konzoli (bar nekoliko), * preko kojih se rezultat izvršavanja osnovnih komandi može izmeniti, filtrirati ili formatirati na prikladniji način.
Pored napredni(ji)h komandi za obradu teksta, administracija sistema takođe obuhvata korišćenje shell skripti (bar povremeno). **
Pod naprednom obradom teksta (da pojasnimo), ovoga puta ne podrazumevamo upotrebu konzolnih editora kao što su Vim ili Emacs, već mislimo na rad sa tekstom u okviru komandne linije, to jest, na upotrebu programa kao što su grep
, sed
i awk
, koji po potrebi primaju tekst sa standardnog ulaza, obavljaju obradu i šalju tekst na standardni izlaz.
Još nekoliko uvodnih napomena ....
Pošto polako zalazimo u oblast naprednijih UNIX komandi, želimo da što pravilnije "naštimamo" očekivanja čitalaca, i stoga ćemo se ukratko osvrnuti na određene pojave ....
Može se reći da oblast UNIX/Linux komandi predstavlja svojevrstan "okean" (pojavu koju odlikuje: i velika širina, i velika dubina), i stoga, sam termin "napredne komande" koji smo izabrali za naslov članka (odnosno, preciznije - "iskazani nivo naprednosti"), može biti protumačen na više načina.
Sa jedne strane, u odnosu na određene komande, tj. programe (koje nismo pominjali do sada, a "kakvih inače ima na Linux-u"), komande koje ćemo prikazati u ovom članku ne deluju 'posebno napredno'.
Sa druge strane - same po sebi - prilično su napredne!
U članku neće biti prikazane sve opcije svih programa (ni iz daleka, pogotovo kada je u pitanju program awk
), ali, bez obzira na navedeno, opcije koje ćemo prikazati, same po sebi omogućavaju prilično napredne vidove obrade teksta (uz mnoštvo zanimljivih kombinacija).
Najprostije bismo mogli reći da će u članku biti prikazan "osnovni nivo naprednijih komandi" (pri čemu je akcenat na prikazivanju ideja koje stoje iza samih alata i praktičnim primerima, a ne - "na nabrajanju opcija").
Tehnike koje ćemo prikazati u nastavku, u praksi su sasvim dovoljne (uz bar ponešto samostalnog istraživanja i 'nadogradnje'), za korisnike koji ne žele previše da se udubljuju u tematiku konzolnih komandi pod Linux-om, već samo žele što bolju osnovu za korišćenje drugih programa (recimo da se navedeno odnosi na dobar deo programera), ali - takođe se može reći i da je gradivo iz članka sasvim dobra osnova za čitaoce koji žele da istražuju nadalje i što više se udube u tematiku.
U budućnosti, sasvim lako nam može pasti na pamet da napišemo i članak koji obuhvata mnogo više opcija i primera koji su vezani za programe grep
, sed
i awk
- a sada je vreme da se vratimo na posao ....
Grep - pretraga teksta preko regularnih izraza
Program grep
(skraćenica za "globally search for a regular expression and print matching lines"), pronalazi linije teksta koje se poklapaju sa određenim obrascem.
Tekst koji se pretražuje može biti izlaz prethodne komande (koji se "pajpuje" u grep
), a mogu se takođe direktno pretraživati i tekstualne datoteke (u tom slučaju, naziv datoteke je jedan od argumenata).
Komanda grep
poziva se po sledećoj šemi:
Sa prosleđivanjem (tj. "pajpovanjem") izlaza određene komande u program grep
, već smo se upoznali u uvodnom članku ....
.... a ovoga puta više pažnje ćemo obratiti na primere pretrage teksta koji se učitava direktno iz datoteka.
Pretraga teksta iz datoteka
Datoteka koju ćemo koristiti za primere u članku sadrži (fiktivne) podatke o osobama koje učestvuju u održavanju velike društvene mreže (po id-ovima se da naslutiti da mreža ima više stotina hiljada korisnika, ali, koristimo sažetu listu koja je dobijena filtracijom veće tabele u kojoj su zapisani svi korisnici, uključujući i osoblje sajta).
Za početak, pozabavićemo se jednostavnom pretragom, bez regularnih izraza.
Ukoliko upotrebimo jednostavan obrazac ....
.... pretraga u prvom slučaju daje rezultat koji je korektan (i očekivan):
Međutim, ako pokušamo - po istom principu - da pronađemo osobu (ili osobe), pri čemu se kao kriterijum koristi 'godina rođenja':
.... rezultat neće biti u skladu sa očekivanjima (jer, "moglo bi se reći" - da samo tražimo običnu nisku koja se može protumačiti na više načina):
U jednom redu, uneta niska (zaista) predstavlja godinu rođenja, dok u drugom redu niska predstavlja (samo) deo id-a saradnika.
U konkretnom primeru, problem se može rešiti na trivijalan način, budući da su podaci u datumima rođenja saradnika razdvojeni crticama (i stoga bi pretraga niske "1981-"
sasvim urodila plodom),
Međutim, zarad opšteg obrazovanja (to jest, zbog toga što u mnogim drugim situacijama nije toliko jednostavno pronaći "priručno rešenje"), "pravićemo se" da ne vidimo jednostavni obrazac i sprovešćemo pretragu preko regularnog izraza.
U slučaju da se u drugim kolonama ne pojavljuju datumi (niti drugi podaci koji po svom formatu 'liče' na datume), regularni izraz, odnosno, cela komanda pretrage - koja prepoznaje datume koji pripadaju 1981. godini - može se definisati na sledeći način:
Zapažamo argument -E
(--extended-regexp) - preko koga se uključuje upotreba regularnih izraza, kao i 'dvostruku' pojavu argumenta \b
(boundary) - preko koga se u regularnim izrazima prepoznaju granice reči (znakovi kao što su razmaci, tabovi, crte i drugi specijalni znaci koji ograničavaju pojedinačne reči).
Vidimo i to da se određene klase znakova (u programu grep
), označavaju drugačije u odnosu na sintaksu koju smo prikazali u uvodnom članku o regularnim izrazima.
Prethodno definisana pretraga sada vraća jedinog kandidata koji je (zapravo) rođen navedene godine.
Ako se datumi pojavljuju u više kolona, situacija više nije jednostavna i potrebno je koristiti program koji je u stanju da pristupa pojedinačnim poljima u okviru reda, tako da budemo sigurni da se pretražuju podaci iz (npr) četvrte kolone, a ne podaci iz sedme kolone i sl (isto naravno važi i za pretrage drugih formata koji se pojavljuju u više kolona).
Za zadatke kakve smo naveli (i druge komplikovanije zadatke), tipično se koristi program awk
(videćemo u nastavku više praktičnih primera).
Dakle, ozbiljniji zahvati praktično zahtevaju upotrebu kompleksnijih programa (u članku ćemo koristiti program awk
kome ćemo uskoro posvetiti više pažnje), međutim - kako to obično biva - upotreba moćnijih programa podrazumeva i upotrebu kompleksnije sintakse, i stoga vredi zadržati se na upotrebi programa grep
za zadatke "manje i srednje" težine.
Da pojasnimo preko dodatnog primera ....
U prvom primeru, tražili smo redove sa podacima o administratorima sajta, a ako bi bilo potrebno pronaći: administratore (nivo privilegija 30) i moderatore (nivo privilegija 20), i takav zadatak bi se mogao obaviti korišćenjem programa grep
- pod uslovom da pažljivo postupimo po pitanju toga koji podatak ćemo koristiti kao kriterijum za pretragu.
Sledeća pretraga:
.... vraća željeni rezultat, ali zato "idejno slična" pretraga, nalik na sledeću pretragu:
.... vraća vrlo diskutabilne rezultate, jer nimalo (!) nije teško zamisliti da niska "20" ili "30" može biti dan rođenja, deo niske id
, ili deo nekog drugog podatka.
Međutim, postoje i drugi detalji o kojima se mora voditi računa.
Recimo, ako postoji šansa da se među email adresama pronađu podniske "admin" i "moderator" (npr. ukoliko postoje korisnici sa email adresama: superadmin2000@gmail.com
i veselimoderator@gmail.com
i sl) - stvari se komplikuju.
U konkretnom primeru, gotovo je sigurno da se problem može rešiti uz upotrebu argumenta \b
, ali, kao što smo već nagovestili, u (drugim) kritičnim situacijama, prepoznavanje sadržaja pojedinačnih polja (tj. kolona), u redovima, najsigurnije je obaviti preko programa awk
.
Videćemo kasnije kako preko programa awk
možemo rešiti zadatak pronalaženja korisnika čiji je nivo privilegija veći ili jednak 20, ali, pre nego što dođemo do toga, razmotrićemo i nekoliko dodatnih opcija vezanih za program grep
.
Numeracija redova
Uz argument -n
(--line-number), komanda grep
prikazuje i redne brojeve linija iz datoteke (na kojima dolazi do poklapanja):
Inverzija pretrage
Uz argument -v
(--invert-match), pretraga vraća sve redove koji ne sadrže (pod)niske koje odgovaraju navedenom obrascu pretrage:
Prikaz prethodnih redova
Uz argument -B
(--before-context), posle čega sledi brojčana vrednost (na primer 2
), rezultat sadrži - pored pronađenih redova - takođe i navedeni broj redova koji prethode pronađenim redovima.
Prikaz sledećih redova
Uz argument -A
(--after-context), posle čega sledi brojčana vrednost (na primer 2
), rezultat sadrži - pored pronađenih redova - takođe i navedeni broj redova koji slede posle pronađenih redova.
Naravno, prethodne dve opcije moguće je i kombinovati:
Pretraga teksta se tipično obavlja preko programa grep
, ali se zato zamena teksta tipično obavlja preko programa sed
(ili awk
, ukoliko postoji potreba za dodatnim vidovima obrade).
Sed - zamena teksta
Program sed
(Stream Editor), omogućava obavljanje raznovrsnih operacija nad ulaznim niskama, ali, u praksi se tipično koristi za izmenu delova ulaznog teksta.
Zamena teksta preko programa sed
izvršava se po sledećoj šemi:
Komande se zadaju unutar niske, a argument "operacija" označava jednu od mogućih internih komandi za obradu teksta.
U praksi, ako se komanda pozove na sledeći način (pri čemu je interna komanda zadata preko argumenta s
i označava zamenu ("s" - skaćeno od "substitute")) ....
.... svaka pojava niske "saradnik"
biće zamenjena niskom "operator"
, pri čemu će rezultat biti prikazan u terminalu.
Kada kažemo "u terminalu", na posredan način smo nagovestili da pozivi komandi grep
i sed
ne podrazumevaju trajnu izmenu sadržaja (ulazne) datoteke!
Za trajno čuvanje - iako same komande nude mogućnost operisanja direktno nad sadržajem datoteka (preko dodatnih opcija) - najpraktičnije je koristiti redirekciju:
Kad smo već kod praktičnosti, naveli smo da se za komplikovanije primere obrade najčešće koristi program awk
, ali, svakako želimo da zainteresujemo čitaoce da dodatno istražuju i program sed
, i stoga ćemo prikazati još koji primer.
Recimo, ukoliko je potrebno ograničiti dejstvo komande sed
na određeni raspon redova, može se koristiti opcija -n
:
Ako pokrenemo poslednju komandu, zaglavlje se (praktično) preskače, što znači da će pretraga i zamena početi od 3. reda (odnosno: u obzir će biti uzeti samo redovi koji sadrže slogove sa podacima).
U prethodnom primeru, znali smo da datoteka sadrži 10 redova, međutim, ponekad nećemo 'znati unapred' i, u takvim situacijama, komanda se može dodatno uopštiti uz navođenje znaka $
, koji predstavlja oznaku za poslednji red:
Zamena pronađenih obrazaca drugim niskama, jeste tipična namena programa sed
, međutim, program se takođe može koristiti i za uklanjanje redova koji odgovaraju pronađenom obrascu (što se postiže preko interne komande d
('delete')),:
AWK - napredna manipulacija tekstom
Dva prethodno navedena programa (uz korišćenje tehnika pajpovanja i redirekcije), znatno proširuju osnovne mogućnosti programa sa kojima smo se upoznali u prethodnim člancima i veoma dobro dođu u velikom broju situacija, ali, rekli bismo da je "zvezda večeri" kada je u pitanju precizna i sveobuhvatna obrada teksta u Linux terminalu - program AWK
.
U najpraktičnijem smislu, program awk
je zapravo svojevrstan interpretator za specijalizovani programski jezik (sa C-olikom sintaksom, posebnim promenljivama, kontrolnim strukturama i ugrađenim funkcijama), i u pitanju je programski jezik koji je specifično namenjen obradi teksta.
Za početak, može se reći da izvršavanje programa awk
funkcioniše po sledećem obrascu:
.... ali, princip funkcionisanja lakše je razumeti uz primere (a opšta šema koju smo prethodno prikazali, uvek može biti nešto na šta ćete se vraćati povremeno, zarad boljeg razumevanja različitih konkretnih primera).
U prvom primeru, razmotrićemo situaciju u kojoj je potrebno izdvojiti samo one redove koji sadrže slogove sa saradnicima (i dalje koristimo datoteku saradnici.txt
), a prvo što nam može pasti na pamet, jeste to da se problem može rešiti upotrebom regularnih izraza.
Ako se filtracija redova obavlja preko regularnih izraza (u primeru koji razmatramo), biće pretraživani redovi koji počinju cifrom:
.... a blok sa izvršnim komandama podrazumeva ispis tri specifične kolone.
Komanda koju smo videli, pruža priliku da se detaljnije pozabavimo strukturom instrukcija koje su argumenti programa awk
: *
- u prvom delu naredbe ("obrazac"), pojavljuje se regularni izraz
^[0-9]
, preko koga se prepoznaju linije koje počinju cifrom - i to su linije koje će program obrađivati (drugim rečima - linije koje ne odgovaraju obrascu - biće zanemarene) - izvršne komande, oivičene vitičastim zagradama
{ .... }
, obuhvataju (u primeru koji razmatramo), formatirani ispis nekoliko proizvoljno izabranih kolona preko komandeprintf
(u pitanju je C-olika sintaksa, što naravno ne čudi, budući da je programawk
razvijen u istoj "softverskoj kuhinji" kao i programski jezik C) - u (internoj) komandi
printf
, kao argumenti se koriste specijalne promenljive:$2
,$3
i$5
, koje odgovaraju poljima (tj. "kolonama") u redu koji se trenutno obrađuje, i praktično su u pitanju: ime ($2
) i prezime saradnika ($3
), kao i funkcija koju saradnik obavlja ($5
) - kao ulaz, koristi se ista datoteka koju smo i do sada koristili u primerima
Program uzima u obzir jednu po jednu liniju i, ukoliko nije naveden uslov (tj. obrazac), u obradi će biti korišćeni svi redovi.
U gornjem primeru, kao što smo već naveli - postoji uslov (obrazac pretrage koji određuje da se na izlaz šalju (samo) redovi koji počinju cifrom, što u praktičnom smislu znači: redovi sa podacima o saradnicima).
U svakom slučaju, kada linija dospe na obradu, ceo slog (tj. pojedinačni red ulaznog teksta), deli se preko niske koja je definisana kao separator (podrazumevani separatori su razmaci i tabovi, ali, mogu se definisati i proizvoljne niske), a u daljoj obradi može se pristupati:
- celom redu, preko promenljive
$0
- pojedinačnim poljima (tj. 'kolonama'), preko promenljivih
$1
,$2
,$3
....$n
(gden
predstavlja indeks poslednje kolone)
.... pri čemu je definisana i promenljiva NF
(Number of Fields), koja odgovara ukupnom broju polja (tj. kolona) u trenutnom redu.
Broj redova, zabeležen je u promenljivoj NR
(Number of Records), koja se može koristiti i kao deo obrasca za proveru, koji prethodi izvršnim komandama za formatiranje.
Pošto smo sagledali dodatne opcije, jasno je da se slogovi sa podacima saradnika mogu izdvojiti mnogo lakše preko jednostavnog uslova NR>2
("redni broj sloga veći od 2" (prva dva reda u tabeli predstavljaju formatirano zaglavlje)):
.... i rezultat je skoro isti kao u prvom pozivu.
Kažemo "skoro", jer biće ispisan i poslednji red koji je prazan, što, u praksi, verovatno (?!) ne predstavlja problem.
Program awk
ne biva zakočen time što se pozivaju argumenti kao što su (na primer) $1
, $4
, ili $17
koji ne postoje u praznom redu, ali, ukoliko se izlaz šalje u drugi program - u kome bi pojava nepravilno formatiranog ulaza mogla dovesti do zastoja - potrebno je da budemo sasvim precizni.
U smislu 'povećanja preciznosti', lako se može urediti da se u rezultatu pojave samo redovi koji sadrže tekst (na primer, preko uslova koji proverava da li je ukupan broj polja u trenutnom redu (promenljiva NF
), veći od 0): *
Još je bolje ako se napiše uslov koji doslovno proverava da li red sadrži tačno onoliko kolona koliko je predviđeno (u našem slučaju 7):
U daljoj obradi, preko prethodno izdvojenih podataka, lako se može uobličiti (na primer), SQL upit za unos u bazu podataka:
"Da sve bude još bolje", izlaz se lako može pajpovati u program preko koga se standardni ulaz prosleđuje u tekstualni clipboard operativnog sistema:
Prikazaćemo prethodnu komandu i u celosti (iako je ponešto "glomazna"):
Za kraj početnog upoznavanja sa komandom awk
, * vratićemo se na primer pronalaženja korisnika iz tabele, čiji je nivo privilegija veći ili jednak 20.
Budući da program awk
omogućava precizan pristup poljima u okviru reda (a pri tom raspolaže i strukturama za kontrolu toka programa), lako se može proveriti da li podatak u 4. koloni zadovoljava uslov koji je naveden u prethodnom pasusu:
.... pri čemu se (očekivano) dobija sledeći rezultat:
Razmotrićemo još i dve jednostavne komande za formatiranje izlaza.
Sort - uređivanje redova
Program sort
uređuje redove ulaznog teksta shodno zadatom kriterijumu (uređivanje podrazumeva premeštanje redova jednih ispod drugih, a kriterijum je sadržaj nekog od polja).
Ako komanda awk
....
.... vrati sledeći izlaz:
.... rezultat se može dodatno sortirati preko komande sort
(argument -k 3
označava da se kao kriterijum za sortiranje koristi treća kolona, što je praktično datum rođenja):
Tabela sada ima sledeći oblik:
Skoro smo gotovi i ostaje samo da povratimo tabelarni prikaz.
Column - tabelarno uređivanje unetog teksta
Program column
može se koristiti za uspostavljanje (ili 'povratak') tabelarne strukture teksta (pri čemu je implicirano da je ulazni tekst formatiran tako da svaki red sadrži isti broj kolona, i tako da se u svakoj koloni nalazi podatak istog tipa).
Pri pokretanju sledeće komande ....
.... dobija se sledeći rezultat:
Sledeći koraci ....
Posle čitanja članka, rekli bismo da čitaocima predstoji poveći (ali veoma zabavan, zanimljiv i pre svega koristan), posao otkrivanja dodatnih opcija komandi koje su do sada nabrojane, kao i otkrivanje drugih programa za obradu teksta.
Pored navedenog, možete se oprobati i u kreiranju sopstvenih programa za obradu teksta (za početak, Python ili Node.js će poslužiti više nego dobro pri realizaciji takvih programa, budući da se bilo koja Python ili Node.js skripta lako može osposobiti da čita standardni ulaz).
U sledećem članku, bavićemo se pisanjem shell skripti za automatizaciju procesa (a upoznaćemo se i sa nekoliko programa o kojima do sada nismo pisali) ....