Datorintroduktion 2011

Teknisk fysik, Chalmers

Material till “Tillämpad UNIX”

Teckenkodning

Bokstäver, siffror och symboler kallas med ett gemensamt namn för tecken. Internt kan en dator emellertid bara hantera tal, så det behövs en teckentabell som säger vilket tecken som svarar mot vilket heltal. Till exempel svarar den stora bokstaven A mot talet 65 internt.

En gång i tiden hade alla datorsystem sin egen teckentabell. En för MS-DOS, en annan för Windows, ytterligare en annan för Mac OS, och så vidare. På motsvarande sätt fanns olika teckentabeller i olika språkområden. Det gick inte alltid att konvertera mellan dessa teckentabeller, för de innehöll olika tecken. (De flesta utgick från amerikansk ASCII, vilket betyder att engelska tecken fungerar överallt.)

Folk tröttnade på kaoset och införde en universell teckentabell, UCS (Universal Character Set), ibland kallad Unicode. UCS innehåller i princip alla tecken i världen. Eftersom alla tecken i världen är fler än vad man kan lagra i en byte, måste åtminstone somliga tecken ta mer än en byte. I den vanligaste teckenkodningen, UTF-8, tar de engelska tecknen en byte, medan å, ä och ö tar två byte. (Det finns också UTF-16, ibland kallad Unicode, där alla de vanliga tecknen tar två byte.)

På svenska Linux-system i allmänhet och på Chalmers i synnerhet brukade den teckenkodningen vara ISO 8859-1. Den ger möjlighet att skriva svenska tecken, men inte till exempel grekiska. Nu har man de flesta tillverkare av Linux-system bytt till UTF-8. På Chalmers bytte vi sommaren 2009.

Det innebär att det fortfarande finns en massa filer som använder ISO 8859-1, medan vi går över till UTF-8. Är teckenkodningen fel, så brukar märker du det genom att åäö inte fungerar. Det blir förstås jobbigt i övergångsperioden, men tanken är att vi kommer kunna slippa problem med olika teckenkodningar i framtiden, när nu alla kan använda samma.

Du kan konvertera från och till olika teckenkodningar med kommandot iconv. Till exempel iconv -f iso-8859-1 -t utf-8 file-in-old-encoding > file-in-new-encoding för att konvertera en fil från (-f) den gamla kodningen till (-t) den nya. Du kan också använda iconv i en UNIX-pipe, om ett gammalt program försöker prata med dig med fel teckenkodning (borde inte förekomma, egentligen): old-buggy-program | iconv -f iso-8859-1 -t utf-8.

Processhantering

Vad är en process? En process kan sägas vara ett program som körs. Det är inte alltid man är så noga med att hålla isär begreppen process och program, men vi ska göra ett försök att klarlägga skillnaden. Ett program är en serie instruktioner för datorn att utföra – vi kan likna ett program på hårddisken vid ett recept i kokboken. En process är ett program som håller på att köras; i receptliknelsen skulle vi kunna göra skillnaden mellan bakreceptet och själva bakprocessen. Två personer skulle kunna baka var sin kaka med samma recept – vi har två processer, men ett recept. På samma sätt skulle vi kunna starta två stycken terminaler, och vi skulle ha två terminalprocesser, fast vi bara startat ett program. Vi har startat det två gånger. Vi har två processer i gång. Detta avsnitt handlar om hur man hanterar processer i UNIX.

Du kan skriva in kommandon när du får upp kommandoprompten. Hur den ser ut kan variera, men den brukar sluta med dollartecken ($). När du har skrivit in ett kommando, till exempel ls, väntar skalet (Bash – det program som tolkar det du skriver och startar program åt dig) på att kommandot ska köra klart innan du får upp kommandoprompten igen. Man säger att kommandot kör i förgrunden. Normalt sett är detta vad du vill.

Det finns emellertid vissa kommandon som normalt sett kör ganska länge. Du kanske startar emacs, som då öppnar sitt eget fönster för sin kommunikation med användaren. Men i terminalen väntar Bash fortfarande på att kommandot ska slutföras, att Emacs ska avslutas, innan den ger dig tillbaka prompten. Eller du kanske har startat en process som bara tar lång tid att avsluta, men som du inte behöver interagera med efter att den startar. Under sådana omständigheter kan du vilja ha tillbaka din prompt utan att processen kört färdigt (utan att programmet behöver avslutas). I sådana fall kan du välja att köra processen i bakgrunden.

Om du vet redan på förhand att du vill lägga programmet i bakgrunden, kan du ange det genom att sätta ett och-tecken (&) sist på kommandoraden. Vill vi starta Emacs i bakgrunden kan vi skriva emacs&. Om vi i stället har en process i förgrunden, så att skalet inte vill ge oss tillbaka prompten, kan vi avbryta processen med C-z (d.v.s., håll nere CTRL och tryck på z). Detta stannar processen, som alltså inte kan fortsätta förrän vi säger till, och ger oss kommandoprompten tillbaka. Vi kan lägga den stoppade processen i bakgrunden med kommandot bg, eller lägga den i förgrunden med kommandot fg.

Du kan se vilka processer du lagt i bakgrunden med kommandot jobs. Processerna har fått ett jobb-nummer, som står inom hakparentes i listan. (Icke att förväxla med processens ID-nummer, även känt som PID.) Vill vi lägga ett konkret bakgrundsjobb, låt oss säga nummer 3, i förgrunden kan vi göra det med kommandot fg %3. Om vi blir arga på den, kan vi i stället döda den med kill %3.

Alla processer har också ett unikt process-ID (PID). Medan två processer som körs i bakgrunden på olika terminaler båda kan ha jobbnumret ett, har alla processer på datorn ett unikt PID. Dessa PID:er fungerar även de med kill, så om vi bestämmer oss för att process nummer 24248 är dum kan vi döda den med kill 24248 (nu utan procent-tecknet). Du kan se vilka processer som körs i din terminal, och vad de har för PID, med kommandot ps. Vill du se vilka processer som körs i alla terminaler kan du köra ps -a. Sedan kör du förmodligen också processer som inte är knutna till en terminal – för att se alla dina processer kör du, om du är inloggad som von, ps -u von (analogt med andra användarnamn). Vill du se allt kan du köra ps -e, där e står för everything. Flaggan -f ger dig extra utförlig information. Vill du se processerna ordnade efter vem som tar mest resurser från systemet, kan du i stället använda kommandot top.

Det kan tänka sig att en process inte vill dö när du kör kill. Den kan få för sig att den är upptagen och inte vill lyssna på dig. För egentligen är kill bara ett artigt sätt att be programmet att gå och lägga sig. Vill du döda det på riktigt, kan man döda det med signal nio, som det heter. Är process nummer 24248 riktigt dum kan du döda den med kill -9 24248. Då kommer processen inte ens att underrättas om vad som håller på att hända, utan avbryts tvärt. Man bör alltid försöka med bara kill innan man försöker med kill -9.

Normalt sett avslutas alla processer när du loggar ut. Men om du har något fint beräkningsprojekt på gång, kan du vilja att datorn fortsätter räkna även efter att du loggat ut (du vet väl att man inte får låsa skärmen i mer än 20 minuter?). Lyckligtvis finns det kommandon även för detta. Kommandot screen ger dig en miljö som överlever en utloggning, om man gör på rätt sätt. Du startar screen genom att skriva just screen, och sedan startar du de program som du vill ska fortsätta köras efter att du loggat ut. Du kan sedan koppla ifrån med C-a d (håll nere CTRL, tryck a, släpp upp allt, tryck d). Nu kan du logga ut. När du vill komma tillbaka kör du screen -r på samma dator, och du kommer tillbaka till den miljön du lämnade för att, förhoppningsvis, kunna beskåda ett färdigräknat resultat. (Chalmers har sedermera komplicerat saker ytterligare, med Kerberos-biljetter och sådant. Kolla in Chalmers IT-Service' sida om långa bakgrundsjobb.)

Nu är det dock inte helt oproblematiskt att lämna en beräkningsprocess på en dator som andra kommer att logga in på – vi vill ju inte att datorerna ska bli slöa för att det körs en massa beräkningsprocesser på dem. Därför finns kommandot nice. Det kommandot används för att ange att ett annat kommando ska vara trevligt och tillmötesgående gentemot andra program. Ett nice:at program kommer alltid att lämna företräde till andra processer som inte är lika nice:a. Det finns 19 nivåer av nice:ighet, och ytterligare 20 nivåer av otrevlighet (negativa nice-värden) som bara systemadministratören kan använda. Vill du starta ett beräkningsprogram är det lämpligt att använda högsta möjliga nice-nivå: nivå 19. Du gör det genom att köra nice -n 19 mitt_program, där mitt_program är det program du vill starta med detta nice-värde. Notera att du använder nice när du vill starta ett program, inte på program som redan körs. För dem finns kommandot renice.

Virtuella konsoller

På ett Linux-system finns det ofta flera virtuella konsoller. Sex av dessa brukar vara textterminaler som man kan logga in på, och den sjunde brukar vara det grafiska användargränssnittet. Du byter till konsol nummer två med tangentkombinationen CTRL+ALT+F2 och tillbaka till det grafiska gränssnittet med CTRL+ALT+F7. De virtuella konsollerna kan vara användbara om det grafiska systemet av någon anledning blir oanvändbart – det kan vara att något program har kraschat och du måste komma till en terminal för att köra kill, eller kanske att någon annan är inloggad och har låst skärmen.

I det senare läget kan man förstås logga ut vederbörande med CTRL+ALT+Backspace, men då förlorar han allt han inte hade sparat. Det kan vara artigare, om personen inte syns till, att helt enkelt gå till en virtuell konsol och logga in där istället. Då hamnar du förstås i textläge, men om du vill komma in grafiskt kan du förslagsvis använda kommandot exec startx -- :1. Glöm inte att byta tillbaka med CTRL+ALT+F7 när du är klar.

Programpaket och packade filer

Säg att du hittar något intressant program, som survivor-programmet som finns på kurshemsidan, som du vill ladda hem och köra. Sannolikt får du programmet i form av förpackad källkod.

Programfiler måste förstås se ut på ett visst sätt för att datorn ska förstå dem. Häri ligger en grundläggande konflikt för den som skriver program – ett program som datorn har lätt att förstå har en människa svårt att förstå. För de flesta är ett program mest bara en följd ettor och nollor som vi inte ser något mönster bakom. Att sitta och skriva dessa ettor och nollor för hand så att datorn förstår vad den ska göra, det är alldeles för jobbigt. Människor vill skriva saker med bokstäver, i ett programmeringsspråk, och sedan ha en speciell tolk (kallad kompilator) översätta den till datorns språk. De filer människan skriver kallas programmets källkod. Om du laddar ner ett program för Linux från nätet kommer du med all sannolikhet att få programmets källkod.

Förpackad källkod. Ett program är ofta många filer, men man vill inte ladda ner många filer. Därför finns programmet tar som låter dig förpacka flera filer tillsammans i en enda tar-fil. Helt okomprimerat; därför låter man ofta ett komprimeringsprogram som gzip eller bzip2 komprimera tar-filen, så att man får en lite mindre tar.gz- respektive tar.bz2-fil. Även du kan förpacka dina filer med tar om du vill skicka flera saker i ett paket, så att säga.

För att packa upp en tar-fil, kör du tar -xf filnamn.tar. För en tar.gz-fil, kör du tar -xzf filnamn.tar.gz. För en tar.bz2-fil, kör du tar -xjf filnamn.tar.gz. Flaggan -x står för extract (packa upp), flaggan -f står för file och anger filens namn. Vill du skapa filer istället, gör du på samma sätt, fast byter ut -x mot -c (create) och skriver namnet på den katalog du vill förpacka efter det blivande filarkivets namn.

För att kompliera källkoden, d.v.s. för att få ett körbart program, följer man instruktionerna i filen INSTALL som alltid brukar finnas. Den brukar säga åt dig att köra ./configure följt av make, men ibland ska man bara köra make. Sedan, för att köra programmet, måste programfilen normalt sett ligga i någon av en handfull fördefinierade kataloger, men man kan komma runt detta genom att stå i programmets katalog och köra ./mitt_program, om mitt_program är namnet på programfilen.

Det kan tyckas lite omständligt att installera program så här, jämfört med vissa system där det räcker att bara klicka på en nedladdad fil. För det mesta innehåller Linuxdistributionen massor med program som man kan installera bara genom något klick, eller rätt kommandorad. Ofta kan man på detta enklare sätt få allt man kan tänkas vilja ha. Men det är lite mer omständligt att installera tredjepartsprogram, vilket kanske kan bidra till att det finns så få virusar för Linux.

Alias

Många kommandon kan ändra beteende beroende på vilka flaggor man ger dem. Detta kan vara ett utmärkt sätt att ange olika inställningar. Vissa inställningar kan man dock vilja ha jämt, utan att man vill skriva in den aktuella flaggan varje gång man kör det. Med alias kan du ange att ett visst kommando alltid ska få vissa flaggor, utan att du behöver skriva ut dem.

Motiverande exempel

XFig är ett bra program för att rita figurer när man gör rapporter med LaTeX (den blir en senare föreläsning). Du startar XFig med kommandot xfig.

XFig med tum som enhet

Det finns emellertid några problem med XFigs standardinställningar. Som synes i figuren vill XFig använda tum som enhet — usch! Men som tur är kan man ändra till vettiga enheter: xfig -metric.

XFig vill också gärna visa en massa versionsinformation när den startar upp. Tycker man inte om det kan man stänga av det: xfig -nosplash.

En av de stora fördelarna med att använda just XFig till att rita sina figurer när man jobbar med LaTeX är att man kan skriva LaTeX-kod direkt i sin figur och få LaTeX att tolka den. Det gör det möjligt att ha till exempel matematiska formler mitt i sin figur, om man kan skriva formeln med LaTeX-kod. Detta förutsätter dock att texten markeras som speciell, att man använder LaTeX-fonter och att man exporterar bilden i det förnämliga formatet pstex. Som tur är finns det flaggor för det: xfig -specialtext -latexfonts -exportLanguage pstex.

Summa summarum blir kommandot för att starta XFig “xfig -metric -nosplash -specialtext -latexfonts -exportLanguage pstex”.

XFig med cm som
enhet

Kör man sedan LaTeX på ovanstående bild blir resultatet det här:
triangel med matematisk
formel

Detta illustrerar behovet av alias. Vi vill kunna starta XFig, inklusive alla våra fina inställningar, med kommandot xfig, utan att behöva skriva in en halv uppsats flaggor.

Att skapa alias

För att skapa ett nytt temporärt alias, är kommandot “alias xfig='xfig -metric -nosplash ...'”, i det ovanstående exemplet. Vill man permanenta sitt alias, kan man lägga in kommandot i filen “.bashrc” i sin hemkatalog. I den filen kan du skriva kommandon som du vill ska köras varje gång skalet (bash) startas. Skriv ett kommando per rad, t.ex. kommandot alias xfig='xfig -metric ...' (där du ersätter punkterna med alla flaggorna enligt ovan).

Chalmers har även en fil “~/.vcs4/bashrc” som ibland kan komma att köras istället för “~/.bashrc”. För att få alias även då Chalmers' fil körs, kan man lägga dem där också.

Variabler

I datorsammanhang är en variabel en plats i datorns minne. Variabeln har ett namn och ett värde, där värdet är det som står på motsvarande ställe i minnet. Du kan skapa en variabel genom att skriva kommandot “VAR=value”. Det skapar variabeln VAR och ger den värdet value. Du kan sedan använda variabeln när du skriver andra kommandon: “ls $VAR” skulle till exempel automatiskt översättas till “ls value” innan kommandot körs (varvid det visar innehållet i katalogen value om en sådan existerar, filen value om den existerar, eller ger ett felmeddelande). Genom att sätta ett dollartecken framför variabelns namn sätter skalet, bash, automatiskt in variabelns värde på det angivna stället.

En variabel du skapar i skalet kommer bara gälla just precis den processen. Skal som körs i andra terminaler kommer inte vara medvetna om den, och inga av de program du startar från skalet kommer kunna komma åt den. Vissa variabler är dock speciella på det viset att programmen kan tänkas vilja komma åt dem: sådana variabler kallas miljövariabler. Ett exempel på en variabel som du kan vilja göra tillgänglig inte bara för skalet, utan även för alla program som startas därifrån, är variabeln “LC_ALL” som anger vilket språk du vill att programmen ska prata. För att skapa eller ändra en miljövariabel, använder du kommandot “export”. Till exempel gör kommandot “export LC_ALL=sv_SE.UTF-8” att alla program som startas från skalet börjar prata svenska med dig.

En annan mycket speciell variabel är variabeln “PS1”. Det är den som innehåller din prompt. I den kan du även skriva vissa specialkoder, som får bash att visa vilken katalog som är den nuvarande arbetskatalogen i själva prompten, eller ger annan intressant information. För att se en lista på specialkoder du kan ha i PS1-variabeln, kör “man bash” och sök efter PROMPTING (skriv “/PROMPTING” i manualbladet). Man kan även ha olika färger i sin prompt. Vill du göra intressanta saker rekommenderas the Bash Prompt HOWTO.

Ändrar man på en variabel gäller det bara tillfälligt. Vill man ändra permanent kan man lägga in det i sin “~/.bashrc”-fil. Det fungerar precis som det står beskrivet i avsnittet om alias ovan (glöm inte bort “~/.vcs4/bashrc”). Bara skriv kommandot för att ändra variabeln på en egen rad i din “.bashrc”.

Loopar

for-loopen

for i in a b c
do
echo $i
done

En for-loop kan användas när du vill köra ett kommando (i exemplet ovan echo $i) flera gånger för olika värden på variabeln i.

Vill man slippa skriva in listan på värden variabeln ska anta själv, finns det några speciella skrivsätt man kan använda. * matchar alla filer i nuvarande katalogen, *.tex matchar alla filer som slutar på .tex. Måsvingar med kommatecken ersätts i tur och ordning av det som står uppräknat inom dem; till exempel ersätts {min,din,Bernhards}_fina_fil med listan min_fina_fil din_fina_fil Bernhards_fina_fil. Sätter du två tal inom måsvingar, åtskilda med dubbelpunkt, kommer måsvingarna ersättas med talen däremellan, i tur och ordning. Så blir hej_{1..5} automatiskt översatt till listan hej_1 hej_2 hej_3 hej_4 hej_5.

Denna typ av for-loop, där man gör saker för varje element i en lista, kallas ibland en loop av foreach-typ. Den typen av loopar återfinns i flera programmeringsspråk, däribland Matlab, GNU Octave och PHP. Ibland använder programmeringsspråket också ordet foreach istället för for för att skriva loopar av denna typ. (Notera dock att man fortfarande skriver for, inte foreach i Bash.)

Det finns nämligen en annan vanlig typ av for-loopar, den typ som används i programmeringsspråket C (som i sin tur inspirerat språk som C++, Java, PHP, med flera). Denna typ kallas också tre-uttrycks for-loopar, eftersom loopen kontrolleras av tre uttryck man specificerar inom parenteser i början av loopen. En sådan loop kan se ut så här:

for ((i=0; i < 10; i=i+2))
do
echo $i
done

Det första uttrycket inom parentesen sätter en variabel till sitt begynnelsevärde. Det görs allra först. Sedan kontrolleras jämförelsen i det mittersta uttrycket, och är det sant utförs kommandona i for-loopen (i detta fallet kommandot echo $i). Sist görs det sista uttrycket, här i=i+2. Detta är inte en jämförelse, ingen matematisk likhet. Datorn räknar först ut högerledet genom att addera två till variabeln i. Sedan ändrar datorn vänsterledet, så att det får det värde som datorn räknade fram i högerledet. Sedan går datorn vidare till mittersta uttrycket igen, och om sant, till kommandona inne i loopen.

while-loopen

while command
do something
done

I en while-loop utförs ett eller flera kommandon (something ovan) om och om igen, så länge ett annat kommando (command ovan) lyckas med det som detta kommando ska utföra. Till exempel:

while ps 5061 > /dev/null
do
echo still waiting
sleep 1
done

Kommandot ps 5061 försöker visa information om process nummer 5061. Detta kommer bara att lyckas så länge process 5061 kör. När 5061 avslutas träder vi ur while-loopen. > /dev/null är en omdirigering, som tar det som ps 5061 normalt sett hade skrivit till skärmen och skriver det till filen /dev/null istället (en specialfil som man kan skriva vad som helst till utan att något sparas). Så, så länge 5061 körs, kommer kommandona echo still waiting (som skriver “still waiting” till skärmen) och sleep 1 (som väntar i en sekund utan att göra något) att köras. (För att koden ska vara användbar förutsätts förstås att det finns en process nummer 5061 som man väntar lite otåligt på.)

Kontrollkommandot är förstås oftast inte ps. Ett av de vanligaste kontrollkommandona är kommandot test, som även går under namnet [. Ja, det finns ett kommando som heter vänster hakparentes. Det är synonymt med kommandot test, med den skillnaden att vänster hakparentes vill ha en höger hakparentes i slutet av raden. Det finns många olika villkor man kan testa med test eller hakparenteser – ta en titt på manualbladet, man test för att se en lista. Till exempel kan man jämföra tal med varandra, som så:

k=10
while [ $k -gt 0 ]
do
factor $k
k=$((k-2))
done

Som vi nämnt tidigare, kommer $k ersättas med värdet av variabeln k. Första gången testuttrycket körs kommer det alltså köras som [ 10 -gt 0 ]. Flaggan -gt betyder greater than, och eftersom 10 är större än 0, kommer hakparenteskommandot att lyckas. De båda kommandona inne i while-loopen kommer köras för k=10. Det sista kommandot, k=$((k-2)), förtjänar en extra förklaring. Dubbla runda parenteser används i Bash närhelst man vill göra en beräkning. $((k-2)) kommer tolkas som $((10-2)) och resultatet blir att kommandot k=8 körs, vilket ändrar värdet på variabeln k. Sedan körs kontrollkommandot igen, och eftersom 8 är större än 0 körs loopen ett varv till. Så småningom körs den för k=2, varvid k=$((k-2)) ändrar värdet på k till noll, och när test-kommandot körs nästa gång körs det som [ 0 -gt 0 ]. Eftersom noll inte är större än noll misslyckas kommandot, vilket leder till att loopen avbryts.

Christian von Schultz <christian.von.schultz@chalmers.se>