Most recent comments
Liveblogg nyttårsaften 2017
Tor, 11 months, 2 weeks
Jogging og blogging
Are, 1 year, 11 months
Liveblogg nyttårsaften 2016
Are, 1 year, 11 months
Reading in dark times
Are, 2 years
Moldejazz 2016
Camilla, 2 years, 4 months
Dørskilt
Karoline, 2 years, 5 months
Halifax
Tor, 2 years, 5 months
Sony Smartwatch 3 review
Tor, 2 years, 6 months
Numerikk, takk
Tor, 2 years, 6 months
Topp tur
Camilla, 2 years, 8 months
50 book challenge
Camilla, 11 months, 2 weeks
Five years ago
Endelig \(\LaTeX\)-støtte
Tor
Controls
Register

commandline-fu

I artikkelen om Unix-filosofi hadde jeg egentlig tenkt å inkuldere et eksempel på hva man kan gjøre ved å lenke sammen programmer på kommandolinjen, men jeg kom ikke på et godt eksempel som virket som noe en vanlig person kunne finne på å gjøre. I dag fant jeg imidlertid et, så her følger en liten demonstrasjon.

Sett at en eller annen arrogant nisse har postet en post i et forum, der han dårlig tilslørt skryter av hvor mange ulike land han har lest bøker fra. Du vil gjerne jekke ham ned noen hakk ved å påpeke at det fortsatt finnes ganske mange land han ikke har lest bøker fra, og du ønsker naturligvis å inkludere en liste over disse landene. Så hvordan kan du få til det, uten å måtte manuelt sammenligne listen hans med listen over alle land som finnes? Trivielt, min kjære Watson.

Først må du kopiere listen hans, og lagre den i en tekstfil. Det regner jeg med du greier selv. For enkelhets skyld lagrer vi filen som larry.txt.

Dernest er det bare å åpne terminalen, og skrive
comm -13 <(cat larry.txt | sort) <(curl http://www.listofcountriesoftheworld.com/ | sed -n 's/.*">\([^<>]*\)<\/a><\/td>$/\1/p' | sort)

så har du listen over alle land han ikke har lest bøker fra. Temmelig smertefritt, om jeg får si det selv.

-Tor Nordam
Are likes this

Comments

Camilla,  23.02.11 00:30

For jeg husker at du lo av at jeg hadde brukt nesten en halv time på det. For fire timer siden.

Are,  27.02.11 17:24

Minuspoeng for at du ikke forklarer kommandoen. Når får vi "Regex med Tor"-artikkelen?!

Are,  27.02.11 17:24

(Hilsen en som burde lære seg regex skikkelig.)
Tor,  27.02.11 23:33

Altså.
larry.txt

er navnet på filen du lagret landene i. Du lagret dem naturligvis som en liste i ren tekst, med ett navn på hver linje.
cat larry.txt

skriver ut innholdet i filen.
|

sender output fra ett program som input til et annet, så
cat larry.txt | sort

skriver ut innholdet i filen, og sorterer det alfabetisk.
curl http://www.listofcountriesoftheworld.com/

skriver ut kildekoden til nettsiden http://www.listofcountriesoftheworld.com/, som har en liste over alle land i verden. Dessverre er ikke listen i ren tekst, så vi må trikse litt.
sed

er er stream editor, som betyr at den tar inn en strøm av tekst, endrer den, og sender den ut igjen. Vi kikker litt i kildekoden til nettsiden, og finner ut all alle navnene på land står oppført som
<td>1</td><td><a href="af.html">Afghanistan</a></td>

Vi søker derfor etter
.*">\([^<>]*\)<\/a><\/td>$

som finner alle linjer som slutter på et hvilket som helst antall tegn (utenom linjeskift), etterfulgt av "> etterfulgt av et hvilket som helst antall tegn utenom < og > etterfulgt av </a></td> (/ er en operator i sed, så den må escapes, og $ markerer slutten på en linje). Parantesene har som funksjon å fange opp det som står inni dem, så vi kan bruke det senere. I dette tilfellet fanger de opp navnet på landet.
sed -n 's/hei/hallo/p'

er en kommando som sier at sed ikke skal printe ut hver linje (-n sørger for det, standard er å printe ut alt som kommer inn, siden sed er en stream editor), videre skal alle tilfeller av hei erstattes av hallo (s/uttrykk1/uttrykk2/ erstatter uttrykk1 med uttrykk2) og til slutt sier p at sed skal printe resultatet.
sed -n 's/.*">\([^<>]*\)<\/a><\/td>$/\1/p'

erstatter for eksempel <td>1</td><td><a href="af.html">Afghanistan</a></td> med Afghanistan (\1 printer innholdet i parantesene), og skriver ut resultatet, så
curl http://www.listofcountriesoftheworld.com/ | sed -n 's/.*">\([^<>]*\)<\/a><\/td>$/\1/p'

skriver ut en liste over alle navnene på land som finnes på den nettsiden.
curl http://www.listofcountriesoftheworld.com/ | sed -n 's/.*">\([^<>]*\)<\/a><\/td>$/\1/p' | sort

skriver ut en sortert liste, skjønt det var strengt tatt ikke nødvendig, siden denne nettsiden tilfeldigvis oppgir landene i alfabetisk rekkefølge.
comm

er et program som tar to filer med lister, og kan skrive ut elementer som er felles, eller kun finnes i den ene listen.
comm fil1 fil2

skriver ut tre kolonner, der den første inneholder elementer som kun finnes i fil1, den andre elementer som kun finnes i fil2 og den tredje inneholder elementer som er felles.
comm -13 fil1 fil2

undertrykker kolonne 1 og 3, så den skriver kun ut de elementene som kun finnes i fil2.
<(cat larry.txt | sort)

utfører kommandoen som står inni parantesene og sender reslutatet som input til et annet program, så
comm -13 <(cat larry.txt | sort) <(curl http://www.listofcountriesoftheworld.com/ | sed -n 's/.*">\([^<>]*\)<\/a><\/td>$/\1/p' | sort)

sender den sorterte listen over land fra hvilke Larry har lest bøker og den sorterte listen over land i verden som input til comm, som skriver ut alle elementene som kun finnes i listen over land i verden, som altså tilsvarer de landen Larry ikke har lest bøker fra. Forutsatt at Larry og http://www.listofcountriesoftheworld.com/ har stavet alle navnene likt.
Category
Technology
Tags
bash
Unix-filosofi
comm
sed
Views
2290