aneb Unix pro (m´ırnˇ e) programuj´ıc´ı lingvisty
Ondˇrej Bojar
bojar@ufal.mff.cuni.cz 25. leden 2006
Pˇrehled
• Motivace a metodika
• V´yhody ˇr´adkov´eho rozhran´ı (bash)
• Unix je n´astroj na pr´aci s textov´ymi soubory (textutils)
• Make pro lepˇs´ı pˇrehled procesech v´yroby soubor˚u
• CVS pro lepˇs´ı pˇrehled o historii i lepˇs´ı poˇr´adek v souˇcanosti
Motivace a metodika
Proˇc se uˇcit nˇeco nov´eho:
• automat ˇsetˇr´ı ˇcas (naprogramuju jednou, pouˇziju mockr´at)
• program je dobr´a dokumentace (m´am rovnou peˇclivˇe zaps´ano, jak jsem pokus prov´adˇel)
Jak se uˇcit Unix:
• po kapk´ach, prax´ı, najednou je toho nezvl´adnutelnˇe moc
• postupn´ym obohacov´an´ım reperto´aru zn´am´ych pˇr´ıkaz˚u a pˇrep´ınaˇc˚u
V´ yhody ˇr´ adkov´ eho rozhran´ı (bash)
• pˇr´ıkazy mohou b´yt pˇresnˇejˇs´ı a pestˇrejˇs´ı neˇz pˇr´ıkazy ud´ılen´e myˇs´ı
• snadno srozumiteln´e pro poˇc´ıtaˇc ⇒ snadno strojovˇe zpracovateln´e – pohodlnˇe reprodukovateln´e pouh´ym copy-paste
– pohodlnˇe zobecniteln´e (nahraˇd konkr´etn´ı jm´ena promˇenn´ymi)
• pln´a s´ıla programovac´ıch jazyk˚u (vˇetven´ı, cykly)
• pohodln´e propojov´an´ı program˚u, navazuj´ıc´ı v´ypoˇcty bash = Bourne-Again Shell
⇒ man bash
Z´ akladem Unixu je roura
cat defined-vallex-frames.txt
absolvovat 0 0 1 phase verb absolvovat-1 ACTobl(#nom) PATobl(#aku) absolvovat 0 0 2 - absolvovat-2 ACTobl(#nom) PATobl(#aku)
akceptovat 0 0 1 - akceptovat-1 ACTobl(#nom) PATobl(#aku,ze)
. . .
cat defined-vallex-frames.txt | head -2
absolvovat 0 0 1 phase verb absolvovat-1 ACTobl(#nom) PATobl(#aku) absolvovat 0 0 2 - absolvovat-2 ACTobl(#nom) PATobl(#aku)
cat defined-vallex-frames.txt | cut -f 1 absolvovat
absolvovat akceptovat
Jeˇstˇ e roura a jeˇstˇ e roura a pˇresmˇ erov´ an´ı
cat defined-vallex-frames.txt | cut -f 1 | sort | uniq -c 2 absolvovat
1 akceptovat 1 analyzovat
Pˇresmˇerov´an´ı na standardn´ı vstup programu a do souboru:
cat vstup | head a cat vstup > head Pˇresmˇerov´an´ı obecnˇe:
program < standardn´ı-vstup > standardn´ı-v´ystup 2> chybov´y-v´ystup program < standardn´ı-vstup >> prodlouˇzen´ı-existuj´ıc´ıho-souboru
program1 2> &1 | program2-dostane-na-vstup-spojen´y-std-a-chyb-v´ystup program1 `program2-v´ystup-vygeneruje-pˇr´ımo-na-pˇr´ıkazovou-ˇr´adku`
N´ aˇs prvn´ı skript
• Z uˇziteˇcn´eho ´useku roury si udˇelej skript:
echo ”sort | uniq -c” > suc
• Nastav mu pˇr´ıznak spustitelnosti:
chmod +x suc
• Um´ısti skript do sv´eho (novˇe vyroben´eho) osobn´ıho adres´aˇre skript˚u:
mkdir -p ˜/bin; mv suc ˜/bin
• Rekni bashi, aˇt programy (skripty) hled´aˇ pˇrednostnˇe v tom osobn´ım adres´aˇri:
export PATH=˜/bin:$PATH
cat defined-vallex-frames.txt | cut -f 1 | suc | head -2 2 absolvovat
1 akceptovat
Promˇ enn´ e v bashi a v prostˇred´ı
• Nastavit promˇennou pouze v bashi:
pozdrav=ahoj
• Nastavit promˇennou v bashi a prostˇred´ı pro vˇsechny d´ale spuˇstˇen´e programy:
export pozdrav=ahoj
Otestujme to:
echo ’echo ”Zdravim: $pozdrav”’ > test-pozdravu cat test-pozdravu
echo "Zdravim: $pozdrav"
sh test-pozdravu Zdravim:
pozdrav=ahoj; sh test-pozdravu Zdravim:
export pozdrav=nazdar; sh test-pozdravu Zdravim: nazdar
Souhrn oper´ ator˚ u pro ˇrazen´ı program˚ u v bashi
pgm1 | pgm2 v´ystup pgm1 bude pˇred´an jako vstup do pgm2
pgm1 ; pgm2 po ukonˇcen´ı pgm1 bude spuˇstˇen (nez´avisle) pgm2, obˇema lze pˇrismˇerovat vstup
m´a stejn´y v´yznam jako nov´y ˇr´adek ve skriptu
pgm1 && pgm2 pokud spuˇstˇen´ı pgm1 skonˇc´ı ´uspˇechem, bude spuˇstˇen i pgm2 pgm1 || pgm2 pokud pgm1 skonˇc´ı ne´uspˇechem, bude spuˇstˇen pgm2
Kulat´e z´avorky znamenaj´ı: spusˇt sekvenci v podshellu.
(cat vstup1.txt ; zcat vstup2.txt.gz) | sem-poteˇcou-data-z-obou-soubor˚u
Unix je n´ astroj pro pr´ aci s texty
• Pˇrehled z´akladn´ıch uˇziteˇcn´ych program˚u:
⇒ info coreutil
cat tac nl wc v´ypis, obr´acen´y v´ypis, ˇc´ıslov´an´ı a poˇc´ıt´an´ı ˇr´adek less prohl´ıˇzen´ı dlouh´eho v´ypisu: cat dlouhy | less
cut paste join vyˇrez´av´an´ı sloupc˚u a spojov´an´ı soubor˚u po sloupc´ıch tee rozvˇetven´ı roury: cat vstup | tee stranou | . . . grep v´ybˇer zaj´ımav´ych ˇr´adek ze vstupu
sed tr awk jednoduˇsˇs´ı i sloˇzitˇejˇs´ı ´upravy text˚u
• Nˇekter´e uˇziteˇcnosti ale chyb´ı:
⇒ http://www.cuni.cz/~obo/textutils
⇒ ls /home/bojar/tools/{vimtext,shell}/
• A nezapomeˇnte si vyr´abˇet vaˇse vlastn´ı!
Make: recept, co z ˇ ceho vznik´ a
⇒ info make
• Make je p˚uvodem n´astroj pro program´atory: z jak´ych zdrojov´ych soubor˚u kompilovat pomoc´ı jak´ych kompil´ator˚u jak´e programy.
• Make pro lingvisty poslouˇz´ı stejnˇe: z jak´ych dat jak´ymi postupy generovat jak´e pˇrehledy, souhrny ˇci jin´a data.
Pravidla zapisujte do souboru Makefile. Struktura pravidla pro make:
cilovy-soubor: zdrojovy-soubor1 zdrojovy-soubor2
prikaz1 < zdrojovy-soubor1 > /tmp/pomocny-soubor
prikaz2 zdrojovy-soubor2 < /tmp/pomocny-soubor < cilovy-soubor
↑ tabul´ator, nikoli mezery!
Kaˇzd´y ˇr´adek spuˇstˇen v nov´em shellu ⇒ nelze pˇredat promˇenn´e, ani pˇres prostˇred´ı, pokud ˇr´adky nespoj´ım.
Pˇr´ıklad: Makefile pro poˇ c´ıt´ an´ı frekvenc´ı
Takto vypad´a pˇripraven´y Makefile cat Makefile
entries.freq: defined-vallex-frames.txt cat $< | suc > $@
Takto jej pouˇzijeme – nech´ame vyrobit v´ystupn´ı soubor a prohl´edneme si ho:
make entries.freq head -2 entries.freq
2 absolvovat 1 akceptovat Po zmˇenˇe vstup˚u:
touch defined-vallex-frames.txt
pˇr´ıkaz make entries.freq pˇregeneruje i v´ystupy, bez zmˇen by nedˇelal nic.
Promˇ enn´ e v pravidlech
Pravidlo pracuj´ıc´ı pro v´ıce r˚uzn´ych vstup˚u:
entries-version-%.freq: defined-vallex-%-frames.txt cat $< | suc > $@
make entries-version-1.0.freq zpracuje defined-vallex-1.0-frames.txt make entries-version-1.5.freq zpracuje defined-vallex-1.5-frames.txt
Promˇenn´a V´yznam
$< zastupuje n´azev prvn´ıho ze vstupn´ıch soubor˚u
$^ zastupuje n´azvy vˇsech vstupn´ıch soubor˚u
$@ zastupuje n´azev v´ystupn´ıho souboru
$* zastupuje tu ˇc´ast, kter´a odpov´ıdala % (napˇr. 1.0)
$(HOME) promˇenn´a z prostˇred´ı (na rozd´ıl od bashe nutn´e z´avorky)
$$ zastupuje prostˇe znak dolar, $
Sloˇ zitˇ ejˇs´ı pˇr´ıklad: maxim´ aln´ı dosaˇ ziteln´ e pokryt´ı pˇri uˇ cen´ı se VALLEXov´ ych hesel
Ukol je vyhodnotit maxim´aln´ı teoreticky dostupn´y recall algoritmu:´
• Vstup: VALLEXov´a hesla pro mnoˇzinu tr´enovac´ıch sloves, mnoˇzina testovac´ıch sloves
• V´ystup: Algoritmem navrˇzen´e r´amce pro testovac´ı slovesa.
Algoritmus pˇritom mus´ı br´at tr´enovac´ı r´amce jako nedˇeliteln´e celky a jen je pro testovac´ı slovesa pouˇz´ıt (nebo nepouˇz´ıt).
• Dostupn´y recall ˇr´ık´a, jak ˇcasto sloveso z principu nem˚uˇze dostat nˇejak´y sv˚uj r´amec, protoˇze takov´y r´amec nebyl u ˇz´adn´eho tr´enovac´ıho slovesa vidˇet.
Postup ˇreˇsen´ı
• defined-vallex-frames.txt pˇrevedeme tak, aby na kaˇzd´e ˇr´adce byly vˇsechny r´amce dan´eho slovesa, kaˇzd´a ˇr´adka aˇt odpov´ıd´a pr´avˇe jednomu slovesu
• soubor pak rozdˇel´ıme na tr´enovac´ı a testovac´ı ˇr´adky
• nap´ıˇseme mal´y perlov´y skript, kter´y vypoˇc´ıt´a dostupn´y recall pro dan´y tr´enovac´ı a testovac´ı soubor
joined: defined-vallex-frames.txt
# tady nejak spoj radky odpovidajici jednomu slovesu training: joined
skip 100 < $< > $@
test: joined
head -100 < $< > $@
vysledek: training testing zmerit-recall.pl ./zmerit-recall.pl training testing > $@
Nakonec zavol´ame make vysledek.
Jak spojit ˇr´ adky pro jedno sloveso
Vstup:
absolvovat 0 0 1 phase verb absolvovat-1 ACTobl(#nom) PATobl(#aku) absolvovat 0 0 2 - absolvovat-2 ACTobl(#nom) PATobl(#aku)
akceptovat 0 0 1 - akceptovat-1 ACTobl(#nom) PATobl(#aku,ze)
joined: defined-vallex-frames.txt
cut -f1,7 $< \ vyseknout lemma slovesa a r´amec
| split_at_colchange 1 \ m˚uj jednoduch´y n´astroj pˇrid´a voln´e ˇr´adky
| tr ’\n’ ’|’ \ nahraˇd konce ˇr´adk˚u speci´aln´ım znakem
| sed ’s/||/@/g’ \ dvojici konc˚u ˇr´adk˚u nahraˇd dalˇs´ım speci´aln´ım znakem
| tr ’@’ ’\n’ \ ze speci´aln´ıho znaku udˇelej zp´atky konec ˇr´adku
> $@ uloˇz v´ysledek
V´ystup:
absolvovat ACTobl(#nom) PATobl(#aku)|absolvovat ACTobl(#nom) PATobl(#aku) akceptovat ACTobl(#nom) PATobl(#aku,ze)
argumentovat ACTobl(#nom) MEANStyp(#instr,ze) PATopt(proti#dat,pro#aku)
Jak spoˇ c´ıtat dosaˇ ziteln´ y recall?
. . . to je cviˇcen´ı z Perlu, za dom´ac´ı ´ukol :)
• Naˇc´ıst dva soubory po ˇr´adc´ıch, druh´y sloupec vˇzdy rozdˇelit (split) na znaku ’|’.
• Z prvn´ıho souboru se nauˇcit mnoˇzinu zn´am´ych r´amc˚u.
• U druh´eho souboru spoˇc´ıtat, kolik r´amc˚u bylo spatˇreno v mnoˇzinˇe zn´am´ych r´amc˚u.
• Recall = pod´ıl tˇech spatˇren´ych ke vˇsem testovac´ım.
Neˇz se skript zmerit-recall.pl podaˇr´ı odladit, budeme opakovanˇe zad´avat make vysledek, abychom skript spustili.
Nakonec se v´ysledek dozv´ıme takto:
cat vysledek ⇒ 67.5 %
Lepˇs´ı pr´ ace s daty: n-fold evaluation
• V´ysledek pˇredeˇsl´eho pokusu je nestabiln´ı, z´aleˇz´ı na konkr´etn´ıch vybran´ych slovesech (vzali jsme slovesa od zaˇc´atku abecedy!)
• Stabilnˇejˇs´ı v´ysledek dostaneme tak, ˇze pokus budeme opakovat desetkr´at, vˇzdy jednu desetinu sloves pouˇzijeme jako test a ostatn´ı k tr´enov´an´ı.
M˚uj skript nfold pr´avˇe tohle ˇreˇs´ı. Obohaˇtme Makefile:
stabilni: joined zmerit-recall.pl
nfold "./zmerit-recall.pl %train %test" < $< > $@
• nfold naˇcte vˇsechny ˇr´adky ze vstupu, zam´ıch´a poˇrad´ı, rozdˇel´ı na desetiny
• pak spust´ı dan´y shellov´y skript (program s parametry) celkem desetkr´at, pˇriˇcemˇz vˇzdy pˇriprav´ı tr´enovac´ı a testovac´ı soubor a speci´aln´ı ˇretˇezec %train a %test nahrad´ı jm´eny pˇr´ısluˇsn´ych soubor˚u
Jak to dopadlo
make stabilni ; cat stabilni 1 85.43
2 91.58 3 90.00 4 93.24 5 90.09 6 88.01 7 94.25 8 92.34 9 92.29 10 90.95
• Uˇz prost´e prom´ıch´an´ı sloves vedlo k podstatnˇe jin´emu v´ysledku: 85 m´ısto 67 %
• Je vidˇet, jak rozpt´ylen´e hodnoty jsou: 85 aˇz 94 %
/home/bojar/tools/shell/grp –keys= –items=AVG2,STDDEV2 < stabilni
⇒ 90.82±2.60
Co kdyˇ z by VALLEX byl menˇs´ı?
vyvoj: joined rm -f $@
for i in 50 100 200 500 1080; do \ cat $< \
| shuffle \
| head -$$(($$i+50)) \
| nfold --testsize=50 "./zmerit-recall.pl %train %test" \
| prefix --tab $$i \
>> $@ ; \ done
for i in ...; do cmd; done cyklus v bashi, promˇenn´a i
$i hodnota bashovsk´e promˇenn´e $i
$((1+2)) v´ypoˇcet v´yrazu v bashi
dvoj´ı dolar nutno ps´at kv˚uli make
shuffle, prefix moje n´astroje na prom´ıch´an´ı ˇr´adek a pˇrid´an´ı prefixu
Jak to dopadlo
make vyvoj ; head -5 vyvoj
↓ pˇridal prefix, velikost tr´enovac´ı sady
↓ pˇridal nfold, ˇc´ıslo skupiny
↓ samotn´y v´ysledek skriptu zmerit-recall.pl
50 1 30.98
50 2 35.22
100 1 41.67
100 2 50.45
100 3 53.43
. . .
grp –keys=1 –items=AVG3,STDDEV3 vyvoj | sort -n
50 33.10 3.00
100 48.52 6.11
200 56.69 2.94
500 76.99 4.10
1080 90.92 2.62
. . . na generov´an´ı tohoto pˇrehlednˇejˇs´ıho v´ystupu se samozˇrejmˇe hod´ı navazuj´ıc´ı pravidlo v Makefile.
Udrˇ zet poˇr´ adek, sd´ılet s kolegy / na v´ıc poˇ c´ıtaˇ c´ıch
CVS = Concurrent Versions System
⇒ man cvs
⇒ http://ufal.mff.cuni.cz/~semecky/vallex/documents/navod cvs/
• Pro vˇsechny ´uˇcastn´ıky existuje jeden spoleˇcn´y depozit´aˇr (repository).
• Jednou za ˇzivot si kaˇzd´y vyrob´ı odˇstˇepek: pracovn´ı adres´aˇr (cvs check-out).
• Pravidelnˇe se v pracovn´ım adres´aˇri:
– zaregistrov´avaj´ı nov´e soubory (cvs add),
– vkl´adaj´ı platn´e verze do repozit´aˇre, aby byly k dispozici vˇsem (cvs commit), – aktualizuj´ı pracovn´ı verze podle toho, co udˇelali jin´ı (cvs update).
Existuj´ı i modernˇejˇs´ı implementace (Subversion, svn) nebo pˇr´ıstupy (git).
V´ yhody CVS
Oˇcividn´e (proto bylo CVS vyvinuto):
• pohodln´e “kop´ırov´an´ı” soustav pracovn´ıch soubor˚u mezi lidmi ˇci poˇc´ıtaˇci
• pohodln´e ˇreˇsen´ı pˇr´ıpadn´ych konflikt˚u
• podrobn´y pˇrehled o historii soubor˚u (moˇznost kdykoli se vr´atit) M´enˇe n´apadn´e:
• vede k peˇcliv´emu oddˇelen´ı citliv´ych soubor˚u (n´aroˇcn´e na v´yrobu, ruˇcn´ı pr´ace ap.) a soubor˚u odvozen´ych pomoc´ı Makefil˚u
Do CVS pˇrid´av´am (add) pr´avˇe ty drah´e soubory, neaktu´aln´ı naopak odstraˇnuji (cvs remove).
• snadno se otestuje, ˇze nic z´akladn´ıho v CVS nechyb´ı
Udˇel´am ˇcist´y check-out (hraju si na nov´eho uˇzivatele), nech´am vˇse vybudovat od z´akladu.
Sl´ıben´ a smrˇsˇt experiment˚ u
• Kaˇzd´y experiment bude m´ıt sv˚uj podadres´aˇr: exp-pokus1, exp-pokus2. . .
• Budu m´ıt jeden spoleˇcn´y Makefile.common s obecn´ymi pravidly a kaˇzd´y experiment bude m´ıt ve sv´em podadres´aˇri vlastn´ı Makefile:
Takto se jeden Makefile vˇcleˇnuje do jin´eho:
include ../Makefile
• Kaˇzd´y Makefile bude (po sv´em) reagovat na dohodnut´e c´ıle:
for e in exp-*; do cd $e; make results; cd ..; done
• Takto spoj´ım v´ysledky vˇsech experiment˚u pro komparativn´ı vyhodnocen´ı:
(for e in exp-*; do prefix --tab $e < $e/results; done) > all-results
• Nav´ıc si mohu pˇripravit glob´aln´ı Makefile s c´ıli:
make all-results
make clone-pokus1-novypokus
# nejen udˇel´a kopii, ale t´eˇz vloˇz´ı standardn´ı v´ybavu experimentu do CVS
Shrnut´ı
Na Unixu se pohodlnˇe pracuje:
• s textov´ymi soubory (roury, pˇresmˇerov´an´ı)
• se stavebn´ımi kam´ınky, skripty vˇseho druhu
• s postupn´ym vylepˇsov´an´ım procedury pro vyhodnocen´ı nˇejak´eho experimentu vˇcetnˇe moˇznosti sledovat v´ıc variant postupu (r˚uzn´e c´ıle v jednom Makefile)
• s v´ıce pracovn´ımi verzemi tˇechˇze experiment˚u, z v´ıce poˇc´ıtaˇc˚u, s v´ıce spolupracovn´ıky souˇcasnˇe (CVS)
Existuje i pro Windows:
• bash a dalˇs´ı n´astroje – bal´ık cygwin ⇒ http://cygwin.com/
• ikonkov´e CVS – TortoiseCVS ⇒ http://www.tortoisecvs.org/