V nasledovnom texte nájdete vysvetlenia ku mnohým situáciám, ktoré by vás mohli stretnúť pri tvorbe programov v C pod Unix. Nenájdete tu však odpovede na všetko, väčšinou sa tu nespomínajú jednoduché problémy. Ak nenájdete odpoveď v tomto FAQ, použite fórum alebo tútoriály na stránkach predmetu Operačné Systémy.
Zoznam tém aj zoznam otázok sú linky – kliknutím sa presuniete na zodpovedajúce miesto.
Ak sa vam otazky budu zdat cudne, lahke alebo inym z inych pricin nehodne zverejnenia, tak toto su otazky, ktore sa pytali vasi kolegovia na forach rokov minulych ;-).
---------------------------------------- ----------------------------------------
Témy FAQ:
Práca s csh Práca s adresárovou štruktúrou a súbormi Program AWK Úvodzovky Premenné v cshelli Otázky k vypracovaniu zadaní Ostatné otázky
---------------------------------------- ----------------------------------------
Zoznam otázok:
Práca s csh Standardny chybovy vystup (stderr) v cshelli. Cyklus foreach. Vysvetlenie chybovyach hlasok v csh. If a medzery okolo operatorov. Exec vo finde a histchars histchars Práca s adresárovou štruktúrou a súbormi Ako zabrániť substitúcii expanzných znakov v rámci mena súboru ? Ako môžem do findu poslať súbory, ktoré obsahujú medzeru ? Ako vynechať určitú (známu) časť textového reťazca v mene cesty k súboru ? Ako sedu oznámiť, že určitú čast textu so znakmi '/' ma považovať za jeden parameter ? Prečo, keď $dir = pepe, cd $dir a následnom použití foreach file (`ls -a`) nasleduje výpis ACCESS DENIED po príkaze $argv[0] $file ? Ako vypísať zoznam, do ktorého som si uložil výpis adresára príkazom ls -la tak, ako výpis pri normálnom príkaze ls -la a nie všetko na nový riadok ? Ako sa dá ošetriť, aby pri porovnávaní prázdnych súborov pomocou cmp nehlásil chybu ? Ak mám testovací adresár !vykricnik, tak tento program vypíše: vykricnik: Event not found. Ako by sa to dalo ošetriť ? Ako cez ls -l vypísať obsah adresára tak, že vyseknúť len mená súborov a adresárov ? Nakoľko treba testovať existenciu súborov, adresárov, userov, či je user prihláseny a podobne ? Ako spracovat subory s medzerami v nazve? Doplnenie relativnej cesty na plnu. Aký tvar má prepínač -exec command v príkaze find ? Je aj iný spôsob ako zistiť kam ukazuje symbolický link ? Ako sa dá odstrániť z cesty k súboru (adresáru) iba určitú čast zo začiatku ? Program AWK Dajú sa pomocou awk konvertovať malé písmená na veľké ? Dá sa explicitne prikázať awk, aby bral posledná položku až do konca riadku ? Ako sa dá do awk dostať premennú ? Je možné zavolať z awk externý program ? Ako sa dá vyexportovať z awk premenná (t.j. jej hodnota bez ovplyvnenia výstupu) ? Je možné uložiť výstup z funkcie system() do premennej v awk ? Ako sa da odkazat na posledny prvok pola? Ako jednoducho zmazat posledny znak v riadku? Ako zistiť najdlhší riadok v súbore ? Úvodzovky Ako dostat cely riadok do premennej Uvodzovky, medzery a premenne... awk a mkdir Premenné v cshelli Set Zaporny pocet argumentov. Set na jeden riadok aby :q fungovalo Ak máme zoznam, ktorý vyzerá napríklad takto : (" 145 \home\two spaces\a.bat" "147 \home\two spaces\b.bat"). Ako mám z neho vybrať prvú položku (od " po ") aby mi dve medzery po sebe nenahradil jednou a zároveň aby tá nová premenná bola zoznam slov ? Ako sa dá skontrolovať, či sa do číselnej premennej skutočne uložilo číslo ? Čo znamená $premenna:q ? Ako sa zisťuje, či je pole prázdne ? Aký je rozdiel pri práci s argumentami, medzi zápisom $1 a $argv[1] ? Ako sa dá zmazať položka zo zoznamu ? Ako zistím dĺžku stringu v premennej ? Existuje priamo v c-shell-i spôsob ako zistiť dĺžku reťazca ? Dajú sa vypísať nejakým príkazom len určité riadky, ak poznáme ich čísla ? Otázky k vypracovaniu zadaní Fungovanie testovacich zadani Je potrebne komentovat pripravne zadania, ktore odovzdavam? Ostatné otázky Dá sa skonvertovať IP adresa na hostname a naopak ? Ako sa dá z premennej, v ktorej je uložený čas vo formáte min:sek získať len údaj o minutách a text do konca reťazca ? Príkaz last vypíše čas koľko bol človek nalogovaný, ale napr. ak bol nalogovaný z rôznych ttx viackrát, tak to tuším neberie do úvahy a výsledok potom po sčítaní časov je viac ako v skutočnosti bol prihlásený, dá sa to uviesť ako obmedzenie, keď to zadanie odovzdám? Ako sa dá konvertovať čas medzi rôznymi formátmi ? Konkrétne, ako sa skonvertuje čas ziskaný príkazom ps -o lstart na formát Epoch (počet sekúnd od 1.1. 1970) ? Ako sa dá ošetriť problém dlhého slova na riadku ? Napríklad niektoré cesty su veľmi dlhé a asi by nebolo múdre ich nejako sekať. Čo znamená keď LAST vypíše v poslednom poli miesto (HH:MM) niečo takéto : (x+HH:MM), kde x je nejaké číslo,H:M sú samozrejme hodiny:minuty ? Dá sa vypísať obsah riadku od určitej pozície (od indexu znaku resp. čísla stĺpca) až do konca riadku pomocou zadania nejakého intervalu ? Ak chceme nájsť procesy používateľov prihlásených zo strojov zadaných ako argumenty, prikaz WHO vypisuje odkiaľ je používateľ prihláseny, ale niekedy ako číselnú IP adresu. Je formát výpisu vždy na ose automaticky pre všetkych ako požadovaný v zadaniach ? (alebo dokáže aj C-shell prevadzat čis. IP na hostname.... asi len funkcia v C-cku alebo treba použiť iný shellovsky prikaz ?). find | grep Ako odstranit 'divne znaky' na konci riadku dosovskych txt suborov? Ako nevytvorit subor, ak by mal byt prazdny? Ako skonvertovat cas z formatu 'who' na pocet sekund?
---------------------------------------- ----------------------------------------
Práca s csh
Otázka:
Chcel by som sa spýtať na stderr v c-shelli. Čo je to štandardne, výstup na obrazovku (terminál) alebo ide to do nejakého systémom pevne určeného súboru. Ďalej ma zaujíma, či sa dá voľajako presmerovať iba stderr, lebo zatial som videl iba spolocne presmerovanie stdout a stderr ako >&. Potom nevidím význam rozdelenia štand. výstupu na úspešný a chybový.
Odpoveď:
Kazdy beziaci proces v Unix-e ma pristupny standardny vstupny subor, standardny vystupny subor a chybovy vystupny subor. Pristupuje k nim cez deskriptory 0, 1 a 2 (v takomto poradi - t.j. ked proces cita z deskriptoru 0, tak cita obsah standardneho vstupneho suboru). Ake subory to v skutocnosti budu sa urci pred spustenim procesu (jadro OS na to ma systemove volania). Vacsinou to byva tak, ze interaktivne procesy ich maju nastavene na specialny subor - zariadenie - /dev/tty, ktory predstavuje ich terminal. Shell Vam potom poskytuje moznost toto nastavenie zmenit - vykonat presmerovanie vstupu a vystupu - a to pomocou specialnych znakov , atd. Napr. ked spustite prikaz ls, tak vypisuje na obrazovku (t.j. terminal). Ked spustite ls > out, tak vystup ls bude presmerovany do suboru out. Co sa tyka presmerovania len chyboveho vystupu v csh, tak to nie je priamo mozne (nepodporuje to csh, ine shell-y to vedia). V csh to nie je asi preto, lebo to nie je bezne potrebne - ved aj ked presmerovavate vystup do suboru, stale by ste chceli byt priamo informovany o vzniknutych chybach. Ak to ale naozaj chcete a musite urobit, mozete skusit nasledovnu fintu (bez vysvetlenia - to najdete inde):
(ls > stdout) >& stderr
---------------------------------------- ----------------------------------------
Otázka:
Skript:
#! /bin/csh foreach riadok ("`ps -auxw`") echo "$riadok" end
Ked som skript spustil par riadkov prebehlo a vypisalo a potom: end: No match. To iste sa dokonca vypisalo aj ked som vymazal echo. Ked som tieto prikazy napisal priamo do prikazoveho riadka prebahli spravne. A fungovali aj v Linuxe. Kde je problem?
Odpoveď:
V csh (nie v tcsh) zoznam hodnot pre prikaz foreach podlieha expanzii znakov *?[] atd. aj ked by nemal. Napr.:
foreach l ("`ps -auxw`") end
a kedze vystup ps moze obsahovat uvedene znaky, nastane uvedena chyba. Iny pripad:
set a = "taky_nebude*" foreach l ("$a") end
Takze ak sa v zozname nachadzaju znaky pouzivane pre expanziu mien suborov, sposobi to uvedenu chybu. Vyhnut sa tomu mozete tak, ze pred takymto cyklom nastavite premennu "set noglob" (cim zrusite expanziu) a po nom ju zrusite "unset noglob". A uplne najlepsie je sa vyhnut cyklu foreach uplne.
---------------------------------------- ----------------------------------------
Otázka:
Nie je v csh nieco, kde by sa dalo najst vysvetlenie chybovych hlaseni? V subore Chybovehlaskycsh.txt ich je sice dost, ale uz mi to vyhadzovalo kopec chyb, ktorym som prilis nerozumel a pomohlo by aspon tusit v com moze byt chyba.
Odpoveď:
Vacsina chybovych hlaseni v csh je sice velmi strucna, ale na druhej strane je z nich vacsinou jasne, kde je problem (teda ked ich viete prelozit :-). Prave preto, ze som nenasiel popis chybovych hlasok csh, zacal som vytvarat subor Chybovehlaskycsh.txt, v ktorom su popisane hlasky, ktore sa vyskytuju najviac, pripadne su problemy s ich vyznamom. Pokial viete o chybovych hlaskach, ktore by bolo dobre so tohto suboru doplnit, sem s nimi.
---------------------------------------- ----------------------------------------
Otázka:
If a medzery okolo operatorov:
Majme priklad, ze v @ pocet=2
Preco:
if ($pocet >= 2 && $pocet 1 && $pocet < 5) then ...
to berie v pohode?
Odpoveď:
Problem je asi v tom, ze $pocet dev/stderr endif POZOR - Pojem plna cesta, pouzivany v zadaniach nie je totozny s pojmom absolutna cesta. To, ze mate vypisat plnu cestu k najdenemu suboru znamena, ze nestaci vypisat len meno suboru ale aj cestu, ktora k nemu vedie (zacinajuc tym, co bolo zadane). napr.
najdi.csh tento/adresar vypise
Output: 'tento/adresar/iny/adresar/subor& #039; alebo
najdi.csh ../iny vypise
Output: '../iny/subor' alebo
najdi.csh /tmp vypise
Output: '/tmp/adresar/subor'
---------------------------------------- ----------------------------------------
Otázka:
Aký tvar má prepínač -exec command v príkaze find ?
Odpoveď:
Ten prepínač má tvar -exec prikaz {} \; Pričom tie zátvorky sa nahradia menom každého nájdeného súboru. Ten príkaz sa vykoná nad každým nájdeným súborom. Dôležité sú lomítko a bodkočiarka na konci. find /home -exec cat {} \;
Vypíše každý nájdený súbor na stdout . ---------------------------------------- ----------------------------------------
Otázka:
Je aj iný spôsob ako zistiť kam ukazuje symbolický link ? Napr.: `filetest -L meno_linku`
Odpoveď:
Tento postup funguje len v tcsh a je ekvivalentný zápisu:
if (-L meno_linky) ...
Csh nepozná test na symbolickú linku a treba to urobiť ináč. Napríklad podľa výstupu príkazu ls : if ("`ls -l meno_linky`" =~ l*) ...
---------------------------------------- ----------------------------------------
Otázka:
Ako sa dá odstrániť z cesty k súboru (adresáru) iba určitú čast zo začiatku ? Napr. keď mám zoznam s položkami (/home/os/csh/doc home/os/csh/pocitacove /home/os/csh) a chcem odstrániť z každej home/os.
Odpoveď:
Môžete to urobiť napríklad použitím programu sed :
echo "$polozka" | sed 's+/home/os++'
čo by malo byť to, že na každom riadku (v tomto prípade iba na jednom - tom, ktorý mu pošle echo ) odstráni reťazec /home/os (resp. nahradí ho (s ako substitute) prázdnym reťazcom). Alebo keď viete, že to má určitý počet znakov: echo "$polozka" | cut -c 9-
---------------------------------------- ----------------------------------------
Program AWK
Otázka:
Dajú sa pomocou awk konvertovať malé písmená na veľké ?
Odpoveď:
Áno, ale jednoduchšie by to bolo pomocou tr:
echo "$text" | tr a-z A-Z
Pomocou awk napríklad vyriešime problém, ak reťazec začína veľkým písmenom skonvertuje všetky znaky na veľké a podobne sa to dá aj naopak: echo $xx | awk '{if($0 ~ /^[[:upper:]]/) print(toupper($0))}'
---------------------------------------- ----------------------------------------
Otázka:
Dá sa explicitne prikázať awk, aby bral posledná položku až do konca riadku ?
Odpoveď:
Naneštastie to nejde. Skúste sa ale pozrieť na funkciu substr(s,i[,n]) a pomocou nej získať to, čo potrebujete. ---------------------------------------- ----------------------------------------
Otázka:
Ako sa dá do awk dostať premennú ?
Odpoveď:
Je niekoľko spôsobov, tu sú tri z nich:
awk -v p=111 'BEGIN {print p}' set p = 111; echo $p | awk {print $0} set p = 111; awk 'BEGIN {print '"$p"'}'
---------------------------------------- ----------------------------------------
Otázka:
Je možné zavolať z awk externý program ?
Odpoveď:
Ale samozrejme, veď awk je veľmi inteligentný program - dá sa to napríklad takto: awk '{system ("ls -la")}'
---------------------------------------- ----------------------------------------
Otázka:
Ako sa dá vyexportovať z awk premenná (t.j. jej hodnota bez ovplyvnenia výstupu) ?
Odpoveď:
Nedá - awk predsa číta súbory (resp.štandardný vstup) a zapisuje na štandardný vystup (resp. do súborov). Tak ako nie je možné k premenným shellu pristupovať z iných programov (sú len v jeho adresnom priestore), tak to nie je možné ani k premenným awk . Snáď jediná možnost ako dosiahnut to čo chcete, je poslať obsah premennej (vo vhodnom formáte) na výstup a z neho ho potom prevziať, prípadne ich zapísať do dočasného súboru. ---------------------------------------- ----------------------------------------
Otázka:
Je možné uložiť výstup z funkcie system() do premennej v awk ?
Odpoveď:
Je možné zistiť len exit status, awk totiž neposkytuje mechanizmus `prikaz` tak ako csh. Funkcia system() je v awk určená na spúšťanie externých príkazov podľa spracovaného vstupu - teda napr. poslať mail pre každý riadok vstupu, spustiť program rm a pod. ---------------------------------------- ----------------------------------------
Otázka:
Ako sa v AWKacku da odkazat na posledny prvok pola?
napriklad:
#rozdeli retazec foo do pola bar podla ":", tj. napr:
#foo="aaa:bbb:ccc" => bar[1]="aaa", bar[2]="bbb" a bar[3]="ccc" split(foo,bar,/:/)
a potrebujem zistit, kolko prvkov ma pole bar.
Odpoveď:
Ked pouzivas split na vyrobenie pola, tak je to jednoduche - samotny split vracia pocet poloziek pola (navratova hodnota). Ked ale ziskas pole nejakym inym sposobom, tak to vo vseobecnosti asi nejde. Polia v awk su asociativne a indexom moze byt v podstate cokolvek. Takze v principe nepotrebujete poznat pocet prvkov v poli - totiz ked potrebujete prejst vsetky prvky, aj tak musite pouzit for+in, pretoze neviete, ake indexy su pouzite. No a pre tych par pripadov, ked to potrebujete si jednoduche pri kazdom pridani do pola zvysite pocitadlo.
---------------------------------------- ----------------------------------------
Otázka:
Ako jednoducho zmazat posledny znak v riadku?
Odpoveď:
Pomocou sed: sed 's/.$//' teda nahradite (substitute - s) regularny vyraz .$ (teda jeden znak na konci riadku) prazdnym retazcom.
Alebo pomocou awk:
awk '{print substr($0, 1, length()-1)}'
---------------------------------------- ----------------------------------------
Otázka:
Ako zistiť najdlhší riadok v súbore ?
Odpoveď:
Length v awk je funkcia, ktorá má jeden parameter (resp. ked ho nezadáte, zistuje dĺžku $0 ). Malo by to byť: awk '{print length(), $0}' subor | sort -n
---------------------------------------- ----------------------------------------
Úvodzovky
---------------------------------------- ----------------------------------------
Otázka:
Uvodzovkovanie Potrebujem spravit,aby v set links = `ls -l | awk '{print $0}'` som dostal za kazdu polozku links CELY riadok prikazu. Nijak mi to neslo (na Linuxe).
Odpoveď:
V awk print bez parametrov vypise $0, takze print $0 je zbytocne.
Je potrebne to nasledovne zaqotovat:
set links = "`ls -l | awk '{print "'$0'"}'` " pretoze musis vonkajsiemu shellu zrusit specialny vyznam znaku $ pri zapise $0 - '$0' vonkajsi shell nenahradi, ale az awk.
---------------------------------------- ----------------------------------------
Otázka:
%set skuska="viac medzier" %echo "$skuska" vypis:viac medzier %set prem=`echo $skuska` %echo "$prem" vypis:viac medzier
problem z tohto je zjavny, pri prvom vypise je este v premennej $skuska spravny pocet medzier, ale v druhom vypise je uz v premennej $prem iba 1 medzera, toto sposobuje problem pri praci s menami adresarov a suborov, ake je riesenie?
nefunguju ani nasledujuce:
%set prem=`echo "$skuska"` %set prem="`echo $skuska`" %set prem=`echo '"'$skuska'&quo t;'` %set prem=`echo "'$skuska'"`
Odpoveď:
Vzhladom na to, ze vnutorny shell (subshell), ktory spracovava prikaz medzi ``, znova analyzuje zadany riadok, treba aj jemu premennu zaquotovat (Samotne `` nequotuju).
Takze po jednom:
>%set prem=`echo "$skuska"` Toto len zaquotuje premennu vo vnutornom shelli. Nesmiete zabudnut na to, ze vystup prikazu v `` sa este rozsekava na slova.
>%set prem="`echo $skuska`" Tu ste uz zabezpecili ze sa vystup rozseka po riadkoch (a teda jednoriadkovy vystup echo zosttne pokope), ale nazaquotovali ste premennu.
>%set prem=`echo '"'$skuska'&quo t;'` Vnutorny shell vidi zaquotovane uvodzovky a nezaquotovanu premennu ...
>%set prem=`echo "'$skuska'"` Tu vnutorny shell vidi apostrofy zaquotovane v uvodzovkach a vonkajsi shell to rozseka po slovach.
Spravne riesenie je:
set prem = "`echo '$skuska'`" alebo
set prem = "`echo "\""$skuska"\ ""`" alebo
set prem = "`echo "\$"skuska:q`"
Zaujimavejsie je toto:
set prem = "`echo "\$"skuska:q`" pretoze az vnutorny shell "uvidi" $skuska:q, zaquotuje to po prvkoch a nahradi.
---------------------------------------- ----------------------------------------
Otázka:
Vyvstal jeden problem s vytvorenim adresara s medzerami v nazve pomocou awk (nezlaknite sa, to je len na ilustraciu parsovania :)
prikaz pre cshell:
awk 'END {a="a b";system("mkdir '\''"a"& #039;\''")}' /dev/null
ako to pracuje?
Odpoveď:
cshell sparuje apostrofy ', ktorych obsah bude brat za retazce (backslash apostrof \' samozrejme nie je zaciatok retazca) a vytvori argument prikazu awk:
END {a="a b";system("mkdir '"a"'")}
awk vytvori retazce podobne ibaze z uvodzoviek ", apostrofy tu budu iba obycajne znaky (to a za mkdir je interna premenna awk, ktora sa nahradi jej obsahom) a prikaz system dostane argument v tvare: mkdir 'a b' ktory sa teda vykona spravne.
---------------------------------------- ----------------------------------------
Premenné v cshelli
---------------------------------------- ----------------------------------------
Otázka:
Ked dam
set prem set prem2 = ( )
tak:
echo $#prem .......... 1 echo $#prem2 .........