Har haft många järn i elden den senaste tiden, därav dålig uppdateringsfrekvens på bloggen. Bättring lovas framöver! Ett hobbyprojekt jag ägnat mig åt lite på senaste tiden behövde en enkel och pålitlig funktion för att omvandla värdet mellan valutor. Det finns gott om kommersiella API:er som gör detta, och ska funktionen användas i professionella sammanhang är det nog där man ska leta, men för detta hobbyprojekt fungerade det utmärkt att anropa Googles valutakalkylator. Funktionen ser ut som följer:

/**
 * Use currency converter from Google to convert between currencies
 * Usage: currencyConvert(1, "USD", "SEK")
 * Uses str_replace to convert into parsable json
 * Returns value, i.e. "6.55420823" as a string
 */
function currencyConvert($amount, $from, $to) {
	$query	= 'http://www.google.com/ig/calculator?q='.$amount.$from.'=?'.$to;
	$curr 	= file_get_contents($query);
	$wrong	= array('lhs','rhs','error','icc');
	$right	= array('"lhs"','"rhs"','"error"','"icc"');
	$arr	= json_decode(str_replace($wrong, $right, $curr), true);
	return strstr($arr['rhs'], ' ', true);
}

echo currencyConvert(1, "USD", "SEK");

Funktionen tar som första argument beloppet som ska beräknas följt av valutan som ska omvandlas från och sedan valutan som ska omvandlas till. Dessa valutor skriv i tre bokstäver enligt standarden för valutorkoder. Funktionen rensar sedan upp i responssträngen och returnerar slutligen endast beloppet i den nya valutan.

notify-send

notify-send är ett litet, enkelt och fantastiskt användbart program för att skicka meddelanden från terminalen till skrivbordets system för underrättelser (notifications). Det spelar ingen roll vilken skrivbordsmiljö du använder, och det är inte heller något du behöver ange; notify-send tar hand om det åt dig. Däremot skiljer sig det förstås lite i utseende mellan underrättelser i de olika miljöerna. För närvarande använder jag skrivbordsmiljön Gnome 3 där underrättelserna dyker upp i nedre skärmkanten och ser ut så här:

Underrättelse

Använder du Ubuntu dyker underrättelserna upp i det övre högra hörnet, använder du KDE i det nedre högra. I program utvecklade för skrivbordsmiljö är det vanligt att underrättelser används för att berätta om status och förändringar i systemet eller i program du kör. Ett vanligt exempel är t.ex. en underrättelse om att en nedladdning är klar. Arbetar man både i terminal och i skrivbordsmiljö är det dock knepigt att ha koll på när långdragna processer och skript kör klart på något annat vis än att öppna upp terminalen och själv se efter. Med notify-send löser vi det problemet genom att från vårt skript skicka ett meddelande till vår skrivbordsmiljö. Du kan alltså lugnt sitta och surfa medan exempelvis rsync kör och få ett meddelande när det är klart.

Kommandot för ovanstående underrättelse är inte svårare än så här:

notify-send -t 10000 "Underrättelse" "Ser ut så här!"

Växeln -t använder vi för att ange hur länge underrättelsen ska visas (i millisekunder), i vår fall alltså 10 sekunder. Nästa parameter är rubriken följt av själva meddelandet. Nedan tänkte jag visa två exempel på hur notify-send kan användas i praktiken.

Lyckokakor med notify-send!

Det första exemplet är kanske roligare än vad det är nyttigt. Fortune är ett riktigt klassiskt program för terminalen vars enda uppgift är att skriva ut ett meddelande likt det som finns i lyckokakor (fortune cookies) till terminalen. Meddelandena är ganska varierade, från aforismer till citat och skämt, de flesta med en ganska ”nördig” betoning. För att använda fortune installerar du paketet fortune-mod och skriver sedan bara fortune för att få en lyckokaka:

[anders@anders-laptop ~]$ fortune
Never do today what you can put off until tomorrow.

Visdomsord indeed! Många använder fortune för att visa ett ”lycko”-meddelande till användare då de loggar in, exempelvis över SSH. Vad vi vill göra här är samma sak fast i form av en underrättelse i skrivborsmiljö. För att skicka meddelandet från fortune anger vi det som en parameter till notify-send:

notify-send -t 10000 "Lyckokaka" "`fortune`"

Lyckokaka

Kommandot är i princip identiskt med det vi skrev tidigare. Det enda vi bytt ut är meddelandet som nu istället för en egen text visar resultatet av kommandot fortune. Detta åstadkommer vi genom att sätta kommandot fortune innanför `-tecken (tecknet kallas ”grav accent” på svenska) vilket signalerar till bash att texten innanför ska ersättas med programmets utskrift och inte texten ”fortune”. Som en sista touch på det hela byter vi ut den tråkiga ikonen med glödlampan till något lämpligare. Jag hittade den här bilden på en lyckokaka. Att ange vilken ikon som ska användas gör vi med växeln -i:

notify-send -t 10000 -i "/home/anders/fc.png" "Fortune Cookie" "`fortune`"

Fortune Cookie med ikon

Jag bytte också ut texten ”Lyckokaka” till ”Fortune Cookie” då alla meddelanden ändå är på engelska och jag föredrar att inte blanda språk. Det sista jag gör nu är att lägga in ovanstående kommando bland skrivbordsmiljöns uppstartsprogram. I Gnome gör vi det grafiskt med programmet gnome-session-properties men varierar förstås mellan olika skrivbordsmiljöer. Varje gång vi startar om datorn eller loggar in i min skrivborsmiljö får vi nu en lyckokaka i form av en underrättelse!

Underrättelser från Apaches loggfil

Ett smart användningsområde för notify-send kan vara att bevaka en loggfil och rapportera ändringar och fel som underrättelser på skrivbordet. Det ”klassiska” sättet att bevaka en loggfil brukar från terminalen vara med kommandot tail och växeln -f (follow) som printar ut de sista 10 raderna ur en fil och uppdateras allteftersom filen fylls med nya rader. Ägnar vi oss åt exempelvis webbutveckling i skrivbordsmiljö kan det dock vara tidsödande att ständigt växla mellan terminalen och skrivbordet för att kontrollera om Apaches loggfil visar några fel. Ett sätt att slippa det kan vara att skicka ändringarna som tail -f registrerar till notify-send för att få dem som underrättelser istället.

För att kunna bevaka Apaches loggfil behöver vi antingen ha root-access eller ändra behörigheten på loggfilen så att den kan läsas av en vanlig användare. I en produktionsmiljö, alltså en webbserver som ligger online är det förstås idioti att låta vilken användare som helst få tillgång till loggfilerna. Här är det dock bara en lokal utvecklingsmiljö vi använder oss av, och gissningsvis är webbserverloggarna på min laptop inte av något större intresse ens om någon utomstående fick tillgång till den! I en ”skarp” miljö används ju dessutom sällan en skrivbordsmiljö på servern, vilket sålunda betyder att dessa instruktioner bara är av användning för utveckling på egen dator! Nåväl, vi börjar därför med att ändra behörighetskraven för katalogen med Apaches loggfiler:

sudo chown -R anders /var/log/httpd/

Ovanstående ändrar ägare för katalogen /var/log/httpd inklusive filer däri från användaren root till användaren anders, alltså min användare. Kontrollera att du kan läsa filen /var/log/httpd/error_log genom att skriva ”tail /var/log/httpd/error_log” utan att använda sudo. Fungerar det kan vi nu fortsätta med vårt kommando som i sin helhet kommer se ut så här:

tail -n0 -f /var/log/httpd/error_log | while read line; do \
notify-send -t 10000 "Apache error_log:" "$line"; done

Vi inleder alltså med att anropa programmet tail med växeln -f för att följa vad som händer i filen /var/log/httpd/error_log. Vad som är nytt här är växeln -n0. -n-växeln anger hur många rader från filen som som ska visas i terminalen. I vårt fall vill vi inte ha några rader alls i terminalen då vi  ju istället vill skicka meddelanden till notify-send, således -n0. Vi låter sedan en loop kontinuerligt läsa av de förändringar som tail rapporterar och spara dessa i variabeln line. Denna variabel anges sedan som meddelande i kommandot notify-send. Programmet kommer alltså att köra tills det avbryts av användaren och lämpar sig därför att köras i en bakgrundsprocess.

Även detta kommandot lägger jag till i gnome-session-properties för att starta processen varje gång jag loggar in i min skrivbordsmiljö. Uppstår det nu något fel i Apache medan jag sitter och utvecklar och testar webbsidor och php-skript kommer det direkt upp en underrättelse om detta:

Apache error

 

En kort introduktion till df och du

Då jag ofta har användning av programmen df och du tänkte jag kort introducera dom här med förhoppningen att det också kan vara till nytta för någon annan. Båda programmen används för att kontrollera tillgängligt/upptaget diskutrymme på UNIX-system (båda finns tillgängliga i både Linux och Mac OS X) men fungerar på lite olika sätt. Kommandot df används för att få en snabb överblick över tillgängliga filsystem och diskanvändningen på dessa. Kommandot du används för att lista diskanvändningen i enskilda kataloger och filer.

df – visa diskutrymme i filsystem

Kommandot df utan några argument ger på min laptop (med en hårddisk) följande:

[anders@anders-laptop ~]$ df
Filsystem             1K-block    Använt Tillgängl Anv% Monterat på
rootfs                31244260  18431140  12495796  60% /
udev                   1003096         0   1003096   0% /dev
tmpfs                  1012220      1352   1010868   1% /dev/shm
tmpfs                  1012220       732   1011488   1% /run
/dev/sda3             31244260  18431140  12495796  60% /
tmpfs                  1012220         0   1012220   0% /sys/fs/cgroup
tmpfs                  1012220         0   1012220   0% /media
/dev/sda3             31244260  18431140  12495796  60% /tmp
/dev/sda3             31244260  18431140  12495796  60% /var/tmp
/dev/sda3             31244260  18431140  12495796  60% /home
192.168.0.20:/home   950079616  24739904 877078400   3% /media/b3
192.168.0.8:/Backup  960301840 762754944 197546896  80% /media/Backup
192.168.0.8:/Upload  960301840 762754944 197546896  80% /media/Upload
192.168.0.8:/Public  960301840 762754944 197546896  80% /media/Public

Här kommer det upp en hel del, och listan är inte helt lätt att tolka om man inte har vanan inne. I första kolumnen listas här alla filsystem som finns monterade för tillfället. Vi ser till exempel att min huvudpartition ”/dev/sda3″ är monterad på ”/” alltså som root-partition och att denna partition använder 31244260 bytes och har 12495796 bytes till förfogande samt att 60% av utrymmet på disken/partitonen används. Längst ner ser vi också vilka filsystem som är monterade över nätverket, i det här fallet genom nfs (network file system). df listar ”riktiga” filsystem lokalt eller över nätverk, såväl som temporära och virtuella filsystem. Normalt sett har vi ingen större glädje av att veta status på de två sistnämnda. Inte heller är byte en särskilt meningsfull måttenhet i dagens datorvärld.

Vi gör därför ett försök att rensa upp lite i listan för att få ett för oss mer användbart resultat. df accepterar ett flertal växlar för att påverka vad som listas och hur. Den kanske enklaste och mest använda är växeln -h som står för ”human readable” och omvandlar bytes till mer lättlästa megabytes (M), gigabytes (G), osv. df -h ger följande:

[anders@anders-laptop ~]$ df -h
Filsystem          Storlek Anvnt Tillg Anv% Monterat på
rootfs                 30G   18G   12G  60% /
udev                  980M     0  980M   0% /dev
tmpfs                 989M  1,4M  988M   1% /dev/shm
tmpfs                 989M  732K  988M   1% /run
/dev/sda3              30G   18G   12G  60% /
tmpfs                 989M     0  989M   0% /sys/fs/cgroup
tmpfs                 989M     0  989M   0% /media
/dev/sda3              30G   18G   12G  60% /tmp
/dev/sda3              30G   18G   12G  60% /var/tmp
/dev/sda3              30G   18G   12G  60% /home
192.168.0.20:/home    907G   24G  837G   3% /media/b3
192.168.0.8:/Backup   916G  728G  189G  80% /media/Backup
192.168.0.8:/Upload   916G  728G  189G  80% /media/Upload
192.168.0.8:/Public   916G  728G  189G  80% /media/Public

Genast känns listan betydligt enklare att överblicka. Återstår gör då att exkludera de filsystem från listan som vi inte är intresserade av att följa. I mitt fall är det alla filsystem som används av systemet och inte direkt av mig och dessa är ovan rootfs, udev och tmpfs. För att ekludera filsystem från listan använder vi växeln -x följt av filsystemstypen. För att få reda på vilka filsystemstyper de olika partionerna tillhör använder vi växeln -T:

[anders@anders-laptop ~]$ df -h -T
Filsystem      Typ Storlek Anvnt Tillg Anv% Monterat på
rootfs      rootfs     30G   18G   12G  60% /
udev      devtmpfs    980M     0  980M   0% /dev
tmpfs        tmpfs    989M  1,4M  988M   1% /dev/shm
tmpfs        tmpfs    989M  732K  988M   1% /run
/dev/sda3     ext4     30G   18G   12G  60% /
tmpfs        tmpfs    989M     0  989M   0% /sys/fs/cgroup
tmpfs        tmpfs    989M     0  989M   0% /media
/dev/sda3     ext4     30G   18G   12G  60% /tmp
/dev/sda3     ext4     30G   18G   12G  60% /var/tmp
/dev/sda3     ext4     30G   18G   12G  60% /home
192.168.0.20:/home
               nfs    907G   24G  837G   3% /media/b3
192.168.0.8:/Backup
               nfs    916G  728G  189G  80% /media/Backup
192.168.0.8:/Upload
               nfs    916G  728G  189G  80% /media/Upload
192.168.0.8:/Public
               nfs    916G  728G  189G  80% /media/Public

Vi ser nu en ny kolumn som listar Typ. Det första filsystem jag inte var intresserad av att visa var rootfs som är av typen rootfs. Den andra var udev som listas som typen devtmpfs och den tredje var tmpfs som också är av typen tmpfs. Då kan vi alltså börja använda växeln -x för att exkludera oönskade filsystemstyper:

[anders@anders-laptop ~]$ df -h -x rootfs -x devtmpfs -x tmpfs
Filsystem          Storlek Anvnt Tillg Anv% Monterat på
/dev/sda3              30G   18G   12G  60% /
/dev/sda3              30G   18G   12G  60% /tmp
/dev/sda3              30G   18G   12G  60% /var/tmp
/dev/sda3              30G   18G   12G  60% /home
192.168.0.20:/home    907G   24G  837G   3% /media/b3
192.168.0.8:/Backup   916G  728G  189G  80% /media/Backup
192.168.0.8:/Upload   916G  728G  189G  80% /media/Upload
192.168.0.8:/Public   916G  728G  189G  80% /media/Public

Aningen enklare att överblicka än tidigare, eller hur? Däremot ganska krångligt att skriva ut hela kommandot med växlar varje gång vi bara vill kolla tillgängligt diskutrymme. Då det är såhär jag alltid vill att kommandot df ska lista filsystem åt mig lägger jag till kommandot med alla växlar som ett alias för ”df”:

[anders@anders-laptop ~]$ alias df='df -h -x rootfs -x devtmpfs -x tmpfs'

Nu kommer det räcka med att skriva bara ”df” för att använda programmet med samtliga växlar vi valt att nyttja. På så vis slipper vi framöver komma ihåg alla filsystemstyper vi inte är intresserade av utan skriver bara ”df” som vanligt och får automagiskt programmet att visa vad vi vill se! Vi har nu sparat vårt alias i minnet. Detta kommer dock försvinna när vi startar om datorn eller loggar ut vår användare. Det sista vi gör är därför att spara vårt alias på hårddisken så att det automatiskt läses in varje gång vi loggar in. Det gör vi genom att klistra in kommandot (precis som det skrevs ut i terminalen) i filen .bashrc som ligger i vår hemkatalog. Ligger den inte där kan du själv skapa den. Nu har vi framöver ett användarvänligt df till vår nytta!

du – visa diskanvändning i kataloger och filer

Kommandot du använder vi när vi vill veta exakt vilka kataloger (och om vi vill, filer) som tar upp plats på disken.  Kommandot utan några växlar listar som standard alla kataloger i den nuvarande katalogen och skriver ut hur många bytes katalogen (med underkataloger och filer) använder. Växeln -a (all) använder vi om vi utöver kataloger även vill visa filer. Även här kan vi använda växeln -h (human readable) för att få en mer lättbegriplig lista:

[anders@anders-laptop HTC Desire]$ du -h
47M	./20-08-2011
76M	./24-4-2011
66M	./23-11-2010
49M	./12-08-2011
30M	./03-01-2011
134M	./Första bilderna
16M	./28-08-2011
125M	./24-07-2011
539M	.

Ovan kör jag kommandot du -h i katalogen ”HTC Desire” (som innehåller alla bilder jag tagit med min mobiltelefon). Totalt är det 8 underkataloger. Den sista raden är en sammanfattning av katalogens totala storlek, det vill säga summan av samtliga underkataloger. Ofta är det bara den siffran vi är intresserade av och kan då använda växeln -s (summary) för just det ändamålet:

[anders@anders-laptop HTC Desire]$ du -sh
539M	.

Skriver vi t.ex. ”du -sh ~” får vi veta hur mycket utrymme hela vår hemkatalog tar upp. Tar vi bort växeln -s får vi veta exakt vilka kataloger som tar upp mest plats. Användbart när du vill jaga de största utrymmestjuvarna.

Avslutningsvis, en elegant variant vi kan använda för att hitta de största katalogerna är att skicka programmets output vidare till kommandot sort för att visa katalogerna i storleksordning. I kommandot nedan skickar vi dessutom den sorterade listan till kommandot tail för att begränsa listan till de 10 största katalogerna i vår hemkatalog:

[anders@anders-laptop HTC Desire]$ du ~ | sort -n | tail 

Geopositionering

Geopositionering (från engelskans ”geo location” eller ”geo tracking”) har de senaste åren blivit allt populärare i takt med att våra datorvanor blivit allt mer mobila. Mer eller mindre alla stora sidor använder någon form av geopositionering för att bestämma var användaren kommer från för att på så vis kunna anpassa innehållet, språket eller marknadsföringen efter det. Även utan att anpassa din sida efter besökarens geografiska position kan det förstås vara intressant att se var dina besökare kommer ifrån.

Det finns i grunden två typer av geopositionering; Antingen gör man det på serversidan med hjälp av besökarens IP-adress och en databas som länkar IP-adresser med geografiska positioner. Det andra alternativet är att göra det i din besökares webbläsare genom Javascript. Följande artikel ger en snabb inblick i båda metoder.

Geopositionering baserat på IP-adress

Fördelen med denna metoden är att du alltid har tillgång till dina besökares IP-adress, varför inte använda den till något? Nackdelen med att geopositionera efter IP-nummer är att metoden är minst sagt opålitlig. Informationen är normalt inte mer exakt än t.ex. vilken stad besökaren finns i. Det finns dessutom ingen som helst garanti för att den informationen du får är korrekt då databasen som mappar IP-nummer till geografisk position kan vara både utdaterad eller direkt felaktig.

Det finns både fria och kommersiella databaser att tillgå för detta ändamål, där de som kostar pengar rimligtvis bör ge något exaktare resultat(?), men fortfarande utan garantier. Å andra sidan är det ju också rätt skönt att ens position inte går att spåra exakt på ens IP-adress, eller hur? Syftet med geopositionering är förstås inte att ta reda på vilken exakt fysisk position din besökare finns på utan mer att kunna se större trender och mönster baserat på dina besökares geografiska plats. Kommer t.ex. 95% av besökarna till din webbshop från Stockholmsområdet är det kanske inte i Göteborg du ska öppna din första fysiska butik!

Geopositionering från webbläsaren

Geopositionering från webbläsaren används flitigt av framförallt sociala medier så som Twitter, Google+ m.fl. När du använder geopositionering från webbläsaren är det användaren själv som väljer huruvida den vill dela med sig sin geografiska position eller inte, normalt sett genom en liten ruta som dyker upp och berättar att webbsidan vill använda din geografiska position och ber dig godkänna eller avslå denna begäran. Detta är helt enligt specifikationerna för Javascripts Geolocation API och är inget som normalt kan åsidosättas.

Geopositionering

”Nackdelen” är således självklart att du inte kan tvinga dina besökare att ange sin fysiska position. Fördelen är att metoden är betydligt mer exakt än positionering baserat på IP-nummer! Då metoden använder sig av både WiFi-positionering och GPS (om tillgängligt) går det oftast att få geografiskt position så exakt som adress och trappuppgång. Viktigt är förstås att denna information används på ett sätt som användaren är informerad om. De flesta hade förmodligen blivit ganska irriterade om det började dyka upp reklam i brevlådan efter att de angett sin geografiska position på din sida! Geopositionering från webbläsaren ska alltså främst ses som något som förbättrar upplevelsen för dina besökare.

Nedan tar vi en titt på hur båda metoder implementeras i praktiken.

Geopositionering från IP-adress på serversidan med PHP

Det finns lite olika metoder att använda i PHP när det kommer till att geopositionera besökare baserat på deras IP-adress. Metoden vi använder här baserar sig på paketet GeoIP som är enkelt att hämta i de flesta linuxdistributioners pakethanterare. I Fedora heter paketet php-pecl-geoip. Du behöver dessutom installera C-biblioteket GeoIP. Använder du inte Linux finns installationsinstruktioner här.

Paketet bygger på en öppen databas som tillhandahålls av företaget MaxMind. Det går även att betala för en komersiell databas och fler funktioner. Nåväl, så fort installationen är klar kan du börja använda de inkluderade funktionerna för geopositionering. De flesta funktioner tar ett värdnamn eller ip-nummer som argument, vilket är enkelt fixat med den globala variabeln $_SERVER['REMOTE_ADDR']. I koden nedan använder vi sedan ip-numret i funktionen geoip_record_by_name, som returnerar en vektor med samlad information om ip-numrets geoposition:

$ip = $_SERVER['REMOTE_ADDR'];
$geodata = geoip_record_by_name($ip);

print_r($geodata);

Här stöter jag dock på patrull i form av en varning: Databasen GeoIPCity.dat verkar inte finnas tillgänglig. Nåväl, efter lite sökande hittar vi instruktioner för att hämta hem den:

wget -N -q http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
gunzip GeoLiteCity.dat.gz
mv GeoLiteCity.dat /usr/share/GeoIP/GeoIPCity.dat

Nu har vi laddat hem databasen och flyttat den till rätt plats. Nu skriver ovanstående kod ut något i stil med vektorn nedan, förstås beroende av var ip-numret härstammar:

Array (
[continent_code] => EU
[country_code] => SE
[country_code3] => SWE
[country_name] => Sweden
[region] => 26
[city] => Stockholm
[postal_code] =>
[latitude] => 59.333301544189
[longitude] => 18.049999237061
[dma_code] => 0
[area_code] => 0
)

Som synes har vi en hel del användbar information endast baserat på IP-adressen. Det är lätt att tänka att latitud/longitud-kordinaterna ger en exakt position. Det handlar dock bara om uppskattningar, så ta dem med en nypa salt. Testa gärna med din egen IP-adress för att se hur exakt resultat du får! Vill du bara testa funktionen utan att skriva kod finns en demo online här.

Geopositionering från webbläsaren med Javascript

För att kunna använda geopositionering från webbläsaren måste vi börja med att kontrollera att webbläsaren har stöd för detta. Det gör vi genom att kolla efter navigator.geolocation:

if (navigator.geolocation)
{
...
}

Såvida du inte använder en antik webbläsare bör det inte vara några problem. När vi väl bestämt att vår användare har tillgång till Javascripts Geolocation API är det primärt tre funktioner vi använder för att spåra geografisk position:

  • getCurrentPosition
  • watchPosition
  • clearWatch

getCurrentPosition gör precis vad namnet antyder. Funktionen tar tre parametrar varav den första är en s.k. callback-funktion som kommer att anropas om funktionen lyckades. Den andra parametern är en motsvarande funktion om du vill hantera vad som händer om funktionen inte lyckas. Den tredje parametern använder vi om vi vill ställa in exakt hur positionen ska mätas. I det här exemplet kommer jag att förutsätta att funktionen lyckas med vad den ska och därför endast använda den första parametern, som alltså är ett anrop till en annan funktion, i mitt fall funktionen showPosition:

if (navigator.geolocation) {

	navigator.geolocation.getCurrentPosition(showPosition);

}

function showPosition(position) {

	console.log('latitude: ' + position.coords.latitude + ' ');
	console.log('longitude: ' + position.coords.longitude);

}

Ovan anropar vi alltså funktionen getCurrentPosition och vid lyckat resultat anropar vi funktionen showPosition. Callbackfunktionen som anropas av getCurrentPosition blir automatiskt tildelad ett positionsobjekt som vi sedan kan använda oss av inuti vår funktion. Vår funktion, här döpt showPosition gör som ni ser inte mer än att skriva ut koordinaterna för latitud och longitud i webbläsarens javascriptkonsol. Ett populärt användningsområde är förstås att koppla ihop resultatet med ett yttre API, t.ex. Google Maps. Det sparar vi till ett senare inlägg!

Återstår gör då de två andra funktionerna, watchPosition och clearWatch. watchPosition fungerar på samma sätt som Javascripts inbyggda timerfunktion setInterval, och uppdaterar med jämna mellanrum den geografiska positionen. Detta används dels för att det kan behövas fler försök att exakt bestämma någons position men också för att användaren ju kan röra på sig under tiden den besöker din sida! Precis som watchPosition används som Javascripts inbyggda setInterval används clearWatch precis som Javascripts inbyggda clearInterval. I övrigt fungerar funktionen watchPosition på samma vis som getCurrentPosition:

if (navigator.geolocation) {

	positionT = navigator.geolocation.watchPosition(showPosition);

}

function showPosition(position) {

	console.log('updated latitude: ' + position.coords.latitude + ' ');
	console.log('updated longitude: ' + position.coords.longitude);

}

Nu har vi förhoppningsvis uppnått en lyckad positionering och avslutar med att stänga av timern för positionering:

navigator.geolocation.clearWatch(positionT);

Sammanfattning

Som förhoppningsvis framgått av den här korta introduktionen krävs det ingen större ansträngning för att använda geopositionering varken på serversidan eller i webbläsaren. Hur man sedan väljer att använda resultatet är förstås det som är viktigast, och där finns det gott om möjligheter. Jag tänkte i ett kommande inlägg koppla ihop vår geopositionering med Google Maps API, vilket är ett vanligt användningsområde. Har du idéer eller tips om roliga användningsområden så skriv gärna en kommentar!

Introduktion till processer i bash

Varje kommando du skriver in i bash startar en egen process. När du skriver t.ex. ”ls” för att lista innehållet i en katalog startar bash en separat process för det och avslutar den sedan direkt när kommandot utförts. Oftast är det inget man behöver ägna någon särskild tankemöda då de flesta kommandon och script man anropar exekveras snabbare än man hinner reagera.  Förr eller senare kan det dock dyka upp kommandon eller script som antingen tar lång tid för att de behandlar en stor mängd data eller för att kommandot eller scriptet till sin natur bör köra under en längre tid, kanske för att samla in information om systemet medan det körs. Ett exempel på ett sådant kommando kan t.ex. vara:

# tail -f /var/log/httpd/error_log

Kommandot tail gör som du kanske vet inte mycket mer än att skriva ut de sista 10 raderna ur en textfil på skärmen. Med parametern -f (follow) fortsätter tail att bevaka filen (som i det här fallet är Apaches loggfil för felmeddelanden; /var/log/httpd/error_log) och skriver ut förändringar till skärmen direkt då de upptäcks. Således ett mycket användbart kommando för att bevaka filer som förändras kontinuerligt. Ett problem med ovanstående kommando är dock att då det fortsätter köra kommer vi inte kunna göra någonting annat förrän kommandot antingen avbryts av oss eller avslutar på naturlig väg. Ett sätt att lösa det på kan förstås vara att öppna en separat terminal för just den processen och sedan växla till den terminalen när vi vill se vad som händer i vår loggfil.

Att köra en process i bakgrunden

En annan metod är att låta processen köra i bakgrunden och endast ”lyfta fram” den till förgrunden då vi vill veta vad som händer. För att starta ett kommando i bakgrunden lägger vi bara till ett ”&”-tecken efter kommandot och eventuella parametrar:

# tail -f /var/log/httpd/error_log &

Resultatet blir precis som innan att vi kommer att se de tio sista raderna ur loggfilen listade. Ovanför dessa kommer vi dock nu att se något i stil med:

[1] 9868

Bash har nu skapat ett separat ”jobb” för vårt kommando och givit det ett identifikationsnummer , i det här fallet 1. Nästa siffra, 9868, är operativsystemets processid (pid). Siffrorna kommer förstås vara annorlunda på din dator. Vi har nu två nummer att använda för att identifiera vår process. Men var tog prompten vägen? Tryck på Enter ytterligare en gång för att flytta processen till bakgrunden och lyfta fram prompten till förgrunden.

Lista processer i bakgrunden med jobs och ps

Till synes är allting nu som tidigare, men vårt kommando ligger fortfarande och kör i bakgrunden. För att lista de jobb vi startat skriver vi kort och gott ”jobs”:

[root@anders-laptop anders]# jobs
[1]+ Kör tail -f /var/log/httpd/error_log &

För att se vilka processer som körs för närvarande använder vi kommandot ps (process status):

[root@anders-laptop ~]# ps
PID TTY TIME CMD
9693 pts/0 00:00:00 su
9698 pts/0 00:00:00 bash
9868 pts/0 00:00:00 tail
9877 pts/0 00:00:00 ps

Flytta processer med fg och bg

För att flytta fram en process till förgrunden använder vi kommandot fg följt av jobbnumret:

[root@anders-laptop anders]# fg 1
tail -f /var/log/httpd/error_log

En alternativ syntax för att göra samma sak är att använda ett procenttecken följt av jobbnumret. Kommandot ovan kan alltså lika väl skrivas ”%1″. Processen kommer nu upp och syns på skärmen. För att åter flytta processen till bakgrunden trycker vi [CTRL+Z] för att först stoppa processen (suspend). Sedan använder du kommandot bg för att starta upp processen i bakgrunden igen:

[root@anders-laptop anders]# bg 1
[1]+ tail -f /var/log/httpd/error_log &

Observera att om du endast har stoppat en enda process behöver du inte ange något jobbnummer.

Avsluta process med kill

En process som körs i förgrunden kan oftast avslutas med tangentkombinationen CTRL+C.  Vi kan när som helst välja att avsluta en process i bakgrunden genom kommandot kill. Det går bra att ange både jobbid eller processid som parameter. Standard är dock processid, och vill vi hellre ange jobbid gör vi det med prefixet %:

[root@anders-laptop ~]# kill 9868 #alternativt "kill %1"

Sammanfattning

Ovan är tänkt som en kortfattad introduktion till processhantering i bash. Vill du veta mer om hur processer fungerar i UNIX-miljö finns en bra introduktion på Wikipedia, samt förstås bash:s egna manual.

Nedanstående är första delen av en serie artiklar med temat ”Optimera din webbplats”. Serien utgår från LAMP (Linux Apache MySQL PHP) som plattform, men de flesta tekniker är förstås tillgängliga universellt. Vi börjar med en av de mest självklara och enkla optimeringsåtgärderna, att optimera de bilder vi presenterar.

Förlustfri Optimering

Tux innan optimeringTux efter optimering

De två bilderna ovan är identiska  till både utseende och kvalitet. Bilden till höger har dock optimerats med optipng och är därför 11% mindre till storlek (datamängd). Den högra pingvinen har optimerats med förlustfri komprimering vilket innebär att ingen synlig data förändrats. I den här introduktionen använder vi endast förlustfri optimering. Det kan ibland vara berättigat att komprimera bilder ytterligare med mer eller mindre synbara förluster som resultat, men att optimera utan försämrat resultat är alltid ett första steg. Att inte göra det är slöseri med både din och andras bandbredd och tid!

Varför optimera?

Optimering gör förstås störst skillnad om du har hög trafik till din sida. Det kan tyckas onödigt att skala bort några enstaka kilobytes här och där om din sida inte besöks av fler än några stycken om dagen, och till viss del stämmer det väl också, men det finns faktiskt skäl till att optimera redan från början:

  • Sökmotorer premierar snabba sidor.
  • En snabb sida är roligare att besöka. Flera undersökningar från bland annat Amazon visar att en sidas responstid står direkt i proportion till hur länge besökare/kunder stannar kvar och om de återkommer eller ej.
  • Det kommer bli mycket mer abete att optimera din sida i efterhand.
  • Ligger din sida på ett webbhotell har du ofta en begränsning i hur mycket data du får lagra, samt hur mycket bandbredd din sida får omsätta per månad.
  • Det är roligt att ständigt förbättra sin skapelse!

Jpegoptim

Programmet jpegoptim använder en förlustfri kompressionsalgoritm som rekommenderas (och får man anta, används) av inga mindre än Google. Använder du Ubuntu finns paketet jpegoptim direkt att hämta med apt-get. För Fedora finns inget färdigt paket, och eftersom det är Fedora jag använder för tillfället var jag nödgad att gå den lite längre vägen och kompilera programmet själv.

Laddade först ner senaste versionen (för stunden 1.2.4) från programmets egna sida. Programmet kräver att utvecklingsfilerna för libjpeg finns installerade. Började därför först med att installera dessa, som på fedora heter libjpeg-turbo-devel. Sedan var det bara att packa upp programmet och kompilera det:

./configue
make
make strip
make install

Proceduren torde se liknande ut även på andra plattformar där färdiga paket saknas, så som Mac OS X. Programmet kommer med en rad möjligheter för kompression, såväl förlustfri som med förluster. Då jag inte vill försämra kvaliteten på bilderna är det förlustfri som gäller, och det får vi med växeln –strip-all:

[anders@anders-laptop ~]$ jpegoptim --strip-all DSC_0055.JPG
DSC_0055.JPG 3008x2000 24bit  [OK] 2525257 --> 2438219 bytes (3.45%), optimized.

Som synes blev det i det här fallet ingen dramatiskt förminskning av bildens storlek. Men 3.45% är förstås bättre än ingenting, framför allt som vi inte försämrat bildens kvalitet! Hur mycket data som kan skalas bort beror på flera faktorer i bilden, men normalt brukar det röra sig mellan 2-20%. Enkelt uttryckt så är det lättare att få bra resultat med enkla bilder med mindre detaljer och färger än t.ex. ett foto.

Optipng

Optipng är nästa program i ordningen och som namnet ger en vag ledtråd om är det specialiserat på bilder i PNG-formatet. Mer detaljerad information om optipng hittar du på programmets webbplats. Programmet används normalt så här:

[anders@anders-laptop tmp]$ optipng -o7 ~/kylskapmini.png
OptiPNG 0.6.4: Advanced PNG optimizer.
Copyright (C) 2001-2010 Cosmin Truta.

** Processing: /home/anders/kylskapmini.png
560x314 pixels, 3x8 bits/pixel, RGB
Input IDAT size = 73786 bytes
Input file size = 74004 bytes

Trying:
  zc = 9  zm = 9  zs = 0  f = 0		IDAT size = 56020
  zc = 3  zm = 9  zs = 0  f = 0		IDAT size = 53390
  zc = 3  zm = 9  zs = 1  f = 0		IDAT size = 53390

Selecting parameters:
  zc = 3  zm = 9  zs = 1  f = 0		IDAT size = 53390

Output IDAT size = 53390 bytes (20396 bytes decrease)
Output file size = 53500 bytes (20504 bytes = 27.71% decrease)

Som synes på sista raden fick vi alltså en förminskning på nästan 28% utan bildförsämring! Växeln -oX där X är ett nummer mellan 1-7 kan användas om man själv vill bestämma hur hårt programmet ska anstränga sig för att optimera din bild. Har du ett stort bildbibliotek får du vara beredd på att en optimering med -o7 kan ta en bra stund. Annars finns ingen anledning att  inte optimera maximalt.

Automatisk optimering av uppladdade bilder

Lättja är en grundpelare i all systemadministration. Istället för att manuellt optimera varje enskild bild med verktygen ovan vore det väl skönt om vi kunde automatisera processen? Optimalt hade det ju varit om vi bara kunde ladda upp våra bildfiler som vanligt och låta servern optimera dom utan att vi behövde lyfta ett finger. Det finns som alltid i Linux olika sätt att lösa ett sådant problem, men i det här exemplet använder jag det utmärkta programmet incron.

Bevaka en katalog med incron

I UNIX-system och dess derivat är cron ett system för att schemalägga processer till valda tidpunkter och intervaller. Programmet incron bygger på samma princip (och använder samma syntax) men istället för att bevaka klockslag och datum bevakar incron förändringar i kataloger. Du väljer själv vilka förändringar du vill bevaka, men i vårt fall vill vi bara se när en ny fil anländer i vår katalog.

Börja med att installera (yum install, apt-get install, etc) incron. Lägg sedan till de användare som ska använda incron i filen incron.allow som du hittar (eller skapar) i /etc/incron.d/. Starta sedan incron med kommandot ”service incrond start”. Det som återstår nu är att välja vilken katalog vi vill bevaka och vad som ska hända när en ny fil anländer till katalogen. För att ställa in de reglerna använder vi kommandot:

incrontab -e

Detta öppnar en editor (vilken bestämmer du i /etc/incron.conf) för redigering av regler för incron. Syntaxen för den här filen känner du igen om du jobbat med crontab tidigare, annars är den som följer:

<katalog> <händelse> <kommando>

Exakt vilka händelser du kan bevaka hittar du i manualsidan för incrontab (och en bra guide online här), och du kan separera flera händelser med ett kommatecken. I vårt fall vill vi veta när nya filer skapats i vår katalog eller flyttats till den. De händelserna heter IN_CREATE och IN_MOVED_TO. Filen ser så här långt alltså ut såhär:

/var/www/uploads/ IN_CREATE,IN_MOVED_TO

Så långt har vi alltså angett vilken katalog som ska bevakas och vilka händelser vi ska bevaka. Återstår gör att ange vad vi vill göra när dessa händelser inträffar, alltså vilket kommando vi vill köra. Vi skulle också behöva veta vilken fil det är som flyttats till vår katalog för att kunna optimera just den. Lyckligtvis har incron två inbyggda variablar som gör det enklare för oss:

$@ den bevakade katalogen
$# filen som triggat händelsen

Fler inbyggda variabler kan du läsa om i manualen, men i vårt fall är det just dessa två vi behöver. För att köra kommandot ”optipng -o7″ på varje png-fil som laddas upp hade vi alltså kunnat skriva:

/var/www/uploads/ IN_CREATE,IN_MOVED_TO /usr/bin/optipng -o7 $@$#

Våra variabler $@ och $# kommer här expandera till /var/www/uploads/uppladdadebilden.png

Bedöma filtyp efter filändelse

Detta funkar ju utmärkt så länge det är en PNG-fil vi laddat upp. Är det en JPEG-bild eller något annat format kommer optipng att säga ifrån. Vi behöver därför något sätt att särskilja de filer vi laddar upp efter filändelse eller typ. Finns även här olika sätt att lösa det på. Min lösning var ett kort shell-skript:

#!/bin/bash
# Simple script to determine file type and pass file to appropriate
# program. To be called from incrontab.
# Written by Anders Eknert, anders@lonewolfmedia.se

if [ "$1" == "" ]; then

	echo "Usage: optimize [file].[jpg|jpeg|png]"

else
	# Set filename to first argument
	filename=$1
	# Set myindex to position of .
	myindex=`expr index $1 .`
	# Set extension to substring after .
	extension=${filename:myindex}
	# Check for filetype and process accordingly
	if [ "$extension" == "png" ]; then
		optipng -o7 $filename &> /dev/null

	elif [ "$extension" == "jpg" ]; then
		jpegoptim --strip-all $filename &> /dev/null

	elif [ "$extension" == "jpeg" ]; then
		jpegoptim --strip-all $filename &> /dev/null

	fi
fi

Skriptet ovan (som jag döpt till optimize och gjort körbart med chmod +x) tar en parameter, nämligen ett filnamn och kontrollerar sedan filändelsen. Är den .png kommer den anropa optipng för att optimera bilden. Är den en bild av jpeg-typ kommer den anropa jpegoptim för samma uppdrag. Det kan tyckas vanskligt att bedöma en fil endast efter filändelse, men i mitt fall vet jag att det bara är jag som kommer att ladda upp bilder och kan därför förutsätta att jag inte laddar upp annat än bilder i korrekta format och filändelser.

Slutligen (äntligen!)

Skriptet placerar vi sedan i valfri katalog. Jag valde /home/anders/bin. Återstår då bara att ändra vår incrontab, vilket vi återigen gör med ”incrontab -e”:

/var/www/uploads/ IN_CREATE,IN_MOVED_TO /home/anders/bin/optimize $@$#

Sådär! Varje gång någonting händer, specifikt när en ny fil skapas eller flyttas in  i vår uploads-katalog kommer incron att kalla på skriptet optimize som bedömer filtypen och sedan komprimerar den uppladdade bilden åt oss. Och vi kan nu framöver jobba mindre med optimering och mer åt att skapa innehåll!

Vad är en rekursiv funktion?

Funktioner är grundläggande inom all programmering. Rekursiva funktioner är kortfattat funktioner som anropar sig själva. Två kriterier ställs alltid på en rekursiv funktion:

  1. Den måste innehålla ett avslutande villkor. Med det innebär att det måste finnas ett tillstånd då funktionen slutar anropa sig själv. Gör den inte det kommer funktionen anropa sig själv i oändlig tid (en. infinite loop), eller till du på annat sätt avbryter processen.
  2. Ett rekursivt funktionsanrop ska alltid skilja sig från det tidigare anropet. Undvik redundans, det vill säga att behandla samma information flera gånger.

Exempel: En enkel rekursiv funktion i PHP

En enkel rekursiv funktion skulle t.ex. kunna vara att räkna alla tal mellan (och inklusive) två angivna heltal. I PHP hade det sett ut så här:

function recursive_count($start, $end)
{
	if ($start <= $end)
	{
		echo $start.' ';
		$start++;
		recursive_count($start, $end);
	}
}

recursive_count(1,10);
echo "<br />";
recursive_count(22, 30);

Koden uppfyller båda kraven för en rekursiv funktion. Dels har den ett avslutande villkor (start-variabeln måste vara av lägre värde eller lika med end-variabeln, annars slutar funktionen) och då variabeln start ökar för varje iteration kommer heller aldrig samma funktionsanrop att göras. Koden ovan genererar förstås följande resultat:

1 2 3 4 5 6 7 8 9 10
22 23 24 25 26 27 28 29 30

Varför använda rekursiva funktioner?

Inga konstigheter så långt, eller hur? Men är det inte enklare att använda en vanlig loop? Jo, förmodligen! Och i de allra flesta fall (som i det ovan) är det svårt att motivera en rekursiv funktion framför användandet av en loop. Resultatet är detsamma, men för de allra flesta är det lättare att läsa och begripa vanliga loopar, kanske just för att de är mer vanligt förekommande.

I några fall är användandet av rekursiva funktioner mer naturliga än användandet av en loop. Ett exempel är t.ex. vid beräkning av matematisk fakultet. Ett annat exempel där rekursiva funktioner brukar användas är då man vill söka igenom katalogstrukturer eller andra trädstrukturer.

Exempel: En rekursiv sökning i en katalogstruktur

En lite mer komplex rekursiv funktion i PHP. Här börjar vi i en katalog och söker igenom varje underkatalog, deras underkataloger, osv, till hela katalogträdet är genomsökt.

function traverse_dir($dir)
{
	if (is_dir($dir))
	{
		$subdirs = get_subdirs($dir);

		foreach($subdirs as $subdir)
		{
			$cwd = $dir . '/' . $subdir;
			traverse_dir($cwd);
			echo $cwd."\n";
		}
	}
	else echo 'No such directory: ' . $dir;
}

function get_subdirs($dir)
{
	$dirs 	= array();
	$files	= @scandir($dir);	// ignore permission denied warnings

	if($files)			// if directory and permission not denied
	{
		foreach ($files as $file)
		{			// ignore . and ..
			if ((is_dir($dir.'/'.$file)) && ($file != '.') && ($file != '..'))
			{
				$dirs[] = $file;
			}
		}
	}
	return $dirs;
}

traverse_dir('/var/www');

Funktionen traverse_dir tar här en katalog (‘/var/www’) som enda parameter och söker sedan igenom den efter  underkataloger. Funktionen gör sedan ett anrop på sig självt och upprepar proceduren i varje underkatalog. Villkoren för funktionen är att fortsätta så länge det finns tillgängliga underkataloger, och då varje funktionsanrop innehåller underkatalogens namn kommer ej heller samma funktionsanrop att upprepas. Funktionen get_subdirs är ej en nödvändighet utan hade kunnat skrivas in i funktionen traverse_dir. Att tydligt separera rekursiva element och icke-rekursiva gör dock koden lättare att överblicka, förstå och felsöka.

Slutligen

Rekursiva funktioner används främst inom funktionell programmering, och sällan i språk som PHP.  I många av de fall där rekursiva funktioner faktiskt hade varit användbara (som i fallet ovan) finns oftast färdiga funktioner att nyttja som utför samma sak men med betydligt färre rader kod (se exempelvis funktionen glob i PHP). Att ha koll på vad en rekursiv funktion är och hur den tillämpas kan ändå vara av värde, dels för att förstå hur de ”färdiga” funktionerna arbetar, men också för att det är ytterligare ett verktyg som kan komma till nytta i andra programmeringssituationer.  Även om exemplen ovan är skrivna i PHP är principen förstås densamma oavsett programmeringsspråk.

Introduktion till kommandot history

I förra bloggposten tittade vi på hur man enkelt repeterar ett tidigare kommando med dubbla utropstecken (!!). Vad bash gör när det upptäcker de dubbla utropstecknen är att det kollar i historiken över tidigare använda kommandon och skriver sedan ut (expanderar) våra utropstecknen till det senaste av dem. Om det inte är det senaste vi vill använda då? Kommandot history i bash ger oss goda möjligheter att överblicka och använda historik, något jag tänkte gå in på närmare här.

Den enklaste introduktionen till history är något de allra flesta som jobbat i terminalen förmodligen känner till: tangentbordsknapparna pil upp och pil ner bläddrar upp/ner bland tidigare skrivna kommandon och skriver ut dem i prompten. Det är ett snabbt och smidigt sätt att komma tillbaks till något man precis skrivit men som kanske innehöll ett stavfel och därför inte gjorde det vi ville. Vill vi däremot gå långt tillbaks blir det snabbt tröttsamt att bläddra rad för rad. För att se en komplett lista över vad som tidigare skrivits in använder vi kommandot history:

[anders@anders-laptop ~]$ history
1  cd /
2  ls
3  sudo apt-get update && sudo apt-get upgrade
4  clear
5  history
...

Som synes ovan får vi en komplett lista över alla tidigare kommandon. I exemplet här har det bara skrivits fem kommandon totalt sedan vårt operativsystem installerades (eller history-filen rensades, mer om den strax), något som förstås är osannolikt i verkligheten men bra för att visa upp. Förmodligen är listan så  lång att den täcker mer än hela skärmen. Skriv

history | less

för att skicka listan till kommandot less som gör det enkelt att bläddra upp och ner bland kommandohistoriken med antingen piltangenterna eller med page up/page down. Ett annat och förmodligen bättre alternativ är att skriva

history > historik.txt

för att istället för att fylla upp skärmen skicka listan till en ny fil som vi i vårt fall pedagogiskt döper till historik.txt. Filen sparas i den nuvarande katalogen och kan sedan ögnas igenom i lugn och ro med valfri textredigerare. Slutligen kan vi också begränsa mängden data genom att säga till history-kommandot hur många av de senaste posterna vi vill visa:

history 10

returnerar alltså de 10 sista kommandona i vår historik. Notera att varje rad innehåller ett nummer framför namnet på kommandot som utförts. Detta indikerar förstås i vilken ordning kommandona skrivits in i historiken. Vi kan nu enkelt repetera valfritt kommando från history-listan genom att skriva ett enkelt utropstecken följt av det nummer i historiken vi vill repetera! I exemplet nedan repeterar vi det tredje kommandot i listan

[anders@anders-laptop ~]$ !3

Två enkla tecken ersätter alltså de 51 tecken vi annars behövt för att uppdatera vårt operativsystem! Normalt sätt är det dock bättre att använda bashkommandot alias om man önskar ge kortare namn åt ofta repeterade långa kommandon.

En djupare inblick i history

Hur fungerar då history-kommandot? Jo, varje gång du trycker på Enter i terminalen sparas informationen du skrivit i prompten till minnet. När du senare stänger ner din terminal sparas historiken från minnet till din history-fil. Den läses sedan in nästa gång du startar en terminal så att du alltid har tillgång till tidigare historik. Vad heter då history-filen och var hittar vi den? Det är lite olika beroende på vilken distribution, men normalt sett ligger den i din hemkatalog och heter enkelt nog .bash_history. Du kan själv ta reda på var din historik finns sparad genom att skriva ut variabeln HISTFILE

[anders@anders-laptop ~]$ echo $HISTFILE
/home/anders/.bash_history

Då den senaste historiken oftast ligger direkt i minne och endast skrivs till fil vid avslut är det dock sällsynt att man läser history-filen direkt. Det är det vi har kommandot history till! Viktigare är då variabeln HISTSIZE som anger hur många kommandon som ska sparas i din historik. Normalt brukar den ha ett värde mellan 500-1000. Det finns dock ingen anledning att snåla på det, så vi höjer värdet en aning med en extra nolla:

[anders@anders-laptop ~]$ export HISTSIZE=10000

För att säkerställa att detta värde laddas även vid omstart sparar vi ovanstående i vår .bashrc-fil i hemkatalogen. Denna fil laddas alltid in när du loggar in och är ett bra ställe att placera variabler du alltid vill ha tillgång till. Öppna den med din föredragna texteditor och skriv in samma som ovan där, alternativt:

[anders@anders-laptop ~]$ echo "export HISTSIZE=10000" >> .bashrc

Viktigt i ovanstående är att säkerställa dubbla ”>”-tecken (har de ett svenskt namn?)! Används bara ett kommer nämligen den befintliga .bashrc-filen skrivas över!

Att söka i historiken

Minns ni hur vi skickade data från history till programmet less för att enklare kunna bläddra i den? Det enklaste sättet att söka i din history är genom att istället skicka all data till programmet grep. Grep är ett otroligt mångfacetterat program, men kortfattat så söker det i sitt igenom en text och returnerar alla rader som innehåller den text vi söker efter. Testar vi t.ex. att söka efter alla kommandon innehållandes kommandot yum (Fedoras pakethanterare) ser vi följande:

[anders@anders-laptop ~]$ history | grep "yum"
458  sudo yum install php-xml
460  sudo yum update
482  yum install fping
483  sudo yum install fping
539  sudo yum install preload
587  sudo yum update
...

Användbart om du t.ex. undrar vad det egentligen var du installerade den där gången du kom hem från krogen!  Citationstecknen är inte nödvändiga så länge vi bara söker ett enkelt ord men behövs så fort vi letar efter ett kommando med fler ord eller parametrar, så som yum install. Det är därför god praxis att alltid citera texten du söker.

Ett annat alternativ för att söka igenom historiken efter ett specifikt kommando är att i terminalen trycka Ctrl+r. Du får då upp en prompt med texten (reverse-i-search)`’ och kan sedan skriva in texten du söker. Tryck Ctrl+r upprepade gånger för att gå längre bak i historiken. När du hittat kommandot du letade efter trycker du vänster- eller högerpil för att flytta ut texten till prompten.

Avancerad användning av history

Två andra användbara variabler som läses av av history-kommandot är HISTIGNORE och HISTCONTROL. Det första är tämligen självförklarande; det anger nämligen precis vilka kommandon (om några) vi inte vill lagra i vår historik. Variabeln kan ta flera värden som separeras med ett kolon ( : ) och fungerar enligt följande syntax:

HISTIGNORE="history:pwd"

Ovanstående skulle göra att kommandona history och pwd upphörde registreras i vår historik. Värt att notera är att ovanstående endast ignorerar exakta träffar! Kommandot history 10 hade alltså även fortsättningsvis registrerats då det inte matchar enbart ”history”. Vill vi att kommandot ska ignoreras totalt oavsett följande värden/parametrar lägger vi bara till ett * vid slutet:

HISTIGNORE="history*:pwd"

Kommandon som history kan möjligen platsa i HISTIGNORE, men min rekommendation är att vara försiktig med vad du lägger till. Det motverkar förstås syftet med history-funktionen att ignorera vad som sker! En kraftfullare och mer användbar variabel är då HISTCONTROL. HISTCONTROL anger både vad som ska sparas och hur det sparas. Variabeln kan ges totalt fyra olika värden av vilket ett är en sammanslagning av två andra. Värdena är som följer:

  • ignorespace – ignorera alla kommandon som börjar med ett mellanslag. Registrerar alltså ”cd ..” men inte ” cd ..”. Användbart om man då och då önskar undanta ett kommando från history.
  • ignoredups – ignorera dubbletter i följd. Skriver du alltså exempelvis ”ls” flera gånger i följd kommer endast den sista att registreras.
  • ignoreboth – Sammanslagning av de två ovanstående parametrarna, aktiverar både ignorespace och ignoredups.
  • erasedups – ignorera samtliga dubbletter i din historik! Skriver du exempelvis ”ls” kommer samtliga förekomster av kommandot ”ls” att tas bort från din historik och ersättas med enbart det sista. Användning av denna parameter känns svår att motivera annat än i specialfall.

Slutligen

Har du orkat ända hit bör du nu ha en god överblick över history-kommandot och hur det används i praktiken. För att avsluta bjuder jag här på en gammal goding i bash-sammanhang, nämligen en salig blandning kommandon sammansatta med pipes i en enda lång one-liner. Resultatet; en tio-i-topp-lista över de mest använda kommandona i historiken, sorterade efter antal!

[anders@anders-laptop ~]$ history | awk '{print$2}' | awk 'BEGIN {FS="|"}{print $1}' | sort | uniq -c | sort -n | tail | sort -nr

Resultatet för mig blir:

    157 git
    143 ls
    129 sudo
    101 cd
     42 echo
     34 man
     33 su
     28 mysql
     24 chmod
     23 rm

Vad blir det för dig? Klipp gärna in i kommentarerna!

Glömde du sudo?

Dubbla utropstecken i bash

Alla som använt Ubuntu eller någon annan linuxdistribution där sudo används för att utföra ett kommando som kräver root-rättigheter har säkerligen någon gång glömt att skriva just sudo innan det kommando man vill utföra. Eller hur? Det ger vanligtvis till svars:

[anders@anders-laptop ~]$ cat /etc/shadow
cat: /etc/shadow: Åtkomst nekas

Detta förstås för att vi glömde sudo och därmed inte har rättigheter att läsa /etc/shadow-filen. Jag glömmer det ofta i kombination med apt-get install då jag behöver hämta hem nya paket och program. Vad göra?

Lugn, det ordnar sig

Vi kan förstås alltid skriva om kommandot med prefixet sudo.

[anders@anders-laptop ~]$ sudo cat /etc/shadow
nobody:*:15014:0:99999:7:::
usbmuxd:!!:15072::::::
avahi-autoipd:!!:15072::::::
...

Har vi just skrivit ett långt kommando (så som i det tidigare rsync-exemplet) med flera parametrar ter sig dock detta jobbigt, och jobbiga saker vill vi ju alltid undvika! Hur görs det mindre jobbigt? Vi kan förstås  trycka pil upp för att bläddra upp ett steg i kommandohistoriken, trycka på home-knappen för att komma till början av raden och skriva sudo där. Nu börjar det bli riktigt enkelt! Det enklaste alternativet kvarstår dock, nämligen användandet av dubbla utropstecken:

[anders@anders-laptop ~]$ sudo !!

De dubbla utropstecknen är en inbyggd funktion i bash som alltid returnerar det senast inslagna kommandot. Att skriva ovanstående blir alltså i vårt fall samma sak som att skriva:

[anders@anders-laptop ~]$ sudo cat /etc/shadow

Enkelt, eller hur?

Pingsvep

Det kan ofta vara av nytta att få reda på vilka andra datorer som befinner sig på samma nätverk. En enkel och snabb metod att göra det är via ett så kallat pingsvep.

Nedan följer tre olika metoder. I exemplen nedan använder vi nätverket 192.168.0.xxx vilket är vanligast för hemnätverk. Byt förstås ut efter behov!

[ Observera att det kan anses vara en fientlig handling att göra ett pingsvep på nätverk som du inte själv administrerar. Trots att metoden i sig är helt ofarlig är den ofta ett första steg en illvillig användare skulle ta för att upptäcka möjliga mål på nätverket. Använd därför endast på de nätverk du själv äger och har kontroll över, så som ditt hemnätverk. ]

Metod 1 – ping

Den första och enklaste metoden är att använda ping för att pinga nätverkets broadcastadress:

ping -b 192.168.0.255

Ovanstående skickar en ping till nätverkets broadcastadress och listar sedan svaren som kommer tillbaks. Responsen kan se ut ungefär såhär:

WARNING: pinging broadcast address
PING 192.168.0.255 (192.168.0.255) 56(84) bytes of data.
64 bytes from 192.168.0.193: icmp_req=1 ttl=64 time=4.28 ms
64 bytes from 192.168.0.199: icmp_req=1 ttl=64 time=4.92 ms (DUP!)
64 bytes from 192.168.0.8: icmp_req=1 ttl=64 time=5.40 ms (DUP!)
64 bytes from 192.168.0.8: icmp_req=2 ttl=64 time=2.06 ms
									

Vi får alltså klart för oss att datorerna med ip-adresserna 192.168.0.8, 192.168.0.193 och 192.168.0.199 alla är online och svarar på vårt anrop. En fördel med ping är att det är installerat som standard i nästan alla UNIX-maskiner. Nackdelen är dock att långt ifrån alla datorer svarar på anrop till broadcastadressen. Vi skulle därför kunna loopa (repetera) vanliga ping-förfrågningar till alla möjliga adresser, men det blir snabbt onödigt krånligt. Låt oss därför titta på nästa metod – fping.

Metod 2 – fping

En bättre metod är att använda fping som är gjort just för detta ändamål. fping finns normalt inte installerat som standard men finns tillgängligt i de flesta distributioners pakethanterare. En enkel yum install fping eller apt-get install fping och programmet är färdigt att användas.

fping -ga 192.168.0.0/24 2>/dev/null

Ger följande resultat:

192.168.0.2
192.168.0.8
192.168.0.20
192.168.0.193
192.168.0.198
192.168.0.199
192.168.0.1
									

Notera förstås att vi får en betydligt bättre respons här än när vi pingade broadcastadressen! Växeln -a anger att vi vill se datorer som är online (alive) på nätverket. Växeln -g anger att vi ska pinga en lista angiven antingen efter nätmask (i vårt fall /24) eller efter startadress och slutadress. Exemplet kan alltså lika gärna skrivas så här:

fping -ga 192.168.0.0 192.168.0.255 2>/dev/null

Det sista 2>/dev/null anger att terminalen ska skicka felmeddelanden till /dev/null istället för till teminalprompten. Det gör vi för att slippa meddelanden av typ ”host unreachable” över hela skärmen, men vill man se alla meddelanden är det förstås bara att strunta i det sista stycket.

Metod 3 – nmap

nmap är ett multiverktyg för nätverksadministration och innehåller en hel uppsjö funktioner. Vanligaste användningsområdet är förmodligen att scanna efter öppna portar på en specifik adress. Det går dock alldeles utmärkt att scanna efter tillgängliga datorer också. Finns nmap inte förinstallerat i din distribution hittar du det i pakethanteraren. nmap finns även för Mac OS X och Microsoft Windows och laddas hem på nmaps hemsida. För att söka igenom det lokala nätverket med nmap skriver vi:

nmap -sn 192.168.0.0/24

Vilket ger oss något i stil med nedanstående till svar:

Starting Nmap 5.50 ( http://nmap.org ) at 2011-07-04 17:47 CEST
Nmap scan report for 192.168.0.1
Host is up (0.00091s latency).
Nmap scan report for 192.168.0.2
Host is up (0.00068s latency).
Nmap scan report for qnap.localdomain (192.168.0.8)
Host is up (0.016s latency).
Nmap scan report for b3.localdomain (192.168.0.20)
Host is up (0.0056s latency).
Nmap scan report for 192.168.0.193
Host is up (0.0027s latency).
Nmap scan report for 192.168.0.198
Host is up (0.048s latency).
Nmap scan report for 192.168.0.199
Host is up (0.047s latency).
Nmap done: 256 IP addresses (7 hosts up) scanned in 2.55 seconds
									

Växeln -sn anger att vi inte vill skanna portar utan endast leta efter andra datorer. Som synes  skriver nmap ut både ip-nummer och svarstid. Programmet tar dessutom en titt i /etc/hosts för att se om några av de datorer som upptäcks har ett hostname angivit där.

Slutligen

Att ta reda på vilka datorer som finns online på nätverket är förstås användbart i många sammanhang. Ovanstående är inte tänkt som en djupare ingång i varken pingfping eller nmap, men en snabb introduktion till hur programmen kan användas till just det. Frågor eller synpunkter? Lämna gärna en kommentar!