Ohjelmistokehityksessä suurin osa käytössämme olevista työkaluista on komentorivisovelluksia. On myös syytä huomata, että monet komentorivillä käytettävät työkalut ovat varsin tehokkaita siinä, mitä niillä voi saada aikaan, triviaaleista ikäviin. Kun asiaa viedään vielä pidemmälle, komentorivisovelluksia voi yhdistää toisiinsa ketjuttamalla haluamansa tuloksen aikaansaamiseksi. Olemassa olevien komentorivikomentojen opettelun avulla voit lisätä tuottavuuttasi ja ymmärtää paremmin, mitä mahdollisuuksia sinulla on käytettävissäsi ja mitä tehtäviä sinun on ehkä toteutettava itse.
Komentorivityökalua suunnitellessasi sinun on todella kiinnitettävä huomiota siihen, kuka tai mikä on kohderyhmäsi. Jos esimerkiksi kaikki ihmiset, jotka tulevat käyttämään tätä työkalua, käyttävät samaa käyttöjärjestelmää ja ympäristöä, voit hyödyntää koko ekosysteemiä mahdollisimman joustavasti. Jos sinun on kuitenkin toteutettava komentorivisovelluksesi toimimaan useissa eri käyttöjärjestelmissä, olet nyt rajoittanut käyttämäsi työkalut niihin, jotka ovat käytettävissä kaikissa näissä järjestelmissä – tai sinun on toteutettava sovelluksessasi poikkeuksia kutakin käyttöjärjestelmää varten. (Se voi olla hyvin työlästä tutkia ja testata.)
Useimmiten käytetty ratkaisu erilaisten käyttöjärjestelmäympäristöjen käsittelyyn on välttää käyttöjärjestelmäkohtaisia ulkoisia työkaluja mahdollisuuksien mukaan tai siirtää vastuu kirjastoille, jotka ovat jo tehneet kovan työn toteuttaakseen ominaisuuksia, jotka toimivat useilla arkkitehtuureilla. Mitä enemmän pystyt pitämään toteutuksen yhteen ydinkieleen ilman riippuvuutta ulkoisista riippuvuuksista, sitä helpompi on ylläpitää projektiasi.
Komentorivisovelluksen komponentit Rubyssä
Komentorivisovellusta rakennettaessa on kolme huolenaihetta: tulo, lähtö ja kaikki siltä väliltä. Jos kehität kertaluonteista komentorivityökalua, joka vain ottaa syötteen tai lähteen, käsittelee/muotoilee sen ja sylkee tiedon ulos, työsi on melko yksinkertaista. Jos kuitenkin kehität käyttöliittymää, jossa on navigoitavia valikoita, asiat alkavat muuttua monimutkaisemmiksi.
Syötteen käsittely
Syötteen käsittelyn aloittamiseksi meillä on parametreja, jotka voidaan antaa sovelluksellesi. Nämä argumentit ovat tyypillisin tapa aloittaa komento, jossa on yksityiskohtia siitä, miten haluat sen suoritettavan ja välttää valikkojärjestelmien tarvetta. Tässä esimerkissä:
ruby -e "puts RUBY_VERSION"
Ruby on suoritettava komentoriviohjelma, -e
kutsutaan lipuksi ja "puts RUBY_VERSION"
on lipulle annettu arvo. Tässä tapauksessa Rubyn lippu -e
tarkoittaa, että seuraava suoritetaan Ruby-koodina. Kun suoritan yllä olevan komentorivillä, saan takaisin 2.4.0
tulostettuna standarditulosteeseen (joka yksinkertaisesti näkyy seuraavalla rivillä).
Argumenttisyöte
Argumenttisyöte eli parametrit ovat kaikki ylimääräiset tekstinpätkät, jotka syötetään komennon jälkeen ja erotetaan välilyönnillä. Lähes kaikki komennot sallivat help flag -argumentin. Lippuargumentin edessä on viiva tai kaksi.
Vakiomuotoiset apuliput ovat -h
ja --help
. MSDOS-aikoina se oli (ja saattaa yhä olla) /?
. En tiedä, onko lippujen trendi Windowsissa jatkanut eteenpäin viivaimen käyttöä lippumerkkinä, mutta alustarajat ylittävät komentoriviskriptit käyttävät nykyään viivaimia.
Rubyssä komentorivin syötteet sijoitetaan kahteen eri muuttujaan: ARGV ja ARGF. ARGV käsittelee syöttöparametrit merkkijonojen joukkona; ARGF on tarkoitettu tietovirtojen käsittelyyn. Voit käyttää ARGV:tä suoraan parametreille, mutta se voi olla enemmän työtä kuin sinun tarvitsee tehdä. On olemassa pari Ruby-kirjastoa, jotka on rakennettu komentoriviargumenttien käsittelyyn.
OptionParser
OptionParserin etuna on se, että se sisältyy Rubyyn, joten se ei ole ulkoinen riippuvuus. OptionParser antaa sinulle helpon tavan sekä näyttää, mitä komentorivivaihtoehtoja on käytettävissä, että käsitellä syötteet haluamiksesi Ruby-objekteiksi. Tässä on ote yhdestä komentorivityökalustani dfm:
require 'optionparser'options = {}printers = Array.newOptionParser.new do |opts| opts.banner = "Usage: dfm \nDefaults: dfm -xd ." + File::SEPARATOR opts.on("-f", "--filters FILTERS", Array, "File extension filters") do |filters| options = filters end opts.on("-x", "--duplicates-hex", "Prints duplicate files by MD5 hexdigest") do |dh| printers << "dh" end opts.on("-d", "--duplicates-name", "Prints duplicate files by file name") do |dh| printers << "dn" end opts.on("-s", "--singles-hex", "Prints non-duplicate files by MD5 hexdigest") do |dh| printers << "sh" end opts.on("-n", "--singles-name", "Prints non-duplicate files by file name") do |dh| printers << "sn" endend.parse!
Tässä esimerkissä jokaisessa opts.on
-lohkossa on koodia, joka suoritetaan, jos lippu on annettu komentorivillä. Neljä alimmaista vaihtoehtoa (lohkonsa sisällä) yksinkertaisesti liittävät lipun tiedot matriisiin, jota voin käyttää myöhemmin.
Ensimmäisessä on Array
annettu yhtenä parametrina metodille on
, joten tämän lipun syöttö muunnetaan Rubyn matriisiin ja tallennetaan hashiin nimeltä options
avaimella filters
.
Metodille on
annetut loput parametrit ovat lipun yksityiskohdat ja kuvaus. Sekä lyhyt että pitkä lippu toimivat seuraavassa lohkossa annetun koodin suorittamiseen.
OptionParserissa on myös -h
– ja --help
-liput oletusarvoisesti sisäänrakennettuna, joten sinun ei tarvitse keksiä pyörää uudelleen. Kirjoita vain dfm -h
yllä olevalle komentorivityökalulle, ja se tulostaa nätisti hyödyllisen kuvauksen:
Usage: dfm Defaults: dfm -xd ./ -f, --filters FILTERS File extension filters -x, --duplicates-hex Prints duplicate files by MD5 hexdigest -d, --duplicates-name Prints duplicate files by file name -s, --singles-hex Prints non-duplicate files by MD5 hexdigest -n, --singles-name Prints non-duplicate files by file name
Slop
OptionParser on hiukan suurpiirteinen kirjoittaessaan komentorivimäärittelyjä. Helmi Slop on suunniteltu niin, että voit kirjoittaa oman komentorivin jäsennyksen paljon vähemmällä vaivalla. Sen sijaan, että sinun täytyisi antaa koodilohkot lippumääritelmässä, Slop luo yksinkertaisesti objektin, jota voit kysyä sovelluksessasi nähdäksesi, onko lippu annettu ja mitä arvoa tai arvoja sille on mahdollisesti annettu.
# Excerpt from https://github.com/leejarvis/slopopts = Slop.parse do |o| o.string '-h', '--host', 'a hostname' o.integer '--port', 'custom port', default: 80 o.bool '-v', '--verbose', 'enable verbose mode' o.bool '-q', '--quiet', 'suppress output (quiet mode)' o.bool '-c', '--check-ssl-certificate', 'check SSL certificate for host' o.on '--version', 'print the version' do puts Slop::VERSION exit endendARGV #=> -v --host 192.168.0.1 --check-ssl-certificateopts #=> 192.168.0.1opts.verbose? #=> trueopts.quiet? #=> falseopts.check_ssl_certificate? #=> trueopts.to_hash #=> { host: "192.168.0.1", port: 80, verbose: true, quiet: false, check_ssl_certificate: true }
Tämä yksinkertaisuus voi yksinkertaistaa koodipohjaasi ja testisarjaasi sekä nopeuttaa kehitystyötäsi.
Tulostus (STDOUT)
Kirjoittaessasi yksinkertaista komentorivityökalua haluat usein, että se tulostaa suoritetun työn tulokset. Kun pitää mielessä, mikä on työkalun tavoite, se määrittää pitkälti sen, miltä haluat ulostulon näyttävän.
Voit muotoilla ulostulodatan yksinkertaiseksi merkkijonoksi, listaksi, hashiksi, sisäkkäisiksi matriiseiksi, JSON:ksi, XML:ksi tai muuksi hyödynnettäväksi tietomuodoksi. Jos datasi suoratoistetaan verkkoyhteyden kautta, haluat pakata datan tiiviiksi merkkijonoksi. Jos se on tarkoitettu käyttäjän silmien nähtäväksi, haluat laajentaa sen esittelykelpoiseksi.
Monet nykyiset Linux/Mac-komentorivityökalut voivat tulostaa tietoja pareittain tai arvojoukkoina. Tiedot voidaan erottaa toisistaan kaksoispisteellä, välilyönneillä, tabulaattoreilla ja sisennyslohkoilla. Kun et ole varma siitä, miten tarkalleen ottaen sitä voidaan käyttää, valitse yksinkertaisin ja esityskelpoisin tapa esittää tiedot.
Yksi esimerkki kohteesta, jota kannattaa ehkä harkita, on API-testausväline. Monet API:t tarjoavat JSON-vastauksen, ja niitä voidaan käyttää komentorivityökalulla, kuten curl
. Jos kaistanleveys on ongelma, käytä JSONin to_json
-menetelmää, mutta jos se on tarkoitettu paikalliseen konetyöskentelyyn, käytä pretty_generate
.
x = {"hello" => "world", this: {"apple" => 4, tastes: "delicious"}}require 'json'puts x.to_json# {"hello":"world","this":{"apple":4,"tastes":"delicious"}}puts JSON.pretty_generate( x )# {# "hello": "world",# "this": {# "apple": 4,# "tastes": "delicious"# }# }
Voit käyttää YAML:ää vastaavalla tavalla dataa varten.
require 'yaml'puts x.to_yaml# ---# hello: world# :this:# apple: 4# :tastes: delicious
Jos haluat monimutkaisemman tulosteen nätisti muotoiltuna, suosittelen lämpimästi awesome_print-helmeä. Tämä antaa sinulle tarkan hallinnan esitystavan tulostukseen.
Standardivirhe (STDERR)
Tämä on toinen tulostustyyppi, jota voi esiintyä komentorivisovelluksista. Kun jokin on vialla tai on mennyt pieleen, on tavallista, että komentorivityökalu kirjoittaa STDERR-nimellä tunnettuun tulosteeseen. Se näkyy tavallisena tulosteena, mutta muut työkalut voivat tarkistaa, että komento ei onnistunut.
STDERR.puts "Oops! You broke it!"
Yleisempi käytäntö on käyttää Loggeria, joka kirjoittaa tarkat tiedot virheistä. Voit ohjata sen komentorivin STDERR-ulostuloon tai mahdollisesti lokitiedostoon.
Käyttäjän käyttöliittymä (STDIN ja STDOUT)
Käyttäjän käyttöliittymän kirjoittaminen voi olla yksi palkitsevimmista tehtävistä. Sen avulla voit soveltaa jonkin verran taiteellista suunnittelua ja tarjota käyttäjälle erilaisia tapoja toimia vuorovaikutuksessa.
Minimaalinen käyttöliittymä on jonkinlaisen tekstin näyttö, jossa on kehote odottamassa syötettä. Se voi näyttää niinkin yksinkertaiselta kuin:
Who is your favorite super hero /Batman/Wonder-Woman ?
Yllä oleva osoittaa kysymyksen, vaihtoehdot ja oletusvaihtoehdon, jos käyttäjä päättää painaa enter-näppäintä syöttämättä mitään. Tavallisella Ruby-kielellä tämä näyttäisi seuraavalta:
favorite = "Superman"printf "Who is your favorite hero /Batman/Wonder Woman?"input = gets.chompfavorite = input unless input.empty?
Tämä on hyvin karkeaa koodia syötteeseen ja tulosteeseen, ja jos aiot kirjoittaa käyttöliittymää, suosittelen lämpimästi kokeilemaan highlinen tai tty:n kaltaisia helmiä.
Helmen highline avulla voisit kirjoittaa ylläolevan seuraavasti:
require 'highline/import'favorite = ask("Who is your favorite hero Superman/Batman/Wonder Woman?") {|question| question.in = question.default = "Superman"}
Tässä highline näyttää kysymyksen, näyttää oletusarvon ja nappaa kaikki virheellisesti syötetyt vaihtoehdot ja ilmoittaa käyttäjälle, että hän ei ole valinnut jotakin annetuista vaihtoehdoista.
Kummassakin highline- ja tty-versiossa on valtavasti ylimääräisiä juttuja, joita voit tehdä valikoinnille ja käyttäjäkokemuksen parantamiselle, esimerkiksi lisäämällä värejä näyttöön. Mutta jälleen sinun on otettava huomioon, kuka on kohderyhmäsi.
Mitä enemmän visuaalista käyttökokemusta tarjoat, sitä enemmän sinun on kiinnitettävä huomiota näiden työkalujen cross-platform-ominaisuuksiin. Kaikki komentorivit eivät käsittele samaa esitystietoa samalla tavalla, mikä luo huonon käyttökokemuksen.
Yleisalustayhteensopivuus
Hyvä uutinen on se, että Rubyssä on paljon ratkaisuja, joita tarvitaan työkalujen käyttämiseen eri käyttöjärjestelmissä. Kun Ruby käännetään jollekin tietylle käyttöjärjestelmälle, RbConfigin lähdetiedosto luodaan hash-muodossa tallennetuilla absoluuttisilla arvoilla, jotka ovat natiiveja sille järjestelmälle, jolle se on rakennettu. Se on siis avain käyttöjärjestelmän ominaisuuksien havaitsemiseen ja käyttämiseen.
Voidaksesi nähdä tämän tiedoston suosikkitekstieditorillasi voit ajaa seuraavan Ruby-koodin:
editor = "sublime" # your preferred editor hereexec "#{editor} #{RbConfig.method(:ruby).source_location}"
Tällöin näet kaiken esityskelpoisessa muodossa, mikä on mielestäni parempi kuin hashin näkeminen RbConfig::CONFIG
:n kautta. Tämä hash sisältää hyödyllisiä asioita, kuten tiedostojärjestelmän käsittelyyn käytetyt komennot, Rubyn version, järjestelmän arkkitehtuurin, missä kaikki Rubylle tärkeä sijaitsee, ja muita tällaisia asioita.
Käyttöjärjestelmän tarkistamiseksi voit tehdä:
case RbConfig::CONFIGwhen /mingw32|mswin/ # Probably a Windows operating systemelse # Most likely a Unix compatible system like BSD/Mac/Linuxend
Muut käyttöjärjestelmäkohtaiset arvot voit katsoa Gem::Platformin lähdekoodista.
Nyt tänne tallennettuja tiedostojärjestelmäkohtaisia komentoja ei ole tarkoitus käyttää tästä resurssista. Rubyssä on sitä varten kirjoitettu luokat Dir, File ja Pathname.
Kun haluat tietää, onko suoritettava tiedosto olemassa järjestelmän PATH-luettelossa, kannattaa käyttää MakeMakefile.find_executable. Ruby tukee C-laajennusten rakentamista ja yksi hieno ominaisuus, jonka he lisäsivät sitä varten, on tämä kyky selvittää, onko suoritettava tiedosto olemassa, jota kutsua.
Mutta tämä luo lokitiedoston järjestelmän nykyiseen hakemistoon joka kerta, kun suoritat sen. Välttääksesi tämän lokitiedoston kirjoittamisen sinun on siis tehtävä seuraavaa:
require 'mkmf'MakeMakefile::Logging.instance_variable_set(:@log, File.open(File::NULL, 'w'))executable = MakeMakefile.find_executable('clear') # or whatever executable you're looking for
Kun piirrät visuaalisen ikkunavalikon komentoriville, on suotavaa, että valikko pysyy kiinteässä asennossa, kun näyttö päivittyy. Yksinkertaisin tapa tehdä tämä on tyhjentää komentorivi jokaisen koko näytön kirjoittamisen välissä.
Yllä olevassa esimerkissä etsin clear
-komentoa. Windowsissa komentorivin tyhjentämiseen tarkoitettu komentotulkkikomento on cls
, mutta et löydä sille suoritettavaa komentoa, koska se on osa command.com
:n sisäistä koodia.
Hyvä uutinen on, että Linuxin clear
-komennon merkkijonotulosteen nappaaminen tuottaa tämän pakokoodisarjan: \e[3J\e[H\e[2J
. Minä ja muut olemme testanneet tätä Windowsissa, Macissa ja Linuxissa, ja tämä tekee juuri sen, mitä haluamme: tyhjentää ruudun, jotta se voidaan piirtää uudelleen.
Tässä escape-merkkijonossa on kolme erilaista toimintoa, jotka se suorittaa:
CLEAR = (ERASE_SCOLLBACK = "\e[3J") + (CURSOR_HOME = "\e[H") + (ERASE_DISPLAY = "\e[2J")
On kuitenkin parempi käyttää komentorivikirjastoa kuin kirjoittaa escape-koodit itse. Tämä on kuitenkin mainitsemisen arvoinen.
Testaus STDIN/STDOUT/STDERR
Mitä tahansa, joka kirjoittaa johonkin STDOUTin tai STDERR:n kaltaiseen tiedostoon, olisi viisainta luoda UI-Ruby-objektiin sisäinen muuttuja, jota voidaan muuttaa valinnaisella parametrilla new
. Kun ohjelmasi normaalisti suoritetaan, arvo on siis STDOUT
. Mutta kun kirjoitat testejä, välität muuttujan StringIO.new
, jota vastaan voit helposti testata.
Kun yrität lukea IO:ta komennosta, joka suoritetaan Rubyn ulkopuolella, testaus on hieman monimutkaisempaa. Sinun täytyy todennäköisesti tutustua Open3.popen3:een, jotta voit käsitellä kutakin IO-virtaa STDIN, STDOUT ja STDERR.
Bash-komentojen käyttäminen
Bash on tullut Windowsiin (WSL)! Tämä tarkoittaa, että voit käyttää bash-komentoja suurimmassa osassa maailmanlaajuisesti käytettyjä käyttöjärjestelmiä. Tämä avaa lisää mahdollisuuksia omalle komentorivityökalullesi.
Tyypillistä on ”putkittaa” komentoja toistensa läpi, jolloin yhden komennon ulostulo lähetetään virtana seuraavaan komentoon. Tämän tietäen sinun kannattaa ehkä harkita streaming-syötteen tuen lisäämistä tai miettiä, miten voit muotoilla tulostuksesi paremmin muiden komentorivityökalujen kulutusta varten.
Tässä on esimerkki regex-substituutiosta, jossa Bashin sed
-komento sed
saa syötteensä putkistettuna echosta:
echo hello | sed "s/ll/ck n/g"
Tämä tuottaa tulosteen heck no
. Mitä enemmän opit Bashista, sitä paremmat valmiudet sinulla on saada asioita aikaan komentorivillä ja sitä paremmat valmiudet sinulla on kirjoittaa parempia omia komentorivityökaluja.
Yhteenveto
Oman komentorivityökalun kirjoittamisessa on paljon opittavaa. Mitä enemmän tilanteita joudut ottamaan huomioon, sitä monimutkaisemmiksi asiat muuttuvat.
Varaa aikaa oppiaksesi käsilläsi olevat työkalut, niin saat monia palkintoja, joista et tiennyt jääväsi paitsi. Toivotan sinulle kaikkea hyvää!