Zsh-komentotulkin tuunaus



Merkittävä osa ajastani tietokoneella kuluu terminaalissa. Käytän sitä paljon esimerkiksi ohjelmointiin, kirjoittamiseen ja milloin mihinkin. Mainittakoon esimerkkinä, että tämä blogi syntyy pääosin terminaalissa. Linux-käyttäjänä bash-komentotulkki on tullut tutuksi, sillä se tulee useimmissa Linux-jakeluissa oletuksena. Vaihdoin bashista Z shelliin (zsh) noin puolitoista vuotta sitten. Tuunasin zsh:ta aiemmin Oh My Zsh-kokoelmalla (omz), mutta en ollut siihen täysin tyytyväinen, joten päädyin luomaan omat asetustiedostot zsh:lle. Oh My Zsh tuntui tarpeettomalta, ja itse tekemällä myös oppii paremmin tuntemaan käyttämänsä järjestelmän.

Yksi oleellisimpia asetuksia on ”prompt”, joka on teksti, joka komentorivillä näkyy ennen jokaista komentoa. Promptin avulla voi näyttää oleellista tietoa, esimerkiksi nykyisen käyttäjän, tietokoneen ja hakemiston. Kun aloin suunnitella promptia uusiin asetustiedostoihini, mietin, miten ja miksi käytän Oh My Zsh:ta. Sillä saa näppärästi hyvät oletukset ja hienoja teemoja, mutta en oikeastaan käytä kuin osaa niistä. En edes tiedä, mitä kaikkia asetuksia omz on puolestani tehnyt, ja vaikka katsoisinkin, on vaikkapa muiden määrittelemiä aliaksia vaikea muistaa.

Ajattelin katsoa, miten manuaalinen konfigurointi sujuu ja saako sillä tavalla tehtyä käytettävän ympäristön.

Terminaali vanhoilla ja uusilla asetuksilla vierekkäin

Vasemmalla on vanha näkymä (Oh My Zsh:lla) ja oikealla sama näkymä uusilla asetuksilla.

Vasemmanpuoleisessa kuvassa on käytössä omz:n kautta saatu teema af-magic. Asetustiedostot ennen omz:n poistoa löytyvät täältä.

Tavoitteena uudelle teemalle oli olla yksinkertainen ja hyödyllinen. Selitän seuraavaksi tarkemmin, miten prompt syntyi. Jos kuitenkin haluat katsoa suoraan asetustiedostot, ne löytyvät täältä (aina ajantasainen versio löytyy tästä).

Prompt

Tämä osa on jaettu kahtia promptin suunnitteluun ja toteutukseen. Promptin suunnittelu -kohdassa kerron, mitä kaikkea halusin promptiin sisältyvän ja mitä muita ominaisuuksia sillä pitäisi olla. Toteutus-kohdasta löytyy teknisiä yksityiskohtia niistä kiinnostuneille.

Promptin suunnittelu

Halusin promptista yksinkertaisen, kauniin, hyödyllistä tietoa näyttävän ja monessa paikassa toimivan. Edellisten asetusten kanssa kohtasin outoja ongelmia tietyissä ympäristöissä, vaikka ainakin prompt oli melko yksinkertainen. Ehkä nuoli » oli liikaa, vaikka sitä käytetään käsittääkseni lainauksissa joissain kielissä, tai sitten asetuksissa tai ympäristössä oli jotain kummallista. Oli miten oli, yksinkertaisesti rakennetun promptin pitäisi toimia hyvin laitteella kuin laitteella. Konfiguraation pitäisi esimerkiksi ilman muuta toimia vanhassa kunnon Linux-virtuaalikonsolissa. Minulla voisi toki olla tällaisia tilanteita varten oma asetustiedosto, jonka voisin ottaa tarvittaessa käyttöön, tai sitten voisin turvautua vanhaan kunnon bash-komentotulkkiin. Miksi kuitenkaan tekisin niin, kun oletusasetukseni voivat olla hyödylliset ja silti toimia kaikkialla? Oh My Zsh tarjoaa kyllä teemoja, jotka käyttävät erityisiä fontteja, emojeita ja niin edelleen. Ovathan ne hienoja, mutta tällä hetkellä en koe kaipaavani niitä, vaan ennemmin yksinkertaisuutta, joka on yleensä mielestäni kaunista.

Promptin pitäisi näyttää hyödyllistä tietoa, joten ajattelin sisällyttää ainakin nämä asiat:

Aiemmin käyttämäni af-magic on mielestäni hyvä teema ja itse rakentamassani promptissa on paljon samaa. Jos värit jättää huomiotta, perusnäkymässä kotihakemistossa on vain pari eroa. Viimeinen promptin merkki (välilyöntiä lukuunottamatta) on minulla % ja af-magic-teemassa », ja promptistani puuttuu af-magicista löytyvä promptin yläpuolella oleva koko terminaali-ikkunan levyinen viiva. Erot näkyvät ylempää löytyvästä kuvakaappauksesta. Molemmilla prompteilla on ajettu samat komennot, mutta uudessa konfiguraatiossa (oikealla) jäi tilaa ajaa vielä pieni neofetch-komento. Uusi konfiguraatio tekee siis samat asiat tiiviimmin, eli näytölle mahtuu kerralla enemmän oleellisia rivejä.

Promptien välisiä eroja tulee ilmi lisää, kun niillä navigoidaan tiedostojärjestelmässä. Kokeilin promptissani sellaista asetusta, että siinä lukee koko tiedostopolun sijaan vain nykyinen hakemisto ja yksi hakemisto siitä ylöspäin. Näin ollen prompti pysyy kohtalaisen lyhyenä. Lisäksi molemmista prompteista löytyy git-versionhallinnan integraatio, mutta af-magicissa se saattaa olla vähän omaa toteutustani hienompi.

Promptin toteutus

Seuraavaksi käyn läpi teknisiä yksityiskohtia promptin toteutukseen liittyen. Olen myös linkannut lähteitä, jotka olivat mielestäni hyödyllisiä projektin aikana. Tästä osasta on toivottavasti hyötyä, jos olet itse konfiguroimassa zsh:ta, mutta jos tämä tuntuu liian tekniseltä, voit huoletta hypätä osan ohi.

Zsh:ssa prompti muodostetaan määrittelemällä muuttujat PROMPT ja valinnaisesti RPROMPT, joista ensimmäinen on tavanomainen prompti ja jälkimmäinen piirretään terminaalin oikeaan reunaan. PROMPT näyttää nykyisen hakemiston, gitin ja pythonin virtuaaliympäristön tilan sekä loppumerkin. Loppumerkki on zsh:ssa perinteisesti %, kuten minullakin, paitsi jos kyseessä on pääkäyttäjä root, jolloin merkki on niin ikään perinteisesti #. RPROMPTissa näytän tiedon käyttäjästä ja tietokoneen nimestä muodossa user@hostname sekä myös edellisen komennon palautuskoodin, mikäli se ei ole nolla. Olen määritellyt muuttujat asetustiedostossa ~/.prompt.zsh. Tämä tiedosto otetaan käyttöön pääasetustiedostosta ~/.zshrc.

Olen kopioinut alle rivejä konfiguraatiostani. Ensin määrittelin versionhallintajärjestelmään liittyvät asetukset promptiin lisäämistä varten. Asetusten joukossa olevissa kommenteissa (risuaidalla alkavat rivit) yritän selittää englanniksi, mitä tapahtuu.

# setup git information
autoload -Uz vcs_info
precmd_functions+=( vcs_info )
setopt prompt_subst
# %b: branch
# %u: unstaged changes
zstyle ':vcs_info:git:*' formats '%F{5}(%b%u)%f '
# this makes %u work, but also the prompt is clearly slower in git dirs when this is on
zstyle ':vcs_info:*' check-for-changes true
# what string to use for %u when there are unstaged changes
zstyle ':vcs_info:*' unstagedstr '*'
# vcs_info supports multiple version control systems, but I need just git
zstyle ':vcs_info:*' enable git

Zsh:n dokumentaation lisäksi Arjan van der Gaagin lähes vuosikymmenen takainen blogikirjoitus oli erittäin hyödyllinen.

Ylempiä määrittelyjä hyödynnetään alla, kun promptin sisältö määritellään kokonaisuudessaan.

# Explaining prompt:
#
# %B / %F{n}: begin bold / color
# %b / %f: end bold / color
# %n~: display n latest directories of current directory
# %#: display a '%' (or '#' when root)
# %(..): conditional expression (see docs)
# %?: exit code of last process
# %n@%m: user@host
PROMPT='%B${vcs_info_msg_0_}%F{12}%2~%f %# %b'
# rprompt is located on the right side of the terminal
RPROMPT='%(?..%F{red}(%?%) %f)%n@%m'

Yksinkertaisuudessaan olen yhdistellyt muotoiluja ja promptin rakennuspalasia yhtenäiseksi, kokonaiseksi promptiksi. Tässäkin zsh:n dokumentaatio (tämä ja muut kappaleet) olivat hyödyksi, mutta myös Armin Briegelin upea blogikirjoitus ja Jonas Jacekin väritaulukko olivat mainioita lähteitä.

Zsh-liitännäisten hallinta

Zsh:n toiminnallisuutta voi laajentaa erilaisilla liitännäisillä (kavereiden kesken plugareilla). Kokeilin aluksi zplug-työkalua liitännäisten hallintaan, mutta se tuntui hidastavan zsh:n käynnistymisaikaa.

Kysäisin ankalta zplugin latausajoista ja löysin Reddit-julkaisun, joka osoitti epäilyni oikeiksi. Zplug on hidas, mutta onneksi myös nopeampia vaihtoehtoja on saatavilla. Päätin kokeilla antigen-työkalua, jonka sanotaan olevan de facto -liitännäistenhallintatyökalu.

Antigen tuntuikin nopealta ja helppokäyttöiseltä, joten lisäsin sen asetustiedostorepositoriooni (dotfiles) gitin submodule-toiminnolla. Tällöin saan päivitykset aina ajaessani päivitysskriptini. Olisin voinut myös lisätä kaikki liitännäiset suoraan submoduleina, kuten olen tehnyt vim-tekstieditorin liitännäisten kanssa. Ajattelin kuitenkin kokeilla liitännäistenhallintatyökalua, ja tähän asti olen ollut tyytyväinen.

Tätä kirjoittaessani minulla on käytössä kaksi zsh-liitännäistä. Ne löytyvät listattuina antigenrc-asetustiedostosta. Ensimmäinen on syntaksin värittäjä ja toinen komentojen automaattinen ehdottaja. Syntaksin värityksen voi nähdä vertailukuvakaappauksesta ylempää esimerkiksi molempien terminaali-ikkunoiden viimeisellä rivillä. Liitännäinen auttaa minua huomaamaan, jos mokaan lainausmerkkien kanssa tai yritän tehdä jotain kiellettyä, kuten syöttää komentoa, jota ei ole olemassa. Automaattiset ehdotukset taas ovat näppäriä, sillä niiden avulla voin helposti palata aiemmin käyttämiini komentoihin. Tästä lisää alempana.

Täydentäminen

Shellissäni on nyt kolmenlaista täydentämistä: sarkaintäydennystä, automaattista täydennystä (ehdotukset) sekä historiasta etsiminen. Sarkaintäydennyksessä zsh täydentää minulle tiedostopolkuja tai komennon parametreja jo kirjoitetun komennon perusteella, kun painan sarkainnäppäintä Tab. Automaattinen täydennys ehdottaa aiemmin kirjoitettua komentoa jo kirjoitetun osan perusteella. Historiasta etsiminen toimii samaan tapaan, mutta vanhoja komentoja, jotka alkavat samalla tavalla kuin nykyinen rivi, voi selata nuolinäppäimillä. Olen selittänyt tarkempia käyttökohteita alempana.

Sarkaintäydennys

Sarkaintäydennys auttaa minua, jos haluan täydentää komentoja, tiedostonimiä tai komennon argumentteja. Tämä on hyödyllistä, koska en aina muista niitä, ja toisaalta olisi turhaa kirjoittaa kaikki kokonaan itse, jos apua on helposti saatavilla. Zsh:n sisäänrakennettu täydennys on todella voimakas ja tarjolla on erilaisia täydennysstrategioita. Käytin compinstall-komentoa konfiguraatiotiedoston luomiseen. Ilmeisesti jotkut pitävät compinstallia hankalana, mutta konfiguraatiota katsottuani olen melko varma siitä, että pääsin helpommalla sitä käyttämällä kuin asetustiedoston syntaksin opettelemalla. Pidän osittaista täydentämistä erityisen voimakkaana ominaisuutena, jolle on paljon käyttöä. Alla on kaksi esimerkkiä.

Hakemistoon siirtyminen

Jos minulla on Projects-hakemiston sisällä kansio nimeltä test, voin hyödyntää sinne siirtyessäni osittaista täydennysta alla kuvatusti. Esimerkeissä merkitsee sarkainnäppäimen Tab painamista.

~ % proj/t↹
~ % Projects/test/

Kun painan Enter-näppäintä, zsh siirtyy hakemistoon ~/Projects/test/ vähemmällä näpyttelyllä. Tämä hyödyntää zsh:n kätevää ominaisuutta, jossa hakemiston vaihtaminen onnistuu ilman cd-komentoa suoraan hakemiston nimen kirjoittamalla. Ominaisuuden saa päälle komennolla setopt autocd.

Tiedoston muokkaus

On tyypillistä, etten muista tiedostonimeä tarkasti, mutta tiedän varmasti mitä siihen ainakin kuuluu (huijaamatta ls-komennolla). Esimerkkinä mainittakoon tilanne, jossa haluan muokata tällaista tiedostonimeltään epäselvää tiedostoa lempitekstieditorillani (joka muuten on tällä hetkellä neovim, kiitos kysymästä). Voin kirjoittaa tiedostonimestä osan, jonka muistan, painaa Tab, ja antaa zsh:n hoitaa loput. Alla on leluesimerkki, jota vastaavia tilanteita tulee silloin tällöin vastaan: tiedostot on numeroitu, mutta en muista järjestysnumeroa, vaan sen jälkeisen osan tiedostonimestä.

~/blog-demo % ls
01-beginning.md  02-middle.md  03-end.md
~/blog-demo % e mid↹
~/blog-demo % e 02-middle.md

Näppärää!

Automaattinen täydennys

Mainitsin aiemmin, että automaattinen täydennys tulee liitännäisenä. En ole koskaan kokeillut Fish-shelliä, jossa on vastaava automaattinen täydennys oletuksena. Mukavaa, että saman saa myös zsh:ssa käyttöön, vaikkakin liitännäisenä. Liitännäinen toimii siten, että se ehdottaa täydennystä komentoon historian perusteella heti, kun komentoa alkaa kirjoittaa. Ehdotuksen voi hyväksyä painamalla nuolta oikealle. Plugarissa on asetuksia, mutta en ole tutustunut niihin tarkemmin, oletukset tuntuvat toimivan mukavasti. Sen verran muokkasin kuitenkin oletuksia, että ehdotukset voi hyväksyä myös painamalla Ctrl + P, samaan tyyliin kuin vim-tekstieditoria voi pyytää täydentämään nykyistä sanaa.

Historiasta etsiminen

Tätä toimintoa kaipasin eniten Oh My Zsh:sta: kun komennosta on kirjoittanut alkuosan, voi -näppäimellä selata historiasta löytyviä samalla tavalla alkavia komentoja. Toiminto on aivan loistava, koska jos tiedän, että minulla on historiassa joku pitkä komento valmiina, se on nopeasti ja intuitiivisesti löydettävissä, eikä minun tarvitse joka kerta arpoa parametrejä uudelleen.

Samoin kuin tehokkaiden täydennystoiminnallisuuksien kanssa yllätyin siitä, että tämä historiasta etsiminen on zsh:n sisäänrakennettu ominaisuus – ei tarvitse asentaa taas yhtä liitännäistä.

Tehokkuus

Shellin pitää käynnistyä nopeasti. En todellakaan halua joutua odottelemaan joka kerta terminaalia avatessani. Oh My Zsh:n käynnistymisaikaan olin melko tyytyväinen, joten uusi konfiguraatio ei saisi olla ainakaan hitaampi. Mittasin, kauanko zsh käynnistyy vanhoilla ja uusilla asetuksilla käyttämällä komentoa time zsh -i -c exit (löysin vinkin aiemmin linkkaamastani Reddit-kirjoituksesta). Lisäksi otin mukaan pari verrokkia: sh, bash ja zsh (Ubuntun) oletusasetuksilla.

shelli latausaika
zsh (omz) 115 ms
zsh (uusi) 104 ms
zsh (uusi, ei antigeniä) 95 ms
zsh (oletusasetukset) 7 ms
bash (oletusasetukset) 38 ms
sh (oletusasetukset) 4 ms

Tulokset ovat mielenkiintoisia: zsh uusilla asetuksilla ei ole juuri aiempaa, omz:llä höystettyä versiota nopeampi. Antigen-liitännäistenhallintaohjelma taas on todella nopea: kahdella pluginilla latausaika nousee yllättävän vähän. Huomasin myös, että zsh oletusasetuksilla on todella nopea, aivan kuten vanha kunnon sh – molemmat aukeavat käytännössä välittömästi. Bash käynnistyy myös varsin ripeästi. Valitettavasti nopealla käynnistyksellä ei saa kaikkia herkkuja. 0,1 sekunnin käynnistymisaika riittää minulle kuitenkin (ainakin toistaiseksi) varsin hyvin, eikä edes tarvitse tehdä kompromissejä toiminnallisuuden suhteen.

Terminaali vanhoilla ja uusilla asetuksilla vierekkäin

Sama vertailu kuin ylempänä, mutta tummalla teemalla.

Lopuksi

Tämä oli mielenkiintoinen ja opettavainen operaatio. Olen lopputulokseen oikein tyytyväinen ja koen, että ymmärrän shelliäni aiempaa syvemmin. Voin myös hallita sitä entistä paremmin, kun tiedän, mistä mitäkin muutetaan ja mitä vaihtoehtoja on. Dokumentaatiosta löytyy tietysti aina uutta, mutta siihen pitäisi olla helppo palata tarvittaessa. Lisäksi on aina mukavampi käyttää jotain, mitä on itse tehnyt, tai ainakin konfiguroinut.

Tällä hetkellä en koe kaipaavani Oh My Zsh:ta, mutta tilanne voi toki muuttua tulevaisuudessa. Omz tarjoaa monille toimivat oletukset ja hyödylliset toiminnallisuudet, jotka yksinkertaisesti toimivat. Koen kuitenkin tällä hetkellä, että olen saavuttanut itse tuunaamalla saman käytettävyystason, oikeastaan jopa korkeamman. Shelli tuntuu nyt omalta ja sitä on mukavampi käyttää.



Ilmoitan uusista kirjoituksista seuraavissa kanavissa:

Blogini etusivulta löydät muut kirjoitukseni.

Voit ottaa minuun yhteyttä Mastodonissa: @[email protected]. Kuulen sinusta mielelläni!