Rozprávkový úvod
V časoch, kedy všetky terminály na svete ešte svietili na zeleno a ich farba sa nedala zmeniť, každé ťuknutie do klávesnice bolo drahšie ako soľ a internetu sa hovorilo lokálna sieť, zrodil sa Unix toolbox. Jednotlivé jeho časti dostali za úlohu robiť iba jednu vec, ale poriadne. A aby sa z tých malých vecí potom dali robiť veľké veci, bol vymyslený shell, ktorý umožňuje tieto jednoduché vecí prepájať. Toolbox sa zvačšoval a aj jeho možnosti rástli.
Vtedy existovali potulný ritieri, ktorí sa zaoberali skutočným spracovaním informácií, pripájali sa od jedného servra k druhému a zadávali komplikované príkazy. Hovorilo sa im „Command Line Jedi“. Osobne som niektorých poznal a snažil som sa im priblížiť, škemral som od nich ich heslá, aby som im mohol vykradnúť history file, ale oni sa nedali tak ľahko zlomiť. Až som sa raz stal administrátorom servra a odvtedy som už viac nepotreboval ich povolenie na prístup k history. Svoje vlastné umenie som tak zdokonaľoval, až som raz zistil, že je na čase chrániť svoje vlastné history.
Dlho som si zo svojím umením vystačil, až na scénu vtrhla armáda GUI klikačov, ktorý zvládli všetko to, čo ja, možno niekedy aj viac, v spresheete, a ešte sa mi smiali, aká je moja zbroj bezzubá voči ostrému hrotu ich šipky, ktorú tak bravúrne, ba až magicky na diaľku ovládali myšou, ktorú pevne zvierali v ruke. Moje skily sa stali irelevantné v novej ére klikačov. Až minule! Do mojej svätyne prenikol zdesený klikač: „Tie súbory majú 2 milióny riadkov.“ A samozrejme treba s nimi niečo urobiť, lebo za 5 minút musia byť výsledky. Vtedy som zo skrine vytiahol svoju starú čelenku, tak charakteristickú pre Command Line Jedi, a zadrtil som im také 4 commandy rovno do shellu že sa aj oni stali tichými obdivovateľmi dávneho umenia Jedi, ktoré aj v kamenných časoch umožnovalo spracovať nespracovateľné, dokázalo človeka zdvihnúť do výšin a otočiť chod elektrónu naruby.
Realita
Dosť bolo romantiky, poďme sa niečo naučiť. Po článkoch o VIMe (Editor Vim efektívnejšie, Editor Vim efektívnejšie po druhýkrát) som dlhšie hľadal nejakú tému, o ktorej by sa oplatilo písať. Pokročilé veci ohľadom VIM nemá zmysel preberať, pretože si to človek môže naštudovať priamo z manuálu VIMka (ale možno ešte urobím jeden článok ohľadom :s, čítaj search and replace). Tému nakoniec ponúkol sám život. Pri práci pre jednu veľkú inštitúciu sme potrebovali zabiť asi 25 procesov, ktoré sa nesprávne pospúšťali. Dotyčný administrátor spustil ps -ef
napísal do command line kill
, myškou označil číslo procesu, klikol pravým tlačidlom do terminálového okna, označil „Copy“, potom znovu pravý klik a „Paste“ a Enter a toto sa opakovalo 25krát. Samozrejme to sprevádzali reči o tom, aký je ten Unix nemožný.
Tento článok nemá za účel pojednávať o tom, ktorý shell je najlepší, ktorý OS je lepší, prípadne vyčerpávajúcim spôsobom pojednať o konkrétnych programoch. Prináša iba rady, ktorými si môžete zjednodušiť prácu. Je mi jasné, že niektoré príklady sa dajú napísať menším počtom znakov, ale ja viem písať 10timi, takže 3–4 znaky nám jar neurobia. Ak ale máte radikálne lepšie riešenie daného problému, prosím podeľte sa on v diskusii, rád si to pozriem. Odvážnejší môžu aj celý history file – zaradím do zbierky.
V prvom článku sa zoznámime s našim najlepším priateľom – shellom. Konkrétne bashom. V druhom článku si povieme viac o Unix toolboxe, príkazoch ako je cut
, paste
, join
, sed
, …, nie vyčerpávajúco, skôr dobrými radami a ukážkami, ako problém riešiť.
Editujeme v shelli
Podľa vzoru článkov o VIMe poďme priamo k radám. Každý „Command Line Jedi“ pracuje v shelli vo VI mode. Prečo tomu tak je, je vysvetlené v článku o VI. A keď ste sa už ten VI naučili, bola by škoda nepoužiť ho. Nastavíte si to v bash-i pomocou set -o vi
, niektoré shelly iný mód ani nemajú, v iných sa to nastavuje ináč, pozrite si dokumentáciu vášho shellu. Bash vie doplňovať názvy súborov po stlačení klávesy TAB, ak je viac možností na doplnenie, ďalšie stlačenie TAB ich zobrazí. Pozor, toto dokážu aj niektoré iné shelly, ktoré by ste mohli považovať za trochu starobylé, ako napríklad ksh, aj na „obskurných“ platformách ako je AIX, ibaže sa vyvoláva stlačením „ESC“ a „\“ po sebe.
Keď už ste vo VI móde ( set -o vi
), vyskúšame si základné príkazy. Detailnejší popis nájdete v manuáli. Štandardne po odoslaní príkazu sa v shelli nachádzate v insert móde, musíme prejsť do command módu – stlačíme ESC
a môže skúšať:
- k – predchádzajúci príkaz
- j – nasledujúci príkaz
- h – posun o jeden znak dolava
- l – posun o jeden znak doprava
- w – nasledujúce slovo
- b – predchádzajúce slovo
- /text<Enter> – nájde v histórií príkaz, ktorý obsahuje text
- n – zopakuje hľadanie
- N – hľadanie opačným smerom
- fx – nájde ďalšie písmenko x na riadku
- ; – zopakuje posledný príkaz f
Vyskúšajte si aj ostatné známe príkazy z VI editora ako ;,tTF spomenuté v prvom článku o VIM.
Zmeny na riadku (pozor na veľké a malé písmená):
- i – prechod do edit módu na mieste pred kurzorom – insert
- a – prechod do edit módu hned za kurzor – append
- x – zmazanie pismena
- A – pridanie na konci riadku
- D – zmazanie textu až do konca riadku – delete
- C – zmena obsahu do konca riadku – change
- R – prechod do replace módu
Máte dokonca aj clipboard, takže môžete urobiť v command móde na jednom riadku copy yw
(yank word), prejsť na iný riadok a dať p
(paste).
Toto vám umožní, aby ktokoľvek sediaci vedľa vás pri termináli okamžite pochopil že pracuje s ozajstným majstrom Jedi, pretože nebude rozumieť: „Čo do paroma sa to deje na tom riadku?!?!?“.
Shell options
Shell poskytuje mnohé interné nastavenia a pomôcky. Zadáme shopt
na command line. Jednotlivé nastavenia nájdete v manuálových stránkach bash-u. Rád by som upozornil na niektoré, ktoré sa vám môžu hodiť. Príkaz autocd
umožňuje, že keď zadáte cestu na commandline bez príkazu, vykoná sa cd
do tohto adresára. To je dobré, ak chcete zadať nejaký príkaz, ale zrazu zistíte, že sa v tom adresári potrebujete poobzerať, nemusíte prerušiť písanie a ísť na začiatok príkazu a zadávať tam cd
.
Ďalší zaujímavý prepínač je dotglob
, ktorý obchádza začiatočnú bodku ako špeciálny znak pre skryté súbory. Hodí sa pre zvedavého a zábudlivého roota.
Potom tu máme ešte dva prepínače na riadenie expanzie *
. Sú to globstar
a nocaseglob
. Prepínač globstar
zabezpečia rekurzívne vyhľadávanie v strome pri zadaní **
a prepínač nocaseglob
ignoruje veľké a malé písmená. Takže si ich nastavíte v shelli pomocou:
$ shopt -s globstar nocaseglob autocd
Teraz keď zadáte napríklad
$ ls -l **/*.txt
je to „viacmenej“ to isté, akoby sme zadali
$ ls -l `find . -type f | fgrep -i .txt`
Samozrejme prvá varianta je oveľa rýchlejšia a intuitívnejšia – po určitom čase, keď si na to zvyknete.
Ďalším často ignorovaným nastavením v bashi je CDPATH
. Dajte si tú robotu a nastavte si najčastejšie navštevované adresáre.
Napríklad:
export CDPATH=~:~/Desktop:~/workspace:/opt
Na záver tejto kapitoly jedna špecialitka:
export HISTTIMEFORMAT="%m/%d/%y - %H:%M:%S "
A zadajte history
. Postupne prejdime do hladiny alpha a rozjímajme nad krásou dokonalosti hlúpeho elektrónu, ktorý vždy ide cestou nižšieho odporu, na rozdiel od niektorých múdrych ľudí.
Zadávanie komplikovanejších príkazov priamo na command line
Častokrát sa stretávam s tým, že ľudia sa boja alebo nevedia zadať štrukturovaný príkaz ako je for
alebo while
priamo na command line. Maximálne niekde dajú rúru |
alebo tí zdatnejší rozumejú aj tomu, čo robí ||
a &&
, keď nie je použité v if
príkaze.
Takže praktický príklad monitorovania výstupu programu. Chceme vediet každých 5 sekúnd, koľko riadkov má súbor mojLog.txt
, ktorý môže byť výstupom nejakého spracovania. Ak nemáte po ruke nič, čo dokáže generovať výstup, zadajte do command line nasledujúci príkaz bez toho, aby sme sa ním bližšie zaoberali:
$ while true; do r=$RANDOM; echo "Random is $r"; sleep $(( $r % 3 )); done > mojLog.txt &
A teraz môžme začať počítať riadky:
$ while (true); do wc -l mojLog.txt; sleep 5; done
Takto to priamo zadajte na command line a odošlite cez Enter. Začne sa vypisovať každých 5 sekúnd stav počtu riadkov daného súboru. Samozrejme takto môžete spúšťat akýkoľvek príkaz potrebujete. Pozornosť som chcel ale upriamiť na konštrukciu za do
. Nie je tam bodkočiarka. Za príkaz do
píšete priamo ďalší príkaz, ktorý sa má spúšťať v bloku while. Toto zastaví mnohých nádejných začiatočníkov.
Teraz sa poďme pohrať so súbormi. Chceme všetky súbory s príponou txt
prekopírovať do súboru s rovnakým menom ale príponou bak
tak že txt
odstrihneme z názvu a to všetko rekurzívne. Prvá idea:
$ for i in `find . -name '*.txt'`; do echo cp $i ${i%txt}bak; done
Čo sme to urobili? Za prvé, postupujeme iteratívne, obzvlášť ak ideme niečo takéto robiť ako „root“ alebo nad veľkým počtom súborov. Takže namiesto skutočného spustenia príkazu ho iba vypisujeme cez echo
, aby sme skontrolovali, ako vyzerá to, čo ideme spúšťať. Find nám nájde všetky súbory s príslušnym názvom.
Pozor na použitie úvodzoviek, vo find vždy použite úvodzovky najlepšie jednoduché, nie dvojité (za chvíľu si ukážeme prečo). Konštrukcia ${i%txt}
spôsobí že sa z konca premennej i odstráni text txt, ak tam je. Ináč je príkaz celkom prehľadný. Teraz si ešte ukážeme, že zložené príkazy sú vlastne iba jedným príkazom. Spočítame, koľko premenovaní sa urobí. Vrátime sa na predchádzajúci riadok a na koniec dopíšeme | wc
. Výsledok by mal byť:
$ for i in `find . -name '*.txt'`; do echo cp $i ${i%txt}bak; done|wc -l
A spustíme cez Enter. Ak sa nám všetko pozdáva, vrátime sa k pôvodnému príkazu a odmažeme príkaz echo
a cp
sa po spustení reálne vykoná. Ak sa bojíte a skutočne si chcete overiť, čo sa bude robiť, presmerujte si výstup do súboru (na koniec pridajte >out.sh
, čo spôsobí vytvorenie out.sh súboru), ktorý môžete pozrieť a upraviť v textovom editore a spustiť. Buď dáte chmod +x out.sh
a spustíte ./out.sh
alebo jednoduchšie priamo bash < out.sh>
.
Escape znaky
Dnes keď robíte v shelli, musíte si dávať pozor na niekoľko zákerností. Medzi najvačšiu by som zaradil medzery v názvoch súborov, prípadne iné riadiace znaky ako – ktoré môže shell expandovat a program interpretovať ako prepínač. Zákerné sú aj znaky *
či \n
(new line). Ak pracujete nad cudzími súbormi, vždy si overte, čo tieto názvy obsahujú. Akonáhle narazíte na súbory a adresáre začínajúce -
alebo súbory obsahujúce medzeru či hviezdičku, musíte si začať dávať pozor, čo idete robiť.
Ja obvykle prácu nad takýmito súbormi odmietam, prípadne si ich najprv premenujem. Pre jednoduchšie prípady postačuje príkaz xargs
v spolupráci s príkazom find
. Find má prepínač -print0
, ktorý začne názvy súborov ukončovať NULL znakom, čo dokáže xargs
pomocou prepínača -0
elegantne čítať. Pre predstavu napríklad:
$ find . -type f -print0 | xargs -0 ls -l
Druhá zákernosť spočíva v používaní jednoduchých a dvojitých úvodzoviek. Ja rád uvažujem v konkrétnych príkladoch, takže ako mentálne cvičenie si vyskúšame (zadávajte na command line):
$ mkdir Q $ cd Q $ cp /dev/null skuska.xml $ aqa=*.xml $ echo $aqa $ echo "$aqa" $ echo '$aqa'
Zdôvodnenie výsledkov nechám na vás. Takže na to tiež pozor, akú úvodzovku kde použijete.
Výpočty na command line
Shell vie aj počítaťm aj keď sa o tom málo vie. Slúži na to špeciálny zápis, napríklad echo $((3 + 4))
vypíše 7. Takže skúsime si modifikovať náš príklad so zobrazením logu tak, aby zobrazil aj počet riadkov, ktoré sa aktuálne spracovali.
$ lastV=0; while (true); do newV=`wc -l roumen.cz.txt | cut -d' ' -f1`; echo $((newV - lastV)); lastV=$newV; sleep 5; done
Prípadne si skúsme spočítať, koľko pamäte nám zaberajú applety.
$ sum=0; ps uax | grep applet | grep -v grep | while read a b c d e f; do ((sum += $e)); done; echo $sum
Ups, výsledok je 0. Skúsime trik z predchádzajúceho príkladu
$ sum=0; ps uax | grep applet | grep -v grep | while read a b c d e f; do echo $((sum += $e)); done
Čo funguje, ale dá toľko riadkov, koľko je vstupných riadkov. Môžeme odfiltrovať posledný riadok:
$ sum=0; ps uax | grep applet | grep -v grep | while read a b c d e f; do echo $((sum += $e)); done | tail -1
Čo nie je celkom pekné riešenie. Command Line Jedi sa zamyslí a skúsi:
$ sum=0; ps uax | grep applet | grep -v grep | ( while read a b c d e f; do ((sum += $e)); done; echo $sum )
Ak nerozumiete, nevadí, to je súčasť toho, ako sa stať správnym majstrom. Rozjímajme…
OK, trik spočíva v tom, že rúry, ktoré spájajú jednotlivé príkazy, spôsobia že sa vykonajú v sub shelli až po done
. Príkaz echo $sum
sa spúšťa v inom shelli, čo spôsobí stratu hodnoty. Pomocou ()
zabezpečíme, že všetko vrátane echo
sa vykoná spolu v jednom shelli.
Záver
Na záver snáď jedna dobrá rada. Dobrý command line Jedi je majstrom improvizácie, nie perfekcionalizmu. Na to samozrejme musíte mať veľa praktických skúseností. Preto je dobré trénovať sa na malých veciach a posúvať si hranice. Každý aj komplikovaný príkaz vzniká postupne, treba ho postupne zdokonaľovať, vylepšovať, občas prekontrolovať či výsledok zodpovedá očakávaniu a pokračovať ďalej vo vylepšovaní. Unix toolbox je silný nástroj. Ušetrí vám veľa písania a množstvo klikania, ak prichádzate z ‚opačnej‘ strany.
Toto je duch Unixu, efektívneho nástroja na prácu. Efektívneho využitia CPU, disku a iných zdrojov. Efektívneho zdieľania týchto zdrojov medzi viacerými uživateľmi. Isteže pri dnešnej sile CPU a množstve pamäte už nie je treba toľko rozmýšľať a improvizovať ako kedysi, to ale neznamená, že sa o svoje CPU cykly mienim deliť s antivírusovým programom.