mandag 24. desember 2012 Julekalender Linux Ruby
Thomas Kjeldahl Nilsson (@thomanil) er blant annet mannen bak Mine Verktøy, hvor han i drøyt et år publiserte en rekke intervjuer hvor norske geeks fortalte hvilket verktøysoppsett de hadde. Dagens blogpost – på selveste julaften – følger i det samme sporet. Thomas snakker om noe som er veldig viktig for en effektiv utvikler; kunsten å lage sine egne verktøy!
Thomas blogger også på kjeldahlnilsson.net.
Hvem er du?
Trodde jeg skulle bli kjemiker på Blindern - men fant ut at jeg hadde mye lettere for programmering, gitt!
Hva er jobben din?
Partner og programmerer hos Gitorious AS - vi utvikler en fullstendig open source løsning for Git-hosting, og lever av å selge hosting og knallgod support til kunder over hele verden.
Hva kan du?
Pragmatisk allrounder som er spesielt glad i entreprenørskap, web-teknologi... og Emacs.
Hva liker du best med yrket ditt?
Å kunne skape noe ut av ingenting.
Jeg er fascinert av smiekunst.
Tradisjonelle smeder jobber med enkle, grunnleggende verktøy: esse, ambolt, hammer og tang. Smeden kan imidlertid lage mer effektive og spesialiserte verktøy ved behov. Det er såvidt jeg vet ingen andre håndtverkere som kan trekke seg selv opp etter bukseselene på denne måten…
… unntatt oss programmerere. Med gode basis-verktøy kan vi bygge alt vi trenger for å jobbe raskere og mer effektivt.
I denne artikkelen skal vi se hvordan Unix-miljøer enkelt lar oss bruke basisverktøy til å bygge nye verktøy og arbeidsflyter. Vi jobber på tre nivåer: rett på kommandolinja, med shellscripting, og med Ruby.
offlineimap
og msmtp
Et av de beste utvikler-verktøyskrinene finner du i Unix-systemene (Linux, OS X, BSD, mfl): der får du mange små, fokuserte og gjenbrukbare byggeklosser du kan sette sammen til nye programmer. Med andre ord: de fleste verktøy du trenger enten finnes allerede, eller kan lages ved å kombinere allerede eksisterende verktøy.
Det er nyttig å omfavne — eller i det minste forstå — Unix-kulturen, for å absorbere en del prinsipper som har gjennomsyret denne tradisjonen gjennom flere tiår. Eric S Raymond oppsummerer disse prinsippene slik:
Disse prinsippene gjør deg til en bedre utvikler selv om du ellers ikke bruker Unix-systemer i det hele tatt.
Den beste måten å få dette til å synke inn er å bruke Unix-verktøy til å bygge noe konkret. Vi skal derfor bruke grunnleggende Unix-programmer til å bygge en enkel arbeidsflyt for å jobbe med Gmail-innboksen din.
offlineimap
og msmtp
Fundamentet for emailklienten vår blir verktøyene offlineimap
(for å hente mail) og msmtp
(for å sende mail).
Når disse er installert kommer vi til å ha en Maildir
katalog som er synkronisert med Gmail-kontoen vår via IMAP, og vi kommer til å ha en enkel kommando for å sende mail rett fra kommandolinja. Begge verktøyene er tilgjengelig på både Linux, OS X og andre Unix-varianter.
Hvis du vil følge eksemplene mine på din egen maskin så bør du få dette til å fungere først. Hvis du bare vil lese resten av artikkelen kan du hoppe over denne seksjonen.
Merk: Jeg har bare testet oppsettet beskrevet nedenfor i Ubuntu --- andre Unix-varianter kan være litt forskjellige.
I Ubuntu installerer du offlineimap
slik:
sudo apt-get install offlineimap
Vi oppretter så en lokal Maildir
katalog der vi vil at offlineimap
skal legge den lokale kopien av gmail kontoen vår:
mkdir ~/Maildir
For å bruke verktøyet må vi konfigurere det til å snakke med vår egen gmail-konto. Vi oppretter fila ~/.offlineimaprc
, og setter korrekte skrive/lese-rettigheter:
touch ~/.offlineimaprc && chmod 600 ~/.offlineimaprc
Vi fyller den med følgende innhold (oppdater til å matche din egen Gmail-konto):
[general] accounts = gmail # Kutt ut all output, med unntak av feilmeldinger ui = quiet [Account gmail] localrepository = gmail-local remoterepository = gmail-remote status_backend = sqlite [Repository gmail-local] type = Maildir localfolders = ~/Maildir/Gmail [Repository gmail-remote] type = Gmail remoteuser = DIN_GMAIL_EMAIL_ADRESSE remotepass = DITT_GMAIL_PASSORD # Vi henter kun ned selve innboksen, ikke resten av folderne folderfilter = lambda folder: folder in ['INBOX'] # Sletting lokalt skal ikke medføre sletting i Gmail-kontoen realdelete = no
La oss teste om verkøyet fungerer som det skal. Kjør kommandoen offlineimap
. Hvis konfigurasjonen din er korrekt så vil hele innboksen din plukkes ned og legges i katalogen Maildir
i rota av hjemmeområdet ditt. Vær tålmodig: dette kan ta en stund hvis du har mye liggende i selve innboksen i gmail.
I Ubuntu installerer du msmtp
-pakken slik:
sudo apt-get install msmtp
Også dette verktøyet må konfigureres: vi oppretter fila ~/.msmtprc
, og gir den korrekte skrive/lese rettigheter:
touch ~/.msmtprc && chmod 600 ~/.msmtprc
Vi fyller den med følgende innhold (oppdater til å matche din egen Gmail-konto):
defaults auth on tls on account gmail host smtp.gmail.com port 587 from DIN_GMAIL_EMAIL_ADRESSE user DIN_GMAIL_EMAIL_ADRESSE password DITT_GMAIL_PASSORD tls_trust_file /etc/ssl/certs/ca-certificates.crt
La oss teste om verkøyet fungerer som det skal. Kjør følgende kommando (bare bruk din egen mailadresse):
echo 'tester msmtp...' | msmtp -a gmail 'thomas@kjeldahlnilsson.net'
Sjekk så emailen din som vanlig. Dersom msmpt
ble konfigurert riktig så skal du ha mottatt en mail fra deg selv nå med innholdet "tester msmtp…"
Fungerer alt som det skal? Flott, da fortsetter vi.
Etter at vi har kjørt offlineimap
første gang ender vi opp med en katalog ~/MailDir/Gmail
, som inneholder underkataloger som representerer mapper og tilstander i hver mailfolder. Hver mail i disse folderne er lagret som en vanlig flatfil.
Folderstrukturen ser omtrent slik ut:
Maildir/ └── Gmail └── INBOX ├── cur │ └── 1352560840_1.25056.localhost,U=2,FMD5=7e33429f656f1e6e9d79b29c3f82c57e:2,S ├── new │ ├── 1352560840_0.25056.localhost,U=1,FMD5=7e33429f656f1e6e9d79b29c3f82c57e:2, │ └── 1352560841_0.25056.localhost,U=3,FMD5=7e33429f656f1e6e9d79b29c3f82c57e:2, └── tmp
Filnavnene ser litt kryptiske ut, fordi offlineimap
bruker filnavnene til å lagre en del informasjon om hver email: unike IDer, checksums etc. Filene under cur
katalogen inneholder leste email, mens uleste mailer ligger under new
.
Tilstand i denne lokale mailboksen synkroniseres til gmail-kontoen vår hver gang vi kjører offlineimap
kommandoen. For eksempel kan vi flytte en email fra new
til cur
mappen og så kjøre offlineimap
. Dette vil sette statusen på email til "lest" i Gmail-kontoen sentralt også.
Innholdet av en email ser ut omtrent slik:
MIME-Version: 1.0 Received: by 10.112.4.227; Sat, 10 Nov 2012 07:18:48 -0800 (PST) Date: Sat, 10 Nov 2012 07:18:48 -0800 Message-ID: <CABQd01N+VFn89guaFCcBu+x=rJudno+yarZPZF1=CqWDz=sQnw@mail.gmail.com> Subject: Import your contacts and old email From: Gmail Team <mail-noreply@google.com> To: Kensei Test Account <kensei.test@gmail.com> Content-Type: multipart/alternative; boundary=00151748de9ec3315804ce259570 --00151748de9ec3315804ce259570 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable You can import your contacts and mail from Yahoo!, Hotmail, AOL, and many other web mail or POP accounts. If you want, we'll even keep importing your mail for the next 30 days. Import contacts and mail =BB<https://mail.google.com/mail/#settings/accounts> We know it can be a pain to switch email accounts, and we hope this makes the transition to Gmail a bit easier. - The Gmail Team Please note that importing is not available if you're using Internet Explorer 6.0. To take advantage of the latest Gmail features, please upgrad= e to a fully supported browser<http://support.google.com/mail/bin/answer.py?answer=3D6557&hl=3Den&= utm_source=3Dwel-eml&utm_medium=3Deml&utm_campaign=3Den> . --00151748de9ec3315804ce259570 Content-Type: text/html; charset=ISO-8859-1 <html> <font face="Arial, Helvetica, sans-serif"> <p>You can import your contacts and mail from Yahoo!, Hotmail, AOL, and many other web mail or POP accounts. If you want, we'll even keep importing your mail for the next 30 days.</p> <table cellpadding="0" cellspacing="0"> <col style="width: 1px" /><col /><col style="width: 1px" /> <tr> <td></td> <td height="1px" style="background-color: #ddd"></td> <td></td> </tr> <tr> <td style="background-color: #ddd"></td> <td background="https://mail.google.com/mail/images/welcome-button-background.png" style="background-color: #ddd; background-repeat: repeat-x" ><a href="https://mail.google.com/mail/#settings/accounts" style="font-weight: bold; color: #000; text-decoration: none; display: block; padding: 0.5em 1em" >Import contacts and mail »</a></td> <td style="background-color: #ddd"></td> </tr> <tr> <td></td> <td height="1px" style="background-color: #ddd"></td> <td></td> </tr> </table> <p>We know it can be a pain to switch email accounts, and we hope this makes the transition to Gmail a bit easier.</p> <p>- The Gmail Team</p> <p><font size="-2" color="#999">Please note that importing is not available if you're using Internet Explorer 6.0. To take advantage of the latest Gmail features, please <a href="http://support.google.com/mail/bin/answer.py?answer=6557&hl=en&utm_source=wel-eml&utm_medium=eml&utm_campaign=en"><font color="#999"> upgrade to a fully supported browser</font></a>.</font></p> </font> </html> --00151748de9ec3315804ce259570--
Siden mailboksen vår er representert med vanlige kataloger, filer og strenger så kan vi bruke standard Unix-verktøy for å lese og manipulere email fra kommandolinja. Nå kan vi begynne å leke med verktøykassa vår!
Unix-verktøy følger noen felles konvensjoner for å ta imot data og og sende resultater ut.
Programmer kjørt på kommandolinja tar data inn på STDIN
strømmen, og spytter resultater ut igjen på strømmen STDOUT
(med unntak av feilmeldinger , som spyttes ut på STDERR
strømmen). Program-output ender enten i terminalen, eller kan sendes videre til et annet program.
Siden disse felles konvensjonene finnes kan vi kombinere programmer: Ved å kjede sammen flere verktøy på samme linje med |
(pipe) mellom hver av dem, kan vi la data flyte gjennom dem etter tur som i et vannrør. Vi kan med andre ord enkelt bygge verktøy som består av pipelines av andre kommandoer.
Et eksempel: denne linja skrev jeg forrige uke for å finne alle sannsynlige synkroniserings-konflikter i Dropbox-mappa mi.
find ~/Dropbox | grep conflicted
Find-kommandoen lister opp alle filer, rekursivt, under den angitte katalogen. Hvert filnavn pipes videre til grep
-kommandoen, som beholder de filstiene som har inneholder 'conflicted'. Listen av filnavn ender så i terminalen min slik at jeg kan se hvilke filer jeg antagelig bør rydde vekk. Det finnes mer optimale måter å gjøre denne oppgaven, men poenget er at det tok meg bare noen sekunder å lage denne automatiseringen.
Å kjede sammen pipelines av filtere, transformasjoner etc føles svært naturlig dersom du allerede er komfortabel med å bruke map, filter og reduce-operasjoner i "ordentlige" programmeringsspråk.
La oss bygge noen verktøy for email.
Denne har du kanskje allerede kjørt for å teste at msmtp
fungerer.
echo 'Dette er body i en mail jeg sender fra terminalen' | msmtp -a gmail 'dinadresse@gmail.com'
echo
skriver ut den påfølgende teksten til STDOUT
. Hvis den står for seg selv så får vi teksten tilbake ut i terminalen. Her piper vi istedet teksten til msmtp
, som bruker teksten den får inn på STDIN
til som body i en ny mail.
Følgende linje teller hvor mange uleste email vi har.
find ~/Maildir/Gmail/INBOX/new -type f | wc -l
Vi finner alle filer i new
folderen (kun filer, ikke kataloger etc), og bruker wc
til å telle hvor mange av dem det var. Jeg synes det er litt knapt å bare dumpe ut tallet, så jeg legger til litt beskrivende tekst også:
echo "Unread emails: $(find ~/Maildir/Gmail/INBOX/new -type f | wc -l)"
Nå har vi alt vi trenger for å lage en liten "widget" for å vise antall uleste email. Vi gjør det ved å ha et lite terminalvindu på desktopen vår, der vi kjører følgende kommando for å sette den igang:
watch -n10 'offlineimap && echo "Unread emails: $(find ~/Maildir/Gmail/INBOX/new -type f | wc -l)"'
watch
kjører de påfølgende kommandoene hvert tiende sekund — vi synkroniserer først mailen vår, så kjører vi programsnutten vår for å telle uleste meldinger.
Nå har vi et terminalvindu som hele tiden ser slik ut, og blir oppdatert hvert 10 sekund:
Every 10.0s: offlineimap && echo "Uleste mail: $(find ~/Maildir/Gmail/INBOX/new -type f | wc -l)" Fri Nov 30 21:14:11 2012
Uleste mail: 1
En naturlig start er å lage oss et sammendrag av hvilke mail vi har liggende i innboksen vår. Her er en måte å skrive ut en liste over mailene våre:
grep -Rh ^Subject: ~/Maildir/Gmail/INBOX
Vi søker rekursivt (R) i innboks-folderen vår etter tilfeller av <Linjestart> Subject:. H-en er der for å kun skrive ut subjekt-linjen, ikke det kryptiske filnavnet i tillegg (grep
skriver ellers ut både filnavn og hvordan treffet ser ut).
Dette bør treffe en gang i hver fil (vi går ut fra at dette er standardformatet på mailfilene og at det forhåpentligvis bare er en slik linje i hver fil). Disse matchende emne-linjene skrives ut til terminalen vår, og ser omtrent slik ut når vi kjører kommandoen vår:
➜ ~ grep -Rh ^Subject: ~/Maildir/Gmail/INBOX Subject: Get Gmail on your mobile phone Subject: Import your contacts and old email Subject: Customize Gmail with colors and themes
Nå har vi oversikt over innboksen vår.
Vi bør også kunne lese en bestemt mail. Denne linja gjør det mulig å dumpe ut innholdet av mail nummer N fra lista ovenfor, telt fra toppen. Denne er noe mer innfløkt:
find ~/Maildir/Gmail/INBOX -type f | sed -n 2p | xargs cat
Vi finner alle filer rekursivt under innboksen vår. Vi plukker den nte linja i fil-lista (i dette tilfelle nummer to), og sender fil-stien til cat
kommandoen, som bare dumper ut innholdet av den gitte fila i terminalen vår.
Disse email-kommandoene vi har laget her fungerer jo helt greit, men de er ikke så smidige å jobbe med hvis du trenger å bruke dem raskt og ofte. Det er på tide å ty til shellscripting for å forenkle og gjenbruke.
Jeg har problemer med å huske hendige terminal-snutter den første gangen jeg bruker dem. Her er noen grep som hjelper:
Ctrl-r
. Hvis du begynner å skrive får du opp første mulige treff, og kan trykke pil-opp for å hoppe til neste kandidat som matcher søket bakover i historikken din. Og sørg for at terminalen din er satt til å bevare mye, eller all, historikk bakover i tid!
Bash
så oppretter eller oppdaterer du fila ~/.bashrc
, og kan legge til linjer på denne formen: alias helloworld="echo 'hello world'"
Når du har lastet miljøet på nytt vil du kunne bruke aliaset som en hvilken som helst annen kommando/script. Vi kan for eksempel forenkle en av linjene vi skrev ovenfor:
alias min_innboks="grep -Rh ^Subject: ~/Maildir/Gmail/INBOX"
Nå blir det litt enklere å sjekke mailboksen:
➜ ~ min_innboks Subject: Get Gmail on your mobile phone Subject: Import your contacts and old email Subject: Customize Gmail with colors and themes
Vi kommer til et punkt der vi trenger mer faktisk programmering for å få ting gjort. Med andre ord: variable, conditionals, løkker, og ikke minst muligheten til å spre logikken over flere linjer.
La oss lage bash-scripts av operasjonene vi gikk gjennom i forrige seksjon. Da kan vi forbedre dem ved å gjøre dem tilgjengelige som kortere, parametriserte kommandoer.
All kode vi skriver her og i de neste seksjonene er forøvrig tilgjengelig for nedlasting.
Vi lager et script som heter send-email
, som tar mottaker og mail-tekst inn som parametre.
#!/bin/sh RECIPIENT=$1 TEXT=$2 echo $TEXT | msmtp -a gmail $RECIPIENT
Den aller første linja er en såkalt shebang som forteller hvordan scriptet skal eksekveres (i dette tilfellet sier vi at programmet er et shellscript). $1, $2 etc er variabler som bindes til å inneholde til shellscriptet. For lesbarhetens skyld tilegner vi dem til nye variable, før vi kjører samme kommando som vi gjorde ovenfor for å sende emailen.
Hvis du putter send-email
i din egen path kan du kjøre den slik:
send-email thomanil@gmail.com "Sent from a tiny shellscript"
Dette verktøyet vårt ble litt mer brukervennlig nå, ikke sant?
Kommandoen vår for å holde styr på antall uleste mail gjør vi om til et script som heter watch-unread-emails
, og ser slik ut:
#!/bin/sh
POLLING_INTERVAL=$1
watch -n$POLLING_INTERVAL 'offlineimap && echo "Unread emails: $(find ~/Maildir/Gmail/INBOX/new -type f | wc -l)"'
Kommandoen vår tar en parameter: antall sekunder mellom hver gang kommandoen skal kjøres/oppdateres. Slik kjøres den når scriptet er i pathen din:
watch-unread-emails 10
Vi lager en kommando som heter display-inbox
for å liste opp innholdet i innboksen vår.
#!/bin/sh grep -Rh ^Subject: ~/Maildir/Gmail/INBOX
Denne er bare den samme pipelinen vi tidligere skrev direkte inn i terminalen — bare mer brukervennlig siden vi slipper å huske den grep
-kommandoen.
Når scriptet er tilgjengelig i pathen din kan du eksekvere det slik:
display-inbox
Linja vi tidligere lagde for å lese innhold av en bestemt mail var rimelig knotete, så det blir fint å forenkle litt.
#!/bin/sh
MAIL_NUMBER=$1
SED_COMMAND=$(printf "sed -n %sp" $MAIL_NUMBER)
find ~/Maildir/Gmail/INBOX -type f | $SED_COMMAND | xargs cat
Scriptet tar "mail nummer N fra toppen" som argument. Vi velger å bygge opp sed
-kommandoen separat underveis for å gjøre det hele mer lesbart.
Når scriptet ligger i pathen din kan du kjøre det på følgende måte.
read-email 2
Helt klart enklere enn den kryptiske linja vi måtte skrive inn før.
Hvis du gjør terskelen for å skrive nye scripts så lav som mulig, så lager du også fler av dem og gjør derfor mer for å forbedre arbeidsflyten din. Her er to grep som hjelper:
~/bin
eller ~/scripts
. Legg til denne katalogen i pathen slik at scriptene dine er tilgjengelige i miljøet ditt overalt. Bonus-poeng: lag en git
-repo av script-katalogen din slik at har versjonskontroll, og hvis du jobber på flere maskiner, legg katalogen i Dropbox-folderen din og symlink dit fra alle maskinene du sitter på.
~/script/generatescript
bash-script. Det tar navn på nytt script som argument, oppretter det i standard-katalogen min (med riktige rettigheter), og fyrer opp standard-editoren min slik at jeg umiddelbart kan begynne å arbeide på det nye scriptet. #!/bin/sh SCRIPTPATH=~/scripts/$1 echo '#!/bin/sh # Generated, add code here ' >> $SCRIPTPATH touch $SCRIPTPATH chmod a+x $SCRIPTPATH $EDITOR $SCRIPTPATH
Perl kom til verden fordi Larry Wall syntes rå shellscripting ble for primitivt og lite ekspressivt. Etterhvert fikk vi også Ruby, Python, Groovy med fler, som sammen med Perl gjorde Unix-scripting langt mer behagelig.
Vi skal skrive om kommandoene våre til Ruby. Dette gir oss to umiddelbare gevinster: mer lesbare og utvidbare scripts og, for read-email
kommandoen vår, parsing av email via et eksternt Ruby-bibliotek.
Ingenting av det vi gjør i Ruby-koden er umulig å få til i vanlige shellscripts — men Ruby er mer lettlest og vedlikeholdbart.
Vi skriver om send-email
shellscriptet vårt til Ruby:
#!/usr/bin/env ruby if ARGV.length != 2 puts "Usage: send-email TO_ADDRESS EMAIL_BODY" exit 1 end recipient = ARGV[0] text = ARGV[1] puts `echo #{text} | msmtp -a gmail #{recipient}`
Vi starter scriptet med en ny "shebang"-linje, denne sier at systemet skal bruke ruby
kommandoen i brukerens miljø til å eksekvere koden.
Vi legger også til en validering av at riktig antall parametre sendes inn til kommandoen. Argumenter til Ruby-programmer blir lagt i en konstant, global array som heter ARGV
. Dersom det er for få eller for mange parametre skriver vi ut en kortfattet bruksanvisning og stopper videre kjøring.
Selve kjøringen av msmtp
sheller vi ut til systemet. Dette er fordelen med å bruke Ruby og lignende språk: vi kan delegere ned til det underliggende systemet når som helst. Vi kan derfor velge hvor mye vi vil lene oss på vanlige Unix-verktøy kontra scriptspråkets egne biblioteker — det er ikke en "enten-eller" situasjon.
Nestemann ut er watch-unread-emails
scriptet vårt.
#!/usr/bin/env ruby if ARGV.length != 1 puts "Usage: watch-unread-emails POLLING_INTERVAL_SECONDS" exit 1 end polling_interval = ARGV[0].to_i while true new_mail_dir = File.expand_path("~/Maildir/Gmail/INBOX/new/*") unread_count = Dir[new_mail_dir].count { |file| File.file?(file) } puts `clear && offlineimap` puts "Unread emails: #{unread_count}" sleep polling_interval end
Istedet for å lene oss på watch
kommandoen så implementerer vi bare tilsvarende logikk selv i Ruby: skriv ut antall uleste email hvert nte sekund.
Vi tømmer terminalen for innhold og synkroniserer email ved å shelle ut til clear
kommandoen og offlineimap
. Selve antallet email finner vi ved å bruke Rubys egne file-apier istedet for find
-kommandoen direkte.
Ruby-scriptet ble her en del lenger enn en det tilsvarende shellscriptet, fordi jeg kapper operasjonene opp i flere steg, variable og linjer. For meg føles dette noe mer utvidbart og lettlest enn det opprinnelige shellscriptet.
display-inbox
scriptet skriver vi bare om til Ruby for å være konsekvente: Ruby-versjonen sheller ut samme operasjon som tidligere. Jeg syntes at en enkelt grep
-linje var helt greit, og det illustrerer at Ruby
også kan fungere som en veldig tynn wrapper rundt ordinær shellscripting.
#!/usr/bin/env ruby puts `grep -Rh ^Subject: ~/Maildir/Gmail/INBOX`
Til slutt porter vi read-email
til Ruby-kode.
#!/usr/bin/env ruby if ARGV.length != 1 puts "Usage: read-email EMAIL_NO" exit 1 end #depends on the 'mail' gem, install like this: gem install mail require 'mail' maildir = File.expand_path("~/Maildir/Gmail/INBOX") all_email_filepaths = Dir["#{maildir}/**/*"].select { |f| File.file?(f) } mail_number = (ARGV[0].to_i)-1 mail_path = all_email_filepaths[mail_number] mail = Mail.read(mail_path) puts mail.text_part
Vi bruker Rubys fil-apier til å finne stien som inneholder den nte mailen fra toppen i innboksen. Så drar vi nytte av et eksternt Ruby-bibliotek (en såkalt gem) som heter Mail til å parse email-fila. Til slutt skrives mailen ut som html.
<html> <font face="Arial, Helvetica, sans-serif"> <p>You can import your contacts and mail from Yahoo!, Hotmail, AOL, and many other web mail or POP accounts. If you want, we'll even keep importing your mail for the next 30 days.</p> <table cellpadding="0" cellspacing="0"> <col style="width: 1px" /><col /><col style="width: 1px" /> <tr> <td></td> <td height="1px" style="background-color: #ddd"></td> <td></td> </tr> <tr> <td style="background-color: #ddd"></td> <td background="https://mail.google.com/mail/images/welcome-button-background.png" style="background-color: #ddd; background-repeat: repeat-x" ><a href="https://mail.google.com/mail/#settings/accounts" style="font-weight: bold; color: #000; text-decoration: none; display: block; padding: 0.5em 1em" >Import contacts and mail »</a></td> <td style="background-color: #ddd"></td> </tr> <tr> <td></td> <td height="1px" style="background-color: #ddd"></td> <td></td> </tr> </table> <p>We know it can be a pain to switch email accounts, and we hope this makes the transition to Gmail a bit easier.</p> <p>- The Gmail Team</p> <p><font size="-2" color="#999">Please note that importing is not available if you're using Internet Explorer 6.0. To take advantage of the latest Gmail features, please <a href="http://support.google.com/mail/bin/answer.py?answer=6557&hl=en&utm_source=wel-eml&utm_medium=eml&utm_campaign=en"><font color="#999"> upgrade to a fully supported browser</font></a>.</font></p> </font> </html>
Rå html-kode er ikke så lesbar, men vi kan kanskje bruke Firefox til å lese mailen?
read-email 2 > email.html && firefox email.html
Er det lurt å holde seg så til så enkle verktøy som mulig, eller bør du alltid hoppe rett til det høyeste abstraksjonsnivået i verktøykassa?
Vel, du kan bygge hva som helst bare du har et turing-komplett språk — se for eksempel denne implementasjonen av Tetris i sed
— men det er fint å kunne ta steget opp til mer ekspressive språk når vi har behov for det.
Fordelen med moderne scriptspråk som Ruby, Python etc er som nevnt over at de har mer behagelig syntax, og mange hendige eksterne biblioteker. De er også mer kryssplatform enn shellscripting, noe som gjør at du brått kan støtte mer enn bare Unix. For eksempel: ved å bruke Rubys fil-apier så kan scriptet ditt potensielt fungere på Windows også.
Ulempen med de moderne scriptspråkene er at de innfører flere eksterne avhengigheter: hvis du holder deg til shellscripting og standard Unix-verktøy så kan scriptet ditt fungere i minimale Unix-systemer uten å måtte installere flere eksterne pakker.
Det jeg ofte gjør når jeg skal skrive små programmer er å starte med noen enkle Unix-kommandoer i terminalen, og så trekke inn Ruby med en gang jeg ser at ting blir for komplekst for Bash
.
Når du plukker opp nye byggeklosser på denne måten så ser du stadig flere løsninger på problemer, og det er fristende å bygge masse greier selv hele tiden. Men: bare fordi du kan bygge hva du vil fra bunnen av, er det ikke alltid lurt å gjøre det.
Vi må velge kampene våre. Noen ganger er det mer pragmatisk å gå ut og kjøpe et kanskje komplekst, suboptimalt, lukket verktøy… som faktisk gjør jobben uten at du trenger å bygge alt selv.
Det kommer an på situasjonen, tenk deg om før du hopper!