Command line Jedi

14. 9. 2011
Doba čtení: 11 minut

Sdílet

V článku získate zopár dobrých rád ako pracovat ešte aj dnes efektívne s command line, shellom a ukážeme si zopár zaujímavých trikov a užitočných programčekov pre prácu s textom. V prvom článku sa sústredím viac na shell, konkrétne bash, ale myslím, že niektoré rady sú univerzálne a platné pre akýkoľvek shell a OS.

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šieE­ditor 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.

bitcoin školení listopad 24

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.

Autor článku