next up previous


Introduktion till
GNU/Linux

Version 0.7.2

Denna bok finns tillgänglig på följande adresser:


Copyright © 1997,1998 Göran Andersson <goran@debian.org>
Copyright © 1998 Kalle Andersson <kalle.andersson@mbox303.swipnet.se>

Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.

Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.


Innehåll

I. Användarguide

1. Introduktion till UNIX

1.1 Inloggning

Varje person som använder en UNIX-dator har ett unikt namn, ett så kallat användarnamn. Det första man gör då man ska börja arbeta vid datorn är att tala om vem man är genom att skriva sitt användarnamn. Därefter måste man bevisa att man verkligen är den person man påstår genom att ange ett lösenord. Denna process, där man anger sitt användarnamn och sitt lösenord, kallas för inloggning. Innan man kan logga in på en viss dator måste man ha fått ett användarnamn och ett preliminärt lösenord av systemadministratören, den person som är ansvarig för skötseln av datorn. (Systemadministratören har användarnamnet root.) Det preliminära lösenordet bör man så snart som möjligt ändra till något som bara man själv känner till.

  xdm.gif Det finns två olika sätt att logga in: via en textterminal eller via fönstersystemet X (se kapitel 4 för information om X). I det förra fallet är skärmen i textläge, och en markör blinkar efter orden fafner login: (''fafner'' är datorns namn). I det senare fallet ser det ut ungefär som i figur 1.1. Det finns oftast sex olika textterminaler att logga in genom. Vissa UNIX-system har inte någon grafisk inloggningsskärm, andra har en eller till och med flera. Man kan växla mellan de olika inloggningsskärmarna genom att hålla tangenterna Ctrl och Alt nedtryckta och samtidigt trycka på en av funktionstangenterna - F1 till F6 ger textterminaler medan F7, F8 osv ger X-skärmar på de datorer som kör X.fafner login: göran
Lösenord:
$
I exemplet ovan skrev datorn först ut texten fafner login:, varefter jag angav mitt användarnamn ''göran'' och tryckte på returtangenten. Datorn frågade sedan efter mitt lösenord. Lösenordet syns inte på skärmen då man skriver in det för att ingen ska kunna se det ''över ens axel''.

Motsatsen till inloggning är utloggning. När man är klar med sitt arbete vid datorn och inte tänker använda den igen på ett tag, så kan man logga ut med kommandot logout.

Vad som sker efter inloggningen beror på hur systemet är inställt. Även om man inte loggar in via fönstersystemet X, så kanske X startas automatiskt då man loggat in. I vilket fall som helst startas förmodligen en kommandotolk, genom vilken man kan ge datorn instruktioner. Datorn visar att den är redo för ett nytt kommando genom att skriva ut den så kallade prompten, som fortsättningsvis anges med ett dollartecken. Man kan konfigurera många av inställningarna i UNIX, dvs ändra dem så att de passar en själv bättre. Detta gäller även prompten, så hur den faktiskt ser ut varierar starkt från system till system. Andra vanliga sätt att ange prompten är till exempel som # eller som fafner>.

Låt oss som första exempel ta kommandot passwd, som man använder för att byta lösenord. Man skriver kommandot vid prompten, och trycker sedan på returtangenten:$ passwd
Nuvarande lösenord:
Nytt lösenord:
Upprepa det nya lösenordet:
Lösenordet har ändrats.
$
Inte heller nu återges lösenordet på skärmen när man skriver in det. Detta innebär en viss risk för att man råkar skriva fel utan att upptäcka det, så för att vara på den säkra sidan får man upprepa det nya lösenordet innan det ändras. Dessutom måste man först bevisa att man kan det gamla lösenordet.

I det här sammanhanget kan det vara värt att nämna att UNIX ibland kan vara tämligen kärvt; kommandonamn är ofta kryptiska förkortningar som passwd i stället för password, som cp i stället för copy och, det kanske värsta exemplet, umount i stället för unmount. Detta tilltalar rutinerade användare, som kan skriva sina kommandon snabbare tack vare att det blir färre tangentnedslag, men kan upplevas som frånstötande eller svårtillgängligt av nybörjare. (Denna brist på inställsamhet kommer sig bland annat av att UNIX skapades innan datorer fanns i varje hem och operativsystem blev kommersiella massprodukter.) Om man inte är nöjd med hur ett visst kommando skrivs, så kan man dock ändra det genom att ange ett alias för kommandot:$ alias lösenord=passwd
$ lösenord
Nuvarande lösenord:

Vilken fantastisk känsla det är att vara inloggad på ett UNIX-system! Låt oss genast prova några kommandon. Vad är klockan nu?$ date
tis sep 2  15:13:52 CEST 1997
$
Klockan är alltså snart kvart över tre. Ovan står ''CEST'' för centraleuropeisk sommartid. Vi kan få almanackan utskriven med hjälp av kommandot gcal. Men det räcker inte att skriva gcal; vi måste också tala om vilken månad och vilket år det gäller. Dessa upplysningar anges efter gcal. De ska separeras med ett eller flera mellanslag. Sådana ''ord'' kallas för argument till kommandot.$ gcal 1 2000

januari 2000

måndag        3 10 17 24 31
tisdag        4 11 18 25
onsdag        5 12 19 26
torsdag       6 13 20 27
fredag        7 14 21 28
lördag     1  8 15 22 29
söndag     2  9 16 23 30

Ett lustigt litet kommando, echo, har som enda funktion att upprepa sina argument. Detta kan tyckas meningslöst, men vi ska senare se att kommandot kan vara mycket användbart.$ echo hejsan svejsan
hejsan svejsan
$

Kommandot man program ger en bruksanvisning för kommandot program.$ man passwd
PASSWD(1)                                               PASSWD(1)

NAME
       passwd - change user password
SYNOPSIS
       passwd [-f|-s] [name]
       passwd [-g] [-r|R] group
       passwd [-x max] [-n min] [-w warn] [-i inact] name
       passwd -l|-u|-d|-S name

DESCRIPTION
       passwd  changes  passwords for user and group accounts.  A
       normal user may only change the  password  for  their  own
       account,  the  super  user may change the password for any
       account.  The administrator of  a  group  may  change  the
       password  for  the  group.   passwd  also  changes account
       information, such as the full  name  of  the  user,  their
       login shell, or password expiry dates and intervals.

   Password Changes
       The  user is first prompted for their old password, if one
       is present.  This password is then encrypted and  compared
       against the stored password.  The user has only one chance
       to enter the correct password.  The super user is  permit-
       ted to bypass this step so that forgotten passwords may be
       changed.
Detta är inledningen av bruksanvisningen till passwd. För att se mer av den ska man trycka på mellanslagstangenten, för att avsluta ska man trycka på q. (I UNIX barndom var man den viktigaste informationskällan för samtliga kommandon, men så är det inte längre. Andra, modernare former att presentera bruksanvisningar håller på att ta över. Symptomatiskt nog har ingen ännu brytt sig om att översätta dessa bruksanvisningar till svenska.)

Vissa kommandon ger en kort introduktion till sig själva om man anropar dem med -h eller --help som argument. (Ett sådant argument, som inte ger mer information till kommandot utan modifierar dess verkan, kallas för en flagga. Flaggor består vanligtvis av ett streck följt av en bokstav eller två streck följt av ett ord.)$ date --help
Användning: date [FLAGGA]... [+FORMAT]
     eller: date [FLAGGA] [MMDDhhmm[[ÅÅ]ÅÅ][.ss]]
Visa aktuell tid på angivet FORMAT, eller ställ systemklockan.

  -d, --date=STRÄNG        visa tid som beskrivs av STRÄNG, inte "nu"
  -f, --file=DATUMFIL      samma som --date en gång per rad i DATUMFIL
  -r, --reference=FIL      visa när FIL senast modifierades
  -R, --rfc-822            skriv ut RFC-822-kompatibel datumsträng
  -s, --set=STRÄNG         sätt tiden enligt STRÄNG
  -u, --utc, --universal   skriv ut eller sätt Coordinated Universal Time
      --help               visa denna hjälptext och avsluta

Det vanliga är att en rad innehåller exakt ett kommando, men man kan skriva flera kommandon på samma rad om de separeras med semikolon. Likaså kan det inträffa att ett kommando är flera rader långt. Om man skriver ett bakvänt snedstreck i slutet av en kommandorad, så tolkas det som att kommandot fortsätter på nästa rad. Man får då en ny prompt (som kan se annorlunda ut än den vanliga) så att man kan skriva färdigt kommandot.$ echo Dagens datum är ; date
Dagens datum är
tis sep 2  16:44:29 CEST 1997
$ echo Detta är ett mycket \
> långt kommando
Detta är ett mycket långt kommando
$
När man separerar två kommandon med semikolon som ovan, så fungerar det precis som om man hade gett det första kommandot, sedan tryckt retur och till slut gett det andra kommandot. Det andra kommandot kör alltså inte igång förrän det första är klart.

1.2 Att redigera kommandon

När man ska skriva in ett kommando, befinner man sig i en enkel textredigerare som hanterar endast en rad i taget. Denna textredigerare heter Readline. I det här avsnittet ska vi beskriva hur Readline fungerar.

De viktigaste Readline-kommandona återges i tabell 1.1. Där det står tex C-a menar vi att man ska man trycka ned kontrolltangenten, hålla den nedtryckt och samtidigt trycka a. Med M-f menas att man ska hålla metatangenten nedtryckt och samtidigt trycka på f. Kontrolltangenten ser troligtvis ut som Ctrl och metatangenten som Alt. Om man inte finner metatangenten, eller om den inte fungerar som den ska, så kan man som ersättning för denna trycka och släppa tangenten Esc. Med C-M-s menas att man ska hålla kontroll- och metatangenterna nedtryckta, och samtidigt trycka på s.


 
Tabell 1.1: Readlinekommandon.
 
Kommando Förflyttning Kommando Förflyttning
C-b Ett steg bakåt C-f Ett steg framåt
M-b Ett ord bakåt M-f Ett ord framåt
C-a Till början av raden C-e Till slutet av raden
Kommando Raderar... Kommando Raderar...
$\longleftarrow$ tecknet bakom markören C-d tecknet under markören
M- $\longleftarrow$ ordet bakom markören M-d ordet framför markören
C-u all text bakom markören C-k all text framför markören
C-w texten från närmast föregående blanktecken fram till markören
Kommando Funktion Kommando Funktion
C-y Klistra in M-y Välj äldre urklipp
C-t Kasta om bokstäver M-t Kasta om ord
C-_ Ångra C-l Rensa skärmen
TAB Komplettera M- nr Kör kommando nr gånger
C-p Föregående kommandorad C-n Nästa kommandorad
M-< Äldsta kommandoraden M-> Senaste kommandoraden
C-r Sök bakåt C-s Sök framåt

Den översta delen av tabell 1.1 visar hur man flyttar markören. Man går ett steg bakåt med C-b och ett steg framåt med C-f. Det är lätt att komma ihåg dessa kommandon eftersom de använder bokstäverna ''b'' som i bakåt och ''f'' som i framåt. Man kan också använda piltangenterna för att gå till vänster eller höger. Man kan förflytta sig ett ord bakåt eller framåt med M-b respektive M-f. Man kommer till början av raden med C-a och till slutet av den med C-e.

Readline har ett mycket användbart ''ångerkommando''. Trycker man C-_, så tas den senaste ändringen man gjort tillbaka. Genom upprepad användning av C-_ kan alla ändringar man gjort tas tillbaka, ända ned till den ursprungliga tomma raden. Kommandot C-x C-u, dvs C-x följt av C-u, är en synonym för C-_.

Ett annat användbart kommando är C-w, som raderar hela det ''ord'' som står bakom markören. Om ett kommando raderar flera tecken åt gången, så säger man att den raderade texten har klippts ut. Readline sparar alla urklipp. Därigenom kan det som klippts ut klistras in igen - när som helst, var som helst och hur många gånger som helst. Kommandot för att klistra in det senaste urklippet är C-y. Om man direkt efter C-y trycker M-y, så återfår man det näst senaste urklippet i stället för det senaste. Man kan upprepa detta och trycka M-y flera gånger för att få tillbaka äldre urklipp.

Men inte bara urklipp sparas, utan även de rader man skrivit in. Den förra kommandoraden man skrev in återfås med C-p eller genom att man trycker på piltangenten som pekar uppåt. Trycker man C-p igen, så återfår man den näst senaste kommandoraden osv. Man kommer till den äldsta bevarade kommandoraden genom att trycka M-<. Efter att ha gått några steg bakåt på detta sätt kan man gå tillbaka till den närmast yngre kommandoraden genom att trycka C-n eller piltangenten som pekar nedåt. För att återvävda till den yngsta kommandoraden, den som man höll på att redigera, kan man trycka M->.

Kommandot C-t kastar om de två bokstäver som står närmast markören. Detta är inte så vansinnigt som det kanske förefaller. Det händer nämligen ofta att man råkar skriva två bokstäver i fel ordning, särskilt om man är snabb vid tangentbordet. Så om man tex råkar skriva ''särskitl'' i stället för ''särskilt'' så kan man snabbt rätta felet genom att placera markören över bokstaven l och trycka C-t. Detta går mycket snabbare än att först radera ''tl'' och sedan skriva ''lt''. Kommandot M-t kastar istället om två ord.

Man kan söka bakåt bland de kommandorader man tidigare skrivit genom att trycka C-r och skriva in ett fragment av det kommando man söker. Om jag till exempel trycker först C-r och sedan h, så återfår jag kommandoraden date -help eftersom det är den senaste kommadoraden bland dem jag skrivit som innehåller texten ''h''. Trycker jag sedan e blir det ingen skillnad eftersom date -help innehåller texten ''he''. Men när jag därefter trycker j, så återfår jag istället kommadoraden echo hejsan svejsan eftersom den är den senaste som innehåller fragmentet ''hej''. Om jag då trycker på returtangenten, så utförs detta kommando. Sökningen avslutas också om jag trycker Esc, men kommandot utförs då inte genast, utan jag får möjlighet att redigera det först. Jag kan också trycka C-r igen för att återfå en äldre kommadorad som innehåller fragmentet ''hej''.

En annan viktig finess är att om man skriver de första tecknen i ett filnamn och sedan trycker på tabulatortangenten, så fyller Readline på med resten av filnamnet om det finns någon fil i den nuvarande katalogen vars namn börjar på det sättet. (Filer och kataloger diskuteras i de följande avsnitten.) Finns det flera filer vars namn börjar likadant, så tutar Readline till, och man får komplettera med ytterligare någon bokstav och därefter trycka på tabulatortangenten igen. Motsvarande gäller också för program- eller kommandonamn: om man skriver pass och sedan trycker på TAB, så kompletteras detta till passwd.

Om man ger Readline-kommandot M-nummer, dvs trycker ned metatangenten och samtidigt skriver talet nummer, så utförs nästa Readline-kommando nummer gånger. (Vissa Readline-kommandon tolkar dock nummer på ett annat sätt medan andra helt ignorerar det.) Kommandot M-8 följt av ett mellanslag skriver alltså ut åtta stycken mellanslag. Kommandot M-5 följt av C-_ tar tillbaka de fem senaste ändringarna. Kommandot M-1 2 följt av C-b flyttar markören tolv steg bakåt.

Readline konfigureras genom att man skriver in speciella kommandon i en fil med namnet .inputrc i sin hemkatalog. För att Readline ska behandla tecknen åäö korrekt, kan man behöva ha följande rader i den filen:

set convert-meta off
set input-meta on
set output-meta on

Readline har sina begränsningar; musen kan inte användas och bara en rad i taget kan redigeras. Dessa begränsningar hävs om man ger kommandona inifrån programmet Emacs.

1.3 Filer

UNIX lagrar information i filer. En fil kan till exempel innehålla en text, en bild eller ett datorprogram. Filerna i sin tur är inplacerade i kataloger. En katalog är alltså en förvaringsplats för filer. Varje fil och katalog har ett namn, som man använder då man refererar till dem.

Alla användare har en hemkatalog där de egna filerna lagras. Direkt efter inloggningen är hemkatalogen nuvarande katalog, dvs man ''befinner sig'' i sin hemkatalog. Kommandot ls ger en lista över filerna i den nuvarande katalogen.$ ls
lenngren     måltidssång
$
Tydligen har jag två filer i min hemkatalog. Den ena heter lenngren, den andra måltidssång. (Om ls inte skriver ut tecknen åäö korrekt, så kan man ange flaggan -N varje gång ls anropas. Ett alternativ sätt är att ge kommandot export LC_ALL=sv_SE. I kapitel 6 ska vi förklara detta närmare.)

Man kan kopiera filer med kommandot cp. $ cp måltidssång bellman
$ ls
bellman      lenngren     måltidssång
$
Ovan kopierade jag först filen måltidssång och gav kopian namnet bellman. Därefter kontrollerade jag att det gick väl genom att ge kommandot ls. Utskriften visar att det verkligen finns en ny fil med namnet bellman.

Om man anropar ls med flaggan -l, så ges utförligare information om filerna. Bland annat kan man utläsa hur stora filerna är och när deras innehåll senast ändrades.$ ls -l
total 3
-rw-rw-r--   1 göran    göran         372 feb  1 16:48 bellman
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
-rw-rw-r--   1 göran    göran         372 feb  1 15:47 måltidssång
$
Filerna bellman och måltidssång innehåller vardera 372 tecken, lenngren innehåller 649 tecken. Det är klart att bellman och måltidssång är lika stora eftersom bellman är en kopia av måltidssång. Senast filen bellman ändrades var den 1 februari, klockan 16.48. Raden med texten total 3 anger hur mycket lagringsutrymme filerna i katalogen förbrukar. Vad de övriga delarna av utskriften ovan betyder ska vi snart berätta.

Man kan byta namn på filer med kommandot mv.$ mv måltidssång epistel21
$ ls
bellman    epistel21  lenngren
$
Som synes har nu måltidssång bytt namn till epistel21.

Kommandot för att radera en fil är rm.$ rm epistel21
$ ls
bellman   lenngren
$
Kontrollen med ls visar att filen epistel21 nu är borta. Du är kanske van vid att få en varning om att filen kommer att försvinna och ett krav på bekräftelse vid raderingskommandon? Så är det inte här. UNIX gör vanligtvis vad man säger utan att ställa några frågor, även om konsekvenserna kan vara ödesdigra. Observera också att det inte finns något sätt att få tillbaka en fil som raderats! Därför är det nödvändigt att man har säkerhetskopior av alla viktiga filer. En av systemadministratörens viktigaste arbetsuppgifter är att regelbundet, lämpligen dagligen, göra en säkerhetskopia av samtliga användares filer. Om en användare av misstag råkar radera en fil, så kan systemadministratören återställa filen till det skick den var i föregående dag. Bara de ändringar som gjorts det senaste dygnet går då förlorade. På detta sätt minskas risken för katastrofer betydligt.

Det ska nämnas att om man anger flaggan -i till rm, så ställs frågan om bekräftelse:$ rm -i bellman
rm: ta bort "bellman"? n
$ ls
bellman   lenngren
$
Om vi hade tryckt j i stället för n, så hade bellman oåterkalleligen raderats. Man kan, om man så önskar, ställa in det så att rm alltid frågar innan någon fil raderas. Men då är risken stor att man efter ett tag får som vana att mekaniskt trycka j vid varje sådan fråga, och då ger frågan inte längre något skydd.

För att skriva ut innehållet i en fil ordagrant kan man använda cat.$ cat bellman
Så lunka vi så småningom
från Bacchi buller och tumult,
när döden ropar: Granne, kom,
     ditt timglas är nu fullt.
Du gubbe, fäll din krycka ner,
och du, du yngling, lyd min lag:
den skönsta nymf, som åt dig ler,
     inunder armen tag.
Tycker du, att graven är för djup,
nå välan, så tag dig då en sup,
tag dig se'n dito en, dito två, dito tre,
     så dör du nöjdare.
$

Kommandot wc räknar antal rader, ord och tecken i en given fil.$ wc bellman
     12      71     372 bellman
$
Utskriften från wc betyder att bellman innehåller tolv rader, 71 ord och 372 tecken (varje radslut räknas som ett tecken).

Om man vill studera en fil som innehåller för många rader för att de ska få plats på skärmen samtidigt, så är cat inte ett idealiskt kommando. Istället kan man använda more.$ more lenngren
Vår prost jag häromdagen såg
en morgon, då han ännu låg
matt utstäckt mellan tvenne lakan.
Hans kinder hade rosens färg,
hans runda armar hull och märg,
och magen, kullrig som ett berg,
sig hävde upp mot isterhakan.
Vid sängen stod ett bord, där denne andans man
sin frukost redan färdig fann
av smör och kycklingar, så läcker och så härlig.
Hans vördighet grep saken an
-More-(57%)
När skärmen fyllts med text, stannar more upp och skriver texten -More-(57%) nederst på skärmen. För att få se mer av filen kan man då trycka på mellanslagstangenten. (Kan du gissa varför kommandot more heter som det gör?) Om man i stället trycker på b, så går more bakåt i filen. Tryck på q för att avsluta, h för hjälp. Man kan göra mycket mer med more, något vi återkommer till senare. Många föredrar att använda kommandot less, som är en mer avancerad version av more.

Kommandona head och tail skriver ut början respektive slutet av en given fil. Så här får man se de sista tio raderna i filen lenngren:$ tail lenngren
av smör och kycklingar, så läcker och så härlig.
Hans vördighet grep saken an
och fann likören rätt begärlig.
Sen han nu visat mycken nit
med klunk för klunk och bit för bit,
han åter sänkte sig på mjuka huvudgärden
och ropte: Store Gud, vad är vårt usla liv?
En ständig kamp mot synd och flärden.
O, Herre, mig Thin styrko giv
i thenna mödosamma världen!
$
Om tio rader är för lite eller för mycket, så kan detta ändras med hjälp av en flagga. De första fyra raderna av filen bellman skrivs ut så här:$ head -4 bellman
Så lunka vi så småningom
från Bacchi buller och tumult,
när döden ropar: Granne, kom,
     ditt timglas är nu fullt.
$

I UNIX tillåts vanligtvis inte att filnamn är längre än 256 tecken. Man brukar oftast använda filnamn som enbart består av små bokstäver, men om man vill så kan man använda mycket udda namn på sina filer.$ cp bellman 2+3
$ ls
2+3       bellman   lenngren
$ rm 2+3
$
UNIX skiljer på stora och små bokstäver; bellman och Bellman betraktas som vitt skilda filnamn.

Varje fil har en ägare. Ägaren till en fil avgör vem som får göra vad med filen. Det finns tre slags rättigheter som är förknippade med varje fil: rätten att läsa den (ta del av dess innehåll), rätten att skriva till den (ändra dess innehåll) samt rätten att exekvera filen (att köra den som ett program). Som ägare till en fil kan man sätta upp det så att man själv har rätt att göra mer med filen än andra. I synnerhet brukar man se till att man själv får ändra i filen, men ingen annan. Om filen innehåller känslig information (som till exempel ens kärleksbrev), så brukar man se till att andra inte ens får läsa filen. Förutom att varje fil har en ägare, så är också varje fil associerad med en grupp. Vad grupper är till för behöver vi inte tänka på just nu, utan det beskrivs i stället i avsnitt 7.1.

Det är inte möjligt att specificera exakt för varje möjlig användare vad de får och inte får göra med en viss fil. Man kan inte säga att eva får göra si och olle får göra så. I stället bestämmer man rättigheterna för tre kategorier av användare: för det första filens ägare, för det andra de resterande medlemmarna i den grupp filen är associerad med och för det tredje alla övriga. För att ta reda på vilka rättigheter som gäller använder man flaggan -l vid anrop av ls för att få utförlig information.$ ls -l
total 2
-rw-rw-r--   1 göran    göran         372 feb  1 16:48 bellman
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
$
Före den siffra (372 respektive 649) som talar om hur stor filen är, står det vem som äger filen och vilken grupp filen är associerad med. För filerna ovan heter såväl ägaren som gruppen göran. Betrakta nu den rad som beskriver filen bellman. Att det första tecknet är - visar att det rör sig om en vanlig fil (för kataloger står det i stället d på motsvarande plats, se nästa avsnitt). De följande tre tecknen rw- avgör vad filens ägare får göra med den. Tecknet r anger att ägaren får läsa den; tecknet w att ägaren får skriva över den och tecknet -, eller snarare frånvaron av ett x, att ägaren inte får exekvera den. Därefter kommer tre tecken, rw-, som avgör rättigheterna för de resterande medlemmarna i gruppen göran (i just detta fall finns dock inga andra gruppmedlemmar). De sista tre tecknen, r--, anger att övriga användare får läsa filen, men varken ändra i den eller exekvera den. Siffran efter skyddskoderna anger hur många hårda länkar det finns till filen, se avsnitt [*].

Kommandot för att ändra skyddskoder heter chmod. Om man vill att ägaren till filen fil ska ha skyddskoden rw- ger man kommandot chmod u=rw fil. Om man vill att gruppen ska ha skyddskoden r-- blir kommandot chmod g=r fil. Kommandot chmod o= fil ger ''övriga'' skyddskoden ---. Man kan göra allt detta med ett enda kommando om man vill:$ chmod u=rw,g=r,o= bellman
$ ls -l bellman
-rw-r-----   1 göran    göran         372 feb  1 16:48 bellman
$

Det finns också konstruktioner som o+rx för att ''övrigas'' rättigheter ska utökas med läs- och exekveringsrättighet, och ug-r för att ta bort läsrättigheten för ägaren och gruppen. I stället för ugo kan man skriva a.$ chmod a-r bellman
$ ls -l bellman
--w-------   1 göran    göran         372 feb  1 16:48 bellman
$ cat bellman
cat: bellman: Åtkomst nekas
$ chmod u+r bellman
$ ls -l bellman
-rw-------   1 göran    göran         372 feb  1 16:48 bellman
$

1.4 Kataloger

Kataloger kan inte bara innehålla filer, utan även andra kataloger som då kallas underkataloger till den givna katalogen. Så här tillverkar man en katalog med namnet dikter:$ mkdir dikter
$ ls
bellman   dikter/   lenngren
$
Att dikter är en katalog markeras i listan ovan med ett snedstreck efter dess namn. (Vanligtvis krävs det att man anropar ls med flaggan -F för att kataloger ska markeras så här. En annan flagga, --color, gör att filer och kataloger markeras genom att deras namn skrivs med olika färger.) Nu är alltså dikter en underkatalog till min hemkatalog. Omvänt är min hemkatalog föräldrakatalog till dikter.

Om man vill få en lista över filerna i en annan katalog än den nuvarande, så ska man helt enkelt ange den önskade katalogens namn som argument till ls.$ ls dikter
$
Än så länge är katalogen dikter tom. Men vi ska nu kopiera filerna bellman och lenngren till den. Kommandot cp kan användas på två sätt; dels för att kopiera en fil till en annan, dels för att kopiera en eller flera filer till en katalog. I det senare fallet anger man destinationskatalogen som sista argument. (På motsvarande sätt kan man använda mv för att flytta filer till en annan katalog.)$ cp bellman lenngren dikter
$ ls -l dikter
total 2
-rw-------   1 göran    göran         372 feb  1 17:12 bellman
-rw-rw-r--   1 göran    göran         649 feb  1 17:12 lenngren
$
Figur [*] ger ett diagram över min hemkatalog och dess nuvarande innehåll.

  
Figur 1.2: Katalogstrukturen i min hemkatalog.
\begin{figure}
\small \psset{angleB=90, angleA=-90, levelsep=36pt, armB=14pt,
...
...\Tr{\psshadowbox{\rule{0pt}{6pt}\texttt{lenngren}}}
}
\end{center}\end{figure}

Man kan radera tomma kataloger med rmdir. Vill man radera en katalog inklusive dess innehåll, så ska man använda rm med flaggan -r.$ mkdir sånger
$ ls
bellman   dikter/   lenngren  sånger/
$ rm sånger
rm: sånger: är en katalog
$ ls
bellman   dikter/   lenngren  sånger/
$ rmdir sånger
$ ls
bellman   dikter/   lenngren
$ mkdir visor
$ cp bellman visor
$ ls visor
bellman
$ rmdir visor
rmdir: visor: Katalog inte tom
$ rm -r visor
$ ls
bellman   dikter/   lenngren
$
Ovan skapade jag först katalogen sånger. Det gick inte att ta bort den med rm, utan jag fick använda kommandot rmdir istället. Därefter skapade jag katalogen visor och kopierade bellman dit. Då gick det inte att ta bort katalogen med rmdir eftersom den inte var tom. Däremot kunde jag radera katalogen och dess innehåll med rm -r.

För att förflytta sig till en annan katalog, det vill säga byta nuvarande katalog, använder man kommandot cd med namnet på den katalog man vill komma till som argument.$ cd dikter
$ ls
bellman   lenngren
$
Nu befinner jag mig i underkatalogen dikter till min hemkatalog. Men om jag vill komma tillbaka dit jag var nyss, hur gör jag då? Ett sätt är att använda en mycket viktig finess, nämligen att namnet .. (två punkter) betecknar föräldrakatalogen till den katalog man befinner sig i.$ cd ..
$ ls
bellman   dikter/   lenngren
$
Nu slår oss tanken att den egna hemkatalogen kanske är underkatalog till en annan! Vi undersöker detta genast:$ cd ..
$ ls
eva/           göran/         lost+found/
$
Ja, så var det. Vi provar samma kommando igen:$ cd ..
$ ls
System.map@  dev/         home/        mnt/         tmp/
bin/         dosc/        initrd/      proc/        usr/
boot/        etc/         lib/         root/        var/
cdrom/       floppy/      lost+found/  sbin/        vmlinuz@
$
Här fanns en massa kataloger. Men vi struntar i det tills vidare, och fortsätter på den inslagna vägen.$ cd ..
$ ls
System.map@  dev/         home/        mnt/         tmp/
bin/         dosc/        initrd/      proc/        usr/
boot/        etc/         lib/         root/        var/
cdrom/       floppy/      lost+found/  sbin/        vmlinuz@
$
Nej, det gick tydligen inte att komma längre. Vi befinner oss i en katalog som inte är underkatalog till någon annan. Nu har vi för övrigt gått vilse, och kan svårligen hitta tillbaka. Lyckligtvis kan vi ta till hjälp att man kommer hem till sin hemkatalog om man ger kommandot cd utan att specificera någon destination.$ cd
$ ls
bellman   dikter/   lenngren
$
Men var någonstans är vi nu? Kommandot pwd (''ange nuvarande katalog'') ger svaret:$ pwd
/home/göran
$
Aha! Om jag i stället för cd gett först kommandot cd home och sedan cd göran, så hade jag också kommit hem. Den katalog jag var i nyss kallas för rotkatalogen, och den har det koncisa namnet /. Min hemkatalog heter göran, detsamma som mitt användarnamn. Den är en underkatalog till home. Och home i sin tur är underkatalog till rotkatalogen. På detta sätt är alla kataloger ordnade i en hierarki med rotkatalogen överst (eller underst, beroende på hur man ser det) i hierarkin. Det är grundläggande för UNIX att det endast finns en sådan hierarki, filsystemet är gjutet i ett stycke (eller åtminstone ser det ut så).

  
Figur: Rotkatalogen och en del av dess innehåll.
\begin{figure}
\small \psset{angleB=90, angleA=-90, levelsep=36pt, armB=14pt,
...
...Tr{\psshadowbox{\rule{0pt}{6pt}\texttt{vmlinuz}}}
}
\end{center}
\end{figure}

Ett alternativt sätt att referera till katalogen dikter är att ange dess absoluta läge i filsystemet, /home/göran/dikter. Man säger att detta är sökvägen till katalogen. Poängen med att ange sökvägen är att man därigenom kan referera till filer eller kataloger som inte finns i just den nuvarande katalogen. Filer har också sökvägar. Sökvägen till filen bellman i min hemkatalog är /home/göran/bellman, medan sökvägen till filen bellman i underkatalogen dikter är /home/göran/dikter/bellman. Trots att dessa filer har samma namn, kan man med hjälp av sökvägar ange vilken av dem man åsyftar.

Så här kan man se vad som finns i katalogen /home/eva:$ ls /home/eva
LPAuppsats/   index.html    paper.dvi     paper.tex~
ekerö.jpg     index.html~   paper.log     post/
idéhistoria/  paper.aux     paper.tex     övrigt/
$
Alternativt kunde jag ha gett kommandot ls ../eva. Detta fungerar om jag befinner mig i min hemkatalog /home/göran, för då står .. för /home. Man säger att ''../eva'' är en relativ referens till katalogen, i motsats till ''/home/eva'' som är en absolut referens.

I detta sammanhang är det kanske lämpligt att påpeka att namnet . (en punkt) betecknar den nuvarande katalogen. Så vi kan använda kommandot cp /bin/bash . om vi skulle vilja kopiera filen bash från katalogen /bin till den nuvarande katalogen.

För att ta reda på hur mycket utrymme en viss katalog (inklusive dess underkataloger) förbrukar, ska man använda kommandot du. Som argument anger man vilken katalog som avses. Om man inte anger flaggan -s, så skrivs också information om underkatalogerna ut.$ du
3       ./dikter
16      .
$ du -s /home/eva
162101  /home/eva
$
Siffrorna i utskriften står för antalet kilobyte. Min hemkatalog, inklusive alla underkataloger, använder alltså 16 kilobyte lagringsutrymme. Jag ska kanske nämna vilka enheter man använder för att beskriva datorminne? En gigabyte är 1024 megabyte. En megabyte är 1024 kilobyte. En kilobyte är 1024 byte. En byte är ett lagringsutrymme i vilket man kan lagra ett tecken som tex en bokstav eller en siffra. (Eller snarare, i en byte lagras ett tal mellan 0 och 255, och detta tal tolkas ofta som ett tecken med hjälp av tabellen på sida [*].) En sida text består av cirka 2000 tecken, ungefär 2 kilobyte. En megabyte motsvarar ungefär 500 sidor text.

I UNIX finns en mer eller mindre standardiserad struktur för hierarkin av kataloger. Till exempel innehåller /bin vissa av de viktigaste programmen (som bash, cp och ls), /sbin innehåller systemprogram, huvuddelen av /usr innehåller användarprogram och filer som de behöver, /home innehåller hemkataloger för de olika användarna, i /etc finns viktiga konfigurationsfiler osv.

Det är en konvention att filer vars namn inleds med en punkt ska döljas, inte för att de är hemliga utan för att man kanske sällan ändrar i dem och vill ha dem ur vägen. Kommandot ls respekterar denna konvention. Om man vill se samtliga filer i den nuvarande katalogen, så ska man anropa ls med flaggan -a.$ ls -a
./             .bash_profile  bellman
../            .bashrc        dikter/
.bash_history  .inputrc       lenngren
$
Jag har alltså fyra ''dolda'' filer i min hemkatalog. Katalogerna . och .. återges som synes också när ls anropas med flaggan -a. Nedan vill vi ha utförlig information om samtliga filer och anropar därför ls med både -l och -a. (I likhet med många andra kommandon tillåter ls att man förkortar exempelvis -l -a till -la.)$ ls -la
total 16
drwxrwxr-x   3 göran    göran        1024 feb  1 17:24 ./
drwxrwsr-x   9 root     staff        1024 nov 23 22:09 ../
-rw-rw-r--   1 göran    göran        6711 feb  1 17:23 .bash_history
-rw-rw-r--   1 göran    göran         235 feb  1 17:23 .bash_profile
-rw-rw-r--   1 göran    göran          81 feb  1 17:23 .bashrc
-rw-rw-r--   1 göran    göran         239 feb  1 17:24 .inputrc
-rw-------   1 göran    göran         372 feb  1 16:48 bellman
drwxrwxr-x   2 göran    göran        1024 feb  1 17:12 dikter/
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
$
Samtliga filer och kataloger ägs av göran och är associerade med gruppen göran, med undantag av katalogen .. som ägs av root och hör till gruppen staff.

Motsvarande skyddskoder som för filer används också för kataloger, men tolkningen är något annorlunda. Ett r betyder att man får se namnen på de filer som katalogen innehåller (tex med ls). Ett x betyder att man får gå in i katalogen (tex med cd). Om x inte är påslaget, så kan man överhuvudtaget inte göra något med katalogen (förutom att se vad den innehåller om r är påslaget). Ett w betyder att man får skriva till katalogen (dvs skapa nya filer eller radera filer i katalogen).

1.5 Övningar

Övning. Logga in och ändra ditt lösenord.

Övning. Slå upp din födelsemånad med hjälp av kommandot gcal (eller cal, ifall gcal inte finns tillgängligt).

Övning. Läs bruksanvisningen för echo med hjälp av man.

Övning. Skriv en kommandorad med några ord. Hoppa omkring i raden med C-a, C-e, M-a och M-e. Prova C-w och C-y. Tryck C-_ några gånger.

Övning. Ge i tur och ordning kommandona less /etc/passwd, date och cal 10 1997. Använd sökfunktionen C-r och tryck först l, sedan e för att återfå det första komandot. (Tryck Esc eller vänsterpil för att avsluta.)

Övning. Prova några andra av redigeringskommandona i tabell 1.1

Övning. Har du någon .inputrc, och hur ser den i så fall ut? Om inte, skapa en så att du kan använda å, ä och ö.

Övning. Ge något kommando som är mer än en rad långt. Hur ser den sekundära prompten ut?

Övning. Undersök vilka filer som finns i katalogen /bin.

Övning. Gå till katalogen /etc och kontrollera vilka filer som finns där. Hur många rader, ord och tecken har filen passwd? Skriv ut de tolv sista raderna av filen passwd. Skriv ut hela filen med cat. Läs filen en skärm i taget med more eller less. Gå därefter tillbaka till din hemkatalog.

Övning. Undersök vilka filer som finns i din hemkatalog. Finns det dolda filer där? Titta på någon av dem i så fall.

Övning. Skapa en katalog med namn test. Se till att du har rättigheterna rwx för katalogen. Kopiera filen passwd från /etc till test och ta bort din egen rättighet att skriva över filen. Vad händer om du då försöker radera filen?

Övning. Skapa en katalog med namn test och kopiera filen passwd från /etc till test. Ändra sedan rättigheterna så att du själv får exekvera, men varken skriva till eller läsa katalogen. Vad händer om du ger kommandot ls test? Gå in i test med kommandot cd test och prova i tur och ordning ls, cat pass och cat passwd. Förklara vad som händer.

  
2. GNU Emacs

2.1 Universalprogrammet Emacs

Ett av de främsta och viktigaste programmen i UNIX-miljön är Emacs. Ytligt sett är Emacs en textredigerare, men tack vare dess närmast ändlösa flexibilitet kan Emacs tjäna som ett universalprogram - via Emacs kan man typsätta sina dokument, programmera, hantera sina filer, skicka datorpost och läsa webbsidor, bland mycket annat. Emacs har ett skräddarsytt läge för varje tillämpning. När man skriver ett C-program ställer Emacs in sig i C-läget; när man arbetar med typsättningsprogrammet LATEX inifrån Emacs befinner sig Emacs i sitt LATEX-läge osv.

Emacs är uppbyggt kring en kärna som ger den grundläggande funktionaliteten och som dessutom implementerar ett helt programmeringsspråk, Emacs lisp. Till denna kärna kommer ett stort antal moduler, skrivna i Emacs lisp, som utökar funktionaliteten och definierar olika lägen. Till exempel finns en modul som möjliggör stavningskontroll av texter och en grupp av moduler som tillsammans definierar LATEX-läget.

Om man inte är nöjd med hur Emacs fungerar, så kan man ofta anpassa det efter sin egen smak genom att ändra på vissa inställningar, antingen inifrån Emacs eller genom att lägga in kommandon i en fil med namnet .emacs i sin hemkatalog. För att tecknen åäö ska behandlas korrekt kan det vara nödvändigt att lägga följande kommandon i filen .emacs:

(standard-display-european t)
(set-input-mode (car (current-input-mode))
                (nth 1 (current-input-mode))
                0)

Emacs startas med kommandot emacs, eventuellt tillsammans med namnet på en eller flera filer som man vill arbeta med. Exempelvis kan man för att redigera filen bellman ge kommandot$ emacs bellmanProgrammet avslutas med C-x C-c, dvs först kommandot C-x och därefter C-c. Det snabbaste sättet att ge detta kommando är att hålla kontrolltangenten nedtryckt med lillfingret och samtidigt trycka först x med långfingret och sedan c med pekfingret på vänster hand. (Emacs är, liksom UNIX i övrigt, kärvt för nybörjare, med många kryptiska kommandon som kan tyckas omöjliga att memorera. Men när man vant sig vid programmet upptäcker man att tangenterna tycks ''sitta rätt'', så att man kan arbeta snabbt och effektivt.)

För den som inte tidigare använt Emacs rekommenderar jag varmt den inbyggda introduktionen, som startas med kommandot C-h t. Genom den får man lära sig hantera de grundläggande funktionerna för textredigering.

Ett annat kommando som är bra att känna till är C-g. Det avbryter ett eventuellt påbörjat kommando. Om man av misstag råkar ge ett kommando och det börjar hända märkliga saker, så kan ordningen alltså ofta återställas med C-g. Om däremot ett kommando redan har utförts och förändrat den text man arbetar med, så ska man använda ångerkommandot C-_ för att återställa bufferten till det skick den var i före kommandot. Genom att upprepade gånger ge kommandot C-_ kan man omintetgöra förändringar som gjorts långt tidigare. En synonym för C-_ är C-x u.

Tabell 2.1 ger en förteckning av de kommandon som beskrivs i detta avsnitt.


 
Tabell: Grundläggande Emacs-kommandon
 
Kommando Funktion Kommando Funktion
C-x C-c Avsluta Emacs C-h t Introduktion till Emacs
C-x C-s Spara bufferten C-_ Ångra föregående kommando
C-x C-f Öppna fil C-g Avbryt kommando
C-x s Spara flera buffertar M-x Ge namngivet kommando
C-x C-w Spara under nytt namn F10 Aktivera menyn
C-x k Stäng buffert C-x i Läs in en fils innehåll

När man öppnar en fil i Emacs, vare sig genom att ange dess namn i kommandot som startar Emacs eller med kommandot C-x C-f, skapas en buffert i emacs. Bufferten innehåller en kopia av filens innehåll. Man arbetar alltså inte direkt mot filen så som den är sparad på tex hårddisken, utan endast med en kopia. För att de ändringar man gjort i en buffert ska bli beständiga måste man spara bufferten. Kommandot för att spara en buffert, och skriva över den motsvarande filen, är C-x C-s. Emacs kan hantera ett stort antal buffertar åt gången. Man kan alltså arbeta med flera olika filer samtidigt.

När Emacs startats ser man menyraden överst i Emacs-skärmen. Med hjälp av menyraden kan man välja vissa kommandon i stället för att skriva in dem via tangenterna. Under menyn finns en stor yta, ett fönster,2.1där man arbetar med sina texter. Långt ned på Emacs-skärmen finns en markerad rad, lägesraden.

Den kan se ut ungefär så här:

--**-Emacs: epistlar.tex      (LaTeX)--L110--34%-------
Låt oss säga något om vilken information man kan utläsa ur lägesraden: Emacs identifierar sig med texten Emacs:, och anger att man arbetar med filen epistlar.tex. De båda asteriskerna till vänster betyder att man inte sparat de senaste ändringarna av sin fil. När bufferten är sparad står det -- i stället för **. (Och om den fil man har framme skulle vara skrivskyddad, så står det %%.) Naturligtvis kan man också se vilket läge Emacs befinner sig i - LATEX-läget. Dessutom ser vi att vi befinner oss vid rad 110 och att 34% av bufferten epistlar.tex ligger före texten som för tillfället visas i fönstret. Om man kan se början av bufferten, så står det Top snarare än 0%; om man kan se slutet av bufferten, så står det Bot - men om bufferten är så kort att man kan se den i sin helhet, så står det i stället All.

Det lilla område som finns under lägesraden kallas för minibufferten. Den används då man i samband med något kommando behöver mata in text som inte hör till det dokument man arbetar med. Till exempel betyder kommandot C-x C-f att man vill öppna en fil för redigering. När man gett detta kommando flyttas markören ned till minibufferten, och Emacs frågar efter namnet på den fil man vill öppna. Ett annat exempel är namngivna kommandon som goto-line, eller M-x goto-line som vi ofta kommer att skriva. För att kunna ge sådana kommandon trycker man först M-x, varefter markören flyttas till minibufferten, så att man där kan skriva kommandot. (Efter att man gett kommandot goto-line får man en följdfråga i minibufferten, nämligen om vilken rad man vill gå till.)

Ett koncept som genomsyrar all användning av Emacs är punkten.2.2Med ''punkten'' menas den position i bufferten där man för tillfället befinner sig. Den text man skriver in hamnar vid punkten. Emacs använder markören för att visa var punkten finns. Närmare bestämt: Punkten befinner sig alltid mellan två tecken i bufferten. Markören placeras över det tecken i bufferten som kommer direkt efter punkten, dvs till höger om den. Till exempel, om punkten befinner sig mellan bokstäverna x och m i ''exmpel'', så placerar Emacs ut markören över bokstaven m. Om man då trycker på e så läggs ett e in mellan x och m, vilket ger ''exempel''. Därefter befinner sig punkten mellan e och m, och markören står fortfarande över m.

Som komplement finns också begreppet märket, en position i texten som man markerat med C-mellanslag för att tex senare kunna återvända dit snabbt eller för att kunna klippa ut texten mellan märket och punkten. Texten mellan märket och punkten kallas för regionen. När märket är satt lyser Emacs upp regionen. Om detta är tröttsamt eller störande, så kan man slippa det genom att trycka C-g.

Det lättaste sättet att vandra omkring i sin fil, dvs att flytta punkten, är att använda piltangenterna samt Page Up och Page Down. Ofta vill man också flytta sig till början av en rad med kommandot C-a, eller till slutet av den med C-e. Men det finns naturligtvis åtskilliga andra kommandon som påverkar punkten och märket - se tabell [*].


 
Tabell 2.2: Kommandon som flyttar punkten
 
Förflyttning Bakåt (uppåt) Framåt (nedåt)
Tecken C-b eller vänsterpil C-f eller högerpil
Rad C-p eller pil uppåt C-n eller pil nedåt
Ord M-b M-f
Till radens början/slut C-a C-e
Till filens början/slut M-< M->
En skärmfull rader M-v eller Page Up C-v eller Page Down
Mening M-a M-e
Stycke M-{ M-}
Gå till rad nummer n M-x goto-line n
Centrering av punkten C-l
Sätt märke C-@ eller C-mellanslag
Byt punkten mot märket C-x C-x

2.2 Textredigering

För inskrivning av text behövs inga kommandon, man matar bara in texten precis som vanligt. Tecknet bakom punkten (dvs närmast bakom markören) raderas med Delete eller raderingstangenten; håller man samtidigt metatangenten nedtryckt, så klipps hela det ord som står före punkten ut. (Med att klippa ut menar jag att med ett kommando radera vad som kan vara ett större stycke text.) Varje gång något klipps ut sparas det i en särskild buffert, urklippsringen. Det senast urklippet återfås (det klistras in) på kommandot C-y.

Man flyttar ett stycke text genom att klippa ut den, flytta punkten dit man vill ha texten, och sedan klistra in den.

Om man genast efter C-y ger kommandot M-y, så byts den nyss inklistrade texten mot det näst senate urklippet; genom att upprepade gånger trycka M-y återfår man successivt äldre och äldre urklipp. Om man kommit till det äldsta urklippet och åter trycker M-y, så startar processen om med det senaste urklippet - det är därför man talar om en ring av urklipp.


 
Tabell 2.3: Raderingskommandon
 
Kommando Klipper ut... Kommando Funktion
C-k raden framför punkten C-y Klistra in senaste urklippet
C-k C-k fram till nästa rad M-y Efter C-y, ersätt med äldre urklipp
C-w regionen M-w Kopiera regionen till urklippsringen
M-Delete ordet bakom punkten Delete Radera tecknet bakom punkten
M-d ordet framför punkten C-d Radera tecknet framför punkten

Synnerligen ofta händer det att man arbetar med en lång text, och vill leta upp ett visst ord i texten. I sådana situationer ska man använda Emacs sökfunktion. Den enklaste formen av sökning är C-s (för att söka framåt i bufferten) eller C-r (för att söka bakåt i bufferten). Efter att ha gett kommandot C-s får man i minibufferten skriva in den text man Emacs ska söka efter i bufferten. Sökningen sker efter hand som man skriver in söktexten. Om man trycker på returtangenten, så avslutas sökningen. Om emacs finner en förekomst av söktexten och man vill komma till nästa, så ska man trycka C-s (eller C-r) igen. När sökningen avslutats är märket placerat där sökningen började, så man kan komma tillbaka med kommandot C-x C-x.

Med kommandot M-% kan man byta ut alla eller vissa förekomster av en text mot en annan, se tabell [*].


 
Tabell: Kommandon för sökning
 
Kommando Funktion Motsv. reguljär sökning
C-s sök framåt C-M-s
C-r sök bakåt C-M-r
M-% sök och byt ut M-x query-replace-regexp
Kommandon under sökning
C-s sök igen framåt C-r sök igen bakåt
Kommandon under sökning och utbyte
y Byt, fortsätt till nästa , Byt, men stanna
n Byt inte; fortsätt ^ Backa till föregående
! Byt alla utan att fråga retur Avsluta sökningen

2.3 Några finesser

Den första gången man under en Emacs-session sparar en förändrad version av en fil, gör Emacs en säkerhetskopia av filen för den händelse att man vid ett senare tillfälle skulle ångra de ändringar man gjort. Om vi till exempel gör ändringar i filen bellman och sparar filen, så kopieras filen i sitt ursprungliga skick till namnet bellman~. Den nya versionen lagras därefter under namnet bellman. Säkerhetskopian har alltså samma namn som filen bortsett från att ett tilde har lagt till på slutet.

Det finns vissa riskmoment i att ändringar man gjort inte sparas genast utan först då man ger kommandot för att spara: om man glömmer bort att spara tillbaka bufferten, så förlorar man sina ändringar. Visserligen är detta ibland precis vad man vill, nämligen om man kommer på att ändringarna var misslyckade och man föredrar den ursprungliga versionen av filen. Men oftast vill man behålla de tillägg man gjort i en fil, så om man är på väg att lämna Emacs trots att det finns osparade buffertar, så frågar Emacs både en och två gånger om bekräftelse av att detta verkligen är ens avsikt. Det finns för övrigt ett kommando för att kasta bort de ändringar man gjort och återställa bufferten så som den är sparad på sekundärminnet, M-x revert-buffer. Även vid detta kommando frågar Emacs ifall detta verkligen är vad man vill.

Vidare, om det blir strömavbrott, så raderas innehållet i datorn och man får inte tillfälle att spara sina ändringar som kanske tagit flera timmar att skriva in. För att ge ett visst skydd mot sådana olyckor gör Emacs med jämna mellanrum tillfälliga kopior av de filer man ändrat. Därigenom kan man efter ett strömavbrott eller en systemkrasch återfå alla eller de flesta av de tillägg man gjort till filen. Dessa autosparade buffertar får samma namn som den fil man arbetar med, bortsett från att tecknet # lagts till i början och på slutet. Om bellman autosparas, så sker det alltså under namnet #bellman#.

Om man någon gång öppnar en fil av vilken det finns en nyare, autosparad version, så påpekar Emacs detta, och uppmanar en att ge kommandot M-x recover-file för att övergå till den nyare versionen i stället.

Ibland vill man kunna se mer är en fil åt gången då man arbetar med Emacs. För att kunna göra det ska man dela upp Emacs-skärmen horisontalt och/eller vertikalt med kommandona C-x 2 eller C-x 3. Varje fält som då uppstår kallas för ett fönster i Emacs.

Man justerar fönsterstorleken med kommandona C-x ^ (gör fönstret högre), C-x } (gör fönstret bredare) samt C-x { (gör fönstret smalare).

Kommandot C-x o flyttar markören till ett annat fönster. Om Emacs-skärmen är indelad i flera fönster, så kan man ta bort alla fönster utom det som markören befinner sig i med kommandot C-x 1.


 
Tabell: Fönsterkommandon
 
Kommando Funktion Kommando Funktion
C-x 1 Stäng alla andra fönster C-x 0 Stäng detta fönster
C-x 2 Dela horisontalt C-x 3 Dela vertikalt
C-x o Byt fönster C-x ^ Gör fönstret högre
C-x } Gör fönstret bredare C-x { Gör fönstret smalare

2.4 Datorpost

Man kan skicka och ta emot datorpost från Emacs. Den enklaste modulen för detta är RMAIL. Den startas med kommandot M-x rmail. Innan man startar modulen bör man dock konfigurera den. För att ange sitt namn och sin datorpostadress ska man lägga in raderna

(setq user-full-name "Göran Andersson")
(setq user-mail-address "goran_a@sslug.dk")
i filen .emacs (men byt först till ditt namn och din adress). För att post ska kunna skickas iväg kan raderna
(setq smtpmail-default-smtp-server "mail.teledyrt.com")
(setq smtpmail-local-domain nil)
(setq send-mail-function 'smtpmail-send-it)
(load-library "smtpmail")
behöva läggas in i .emacs. I stället för mail.teledyrt.com ska det stå vilken datorpostserver man använder.

Om man ska hämta hem post från någon annan dator med hjälp av RMAIL måste följande rader skrivas in i .emacs:

(setenv "MAILHOST" "lukas.teledyrt.com")
(setq rmail-primary-inbox-list '("po:goran97") rmail-pop-password-required t)
(setq-default rmail-pop-password "tax45lax")
(load-library "message")
I sället för lukas.teledyrt.com ska det stå adressen till den dator du hämtar posten från. Byt också ut goran97 mot ditt användarnamn på den datorn och tax45lax mot ditt lösenord där. Raden som anger lösenordet bör utelämnas om du inte är enda användaren på datorn, annars är risken stor att någon kan komma åt det - även om du ser till att lässkydda filen .emacs. Om man inte vill skriva in sitt lösenord i .emacs, så frågar Emacs efter lösenordet varje gång man hämtar hem post.

En annan och mer avancerad modul för datorpost är VM. Den startas med kommandot M-x vm. Konfigurationen är densamma som ovan, men om man ska hämta post från någon annan dator ska man också lägga in raderna

(setq vm-spool-files 
  (list 
    (list "~/INBOX"
          "lukas.teledyrt.com:110:pass:goran97:tax45lax"
          "~/INBOX.CRASH")))
i sin .emacs. Man kan sätta tecknet * i stället för lösenordet om man inte vill skriva in det i en fil så här.

 

2.5 Stavningskontroll

För att kontrollera stavningen i en buffert ska man ge kommandot M-x ispell-buffer. För att detta ska fungera måste programmet ispell vara installerat på datorn. En svensk ordlista till ispell finns att hämta på adressen http://www.sslug.dk/ispell/. Det kan vara praktiskt att lägga in raden

(setq ispell-dictionary "svenska")
i sin .emacs, för annars kan man behöva ge kommandot M-x ispell-change-dictionary för att byta till den svenska ordlistan.

2.6 Övningar

Övning. Starta Emacs och avsluta därefter direkt.

Övning. Starta Emacs och ge kommandot C-h t. Arbeta en stund med uppgifterna i den fil som då dyker upp!

Övning. Skapa med hjälp av Emacs en fil med följande innehåll:

Solen glimmar blank och trind,
vattnet lik en spegel;
småningom uppblåser vind
i de fallna segel;
vimpeln sträcks, och med en år
Olle på en höbåt står;
Kerstin ur kajutan går,
skjuter lås och regel.
Ge filen namnet ulla.

   
3. Kommandotolken

Näst efter Emacs brukar skalet, eller kommandotolken som det också kallas, vara det program som används mest. Det är skalet som läser in och tolkar de kommandon man ger och som ser till att de exekveras. Man kan tänka på skalet som en betjänt som tar emot en order och som vidarebefordrar den så att den blir utförd. Vägen till de program som finns på datorn går nästan alltid via skalet. Det är därför viktigt att man kommer väl överens med skalet, och att man är förtrogen med dess ''språk''. Vi ska i detta kapitel beskriva några av de finesser skalet erbjuder. Förmodligen är det enklast att till en början bara bläddra igenom detta kapitel, och återvända hit för att studera detaljerna först när de verkligen behövs.

I UNIX finns flera olika skal, varav de mest använda är Bourne-skalet, C-skalet, Korn-skalet och Z-skalet. I mångt och mycket är dessa skal lika, men de skiljer sig åt på vissa detaljer. För enkelhets skull ska vi i denna bok hålla oss till ett enda skal, en modern och oerhört kraftfull variant av Bourne-skalet som heter Bash - Bourne-again shell.3.1

3.1 Jokertecken

Antag att en viss katalog innehåller följande filer:$ ls
epistel23.text   epistel28.text~  fredman.html~    haga.sång~
epistel25.text   epistel36.text   fredman.ps       movitz.html
epistel28.text   fredman.html     haga.sång        winblad.html
$
Om vi nu vill radera alla filer vars namn slutar med ett tildetecken så kan vi ge kommandot rm epistel28.text~ fredman.html~ haga.sång~. Men det känns lite omständligt att behöva räkna upp alla dessa filer. Lyckligtvis finns det ett enklare sätt. Man kan nämligen referera till flera filer samtidigt med hjälp av jokertecken.

Jokertecknen är *, ? och [. Om ett argument innehåller något av dessa tecken, så tolkar skalet argumentet som ett mönster som ska paras ihop med namnen på filerna (och katalogerna) i den nuvarande katalogen. Om ett eller flera av filnamnen ''matchar'' mönstret så skriver skalet om kommandot man gav genom att byta ut mönstret mot en alfabetiskt sorterad lista av de filnamn som matchar mönstret. Om mönstret däremot inte matchas av något filnamn, så görs inga omskrivningar, utan skalet lämnar då mönstret som det är.

Jokertecknet * matchar vilken som helst följd av noll eller flera tecken. Mönstret *~ matchar därför precis de filnamn som slutar med ett tilde. För att radera alla filer vars namn slutar med tilde kan vi tydligen ge kommandot rm *~. Detta kommando skriver skalet (utan att det syns) om till rm epistel28.text~ fredman.html~ haga.sång~ innan det exekveras. Tolkningen görs alltså av skalet - kommandon som tex rm vet ingenting om jokertecken.$ rm *~
$ ls
epistel23.text  epistel36.text  haga.sång
epistel25.text  fredman.html    movitz.html
epistel28.text  fredman.ps      winblad.html
$

  
Figur 3.1: Skalet skriver om ett kommando.
\begin{figure}
\begin{center}
\psframebox[framesep=1.1]{
\begin{psmatrix}
\p...
...ows=->]{1,1}{2,1}\tlput{Omskrivning}
\end{psmatrix} }
\end{center}\end{figure}

I de följande exemplen, där vi inte gör något meningsfullt utan bara demonstrerar hur jokertecken fungerar, ska vi använda kommandot echo som ju skriver ut sina argument exakt som de är. Då får vi se hur argumenten såg ut efter omskrivningen.

Mönstret epistel* matchar alla de filer vars namn börjar med ''epistel'':$ echo epistel*
epistel23.text epistel25.text epistel28.text epistel36.text
$
Så här väljer man ut alla filer vars namn slutar med .html:$ echo *.html
fredman.html movitz.html winblad.html
$
Det finns ingen fil vars namn slutar med .htm:$ echo *.htm
*.htm
$
Här ser vi att skalet skickar mönstret vidare oförändrat ifall det inte matchar något filnamn.

Det får förekomma jokertecken i fler än ett av argumenten:$ echo winblad.* fredman.*
winblad.html fredman.html fredman.ps
$
Man får också använda flera jokertecken i samma mönster:$ echo e*3*t
epistel23.text epistel36.text
$ echo *n*
fredman.html fredman.ps haga.sång winblad.html
$
På detta sätt fann vi alla filnamn som innehåller bokstaven n.

Jokertecknet ? matchar ett (exakt ett) godtyckligt tecken.$ echo epistel2?.text
epistel23.text epistel25.text epistel28.text
$ echo *.??
fredman.ps
$
Mönstret *.?? matchar de filer vars namn slutar på en punkt följt av exakt två tecken.

Det finns två undantag mot reglerna för jokertecken. Det ena undantaget är att tecknet / måste anges uttryckligen i mönstret. Så till exempel matchar mönstret /etc/*wd filnamnet /etc/passwd men inte filnamnet /et*wd.$ echo /et*wd
/et*wd
$ echo /etc/*wd
/etc/passwd
$ echo /usr/*/emacs
/usr/bin/emacs /usr/doc/emacs /usr/lib/emacs
$
Det andra undantaget är att filnamn som inleds med en punkt endast matchas av mönster som också inleds med en punkt.$ echo .*
. .. .bash_history .bash_profile .bashrc .inputrc
$ echo .ba*
.bash_history .bash_profile .bashrc
$

Om man vill att mönstret ska matcha bara vissa utvalda tecken, så behövs andra hjälpmedel än * och ?, nämligen [...]. Mellan hakparenteserna ska man räkna upp ett antal tecken, och då tolkar skalet detta som ett mönster som matchar ett (exakt ett) av de uppräknade tecknen. Till exempel matchar [058] ett valfritt av tecknen 0, 5 och 8:$ echo epistel2[058].text
epistel25.text epistel28.text
$
Man kan också ange intervall, [3-7] matchar 3, 4, 5, 6 eller 7, och [a-d] matchar a, b, c eller d.$ echo epistel2[3-7].text
epistel23.text epistel25.text
$ echo *.[a-r]*
fredman.html fredman.ps movitz.html winblad.html
$
Om man vill att mönstret ska matcha alla tecken som inte är uppräknade, så ska man ange tecknet ^ direkt efter [. Mönstret [^te-h] matchar alltså exakt ett tecken som inte är något av t,e,f,g eller h.$ echo *[^te-h]
fredman.html fredman.ps movitz.html winblad.html
$
(Man kan använda ! som en synonym för ^.)

Låt oss avslutningsvis nämna en liten komplikation: Hur kan man ange ett mönster som ska matcha exakt ett av tecknen 2, - och 5? Inte som 2-5, för då tolkas det som 2,3,4 eller 5. Men om man sätter tecknet - först eller sist, som i [-25], så förstår skalet att man inte syftar på ett intervall. På motsvarande sätt gäller att om man vill räkna upp ett antal tecken varav ett är ], så ska man ange detta först. Exempelvis matchar []aö] ett av tecknen ], a och ö.

  
3.2 Jobb

Med hjälp av skalet kan man få flera kommandon utförda samtidigt. Man kan också avbryta kommandon, eller tillfälligt stoppa dem för att låta dem fortsätta vid ett senare tillfälle. I det här avsnittet ska vi berätta hur det går till.

Antag att vi kör ett kommando som tar lång tid och blir otåliga medan vi sitter och väntar på att prompten ska återvända. När vi tröttnat på att vänta kan vi trycka C-c. Då gripet skalet in och avbryter kommandot, och prompten återvänder omedelbart.$ cp /usr/bin/emacs .
$ ls -l emacs
-rwxrwxr-x   1 göran    göran     1814268 feb  5 14:19 emacs*
$ gzip -9 emacs          Efter en stund trycker jag C-c...
$ ls -l emacs
-rwxrwxr-x   1 göran    göran     1814268 feb  5 14:19 emacs*
$
Vad var det som hände ovan? Jo, först kopierade jag en stor fil, den som innehåller programmet Emacs, till den nuvarande katalogen. Sedan startade jag ett kommando, gzip emacs, som komprimerar filen. Efter ett antal sekunder hade prompten fortfarande inte återvänt, och då tryckte jag C-c. Därigenom avbröt jag kommandot, och filen emacs blev inte komprimerad. (Asterisken som ls skriver ut efter filnamnet emacs markerar att filen är exekverbar.)

Metoden att avbrya ett kommando med C-c är destruktiv; den del av arbetet som kommandot redan gjort är förgäves. Ett annat och bättre sätt att slippa vänta på prompten medan ett kommando är igång, är att låta kommandot köra i bakgrunden. Detta innebär att kommandot undviker att blockera det skal man arbetar vid. Medan körningen av kommandot pågår kan man då ge nya kommandon. För att ett kommando ska köra i bakgrunden, ska man helt enkelt skriva tecknet & efter kommandot.$ gzip emacs &
[1] 2792
$ ls
bellman   dikter/   emacs*    emacs.gz  lenngren
$ date
tor feb  5 14:37:28 CET 1998
$ ls -l
total 625
-rw-------   1 göran    göran         372 feb  1 16:48 bellman
drwxrwxr-x   2 göran    göran        1024 feb  1 17:12 dikter/
-rwxrwxr-x   1 göran    göran      632176 feb  5 14:19 emacs.gz*
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
[1]+  Done                    gzip emacs
$
Eftersom jag satte tecknet & efter kommandot gzip emacs, kördes det i bakgrunden. Därför återkom prompten genast efter att ett meddelande, [1] 7809, skrivits ut. Därigenom kunde jag ge andra kommandon, som ls och date, medan gzip arbetade med att komprimera filen emacs. Siffran 7809 är ett slags namn, processidentifikationsnumret, som systemet gav kommandot som bestod i att köra gzipemacs. Numret 1 som skrevs ut är ett namn, jobbnumret, som skalet gav kommandot. Vilken nytta man kan ha av processidentifikationsnumret ska vi berätta i avsnitt 5.5. I det här avsnittet intresserar vi oss bara för jobbnumret. När jobb nummer 1 var klart meddelades detta med utskriften [1]+ Done. (Eller snarare, när ett jobb är klart väntar skalet med att tala om detta tills det nästa gång får tillfälle att skriva ut en prompt. Därigenom slipper man bli störd medan man skriver något.) Man kan se ovan att filen komprimerades till lite drygt en tredjedel av sin ursprungliga storlek. Den komprimerade filen har samma namn som den ursprungliga men med tillägget .gz.

Kommandon som inte kör i bakgrunden sägs köra i förgrunden. Om ett kommando som kör i förgrunden visar sig ta alltför lång tid, behöver man inte nödvändigtvis avbryta det med C-c. I stället kan man trycka C-z. Då stoppas körningen, men bara tillfälligt. Den kan fortsätta från samma punkt vid ett senare tillfälle då vi har tid att vänta. En körning som har avbrutits med C-c kan däremot inte återupptas. I exemplet nedan börjar jag dekomprimera filen emacs.gz i förgrunden med kommandot gunzip, men stoppar kommandot efter en stund:$ gunzip emacs.gz          Efter en stund trycker jag C-z

[1]+  Stopped                 gunzip emacs.gz
$ ls
bellman    dikter/    emacs      emacs.gz*  lenngren
$
Kommandot är nu stoppat. Det har fått jobbnummer 1, men det sitter bara och väntar på nya signaler. Vi kan aktivera kommandot igen, och sätta det i förgrunden, med kommandot fg %1. Procenttecknet följt av ett nummer är en referens till jobbet med detta nummer.$ fg %1
gunzip emacs.gz
$ ls
bellman   dikter/   emacs*    lenngren
$
En liten stund efter kommandot fg %1 återvände promtpen, och då var filen komprimerad.

Vi gör nu ett nytt experiment:$ gzip -9 emacs          Efter en stund trycker jag C-z

[1]+  Stopped                 gzip -9 emacs 
$
Flaggan -9 gör att gzip försöker komprimera bättre, men oftast blir enda skillnaden att det tar längre tid. Efter en stund stoppade jag kommandot med C-z. För att få reda på vilka jobb som är igång, inklusive de som är stoppade, ska man ge kommandot jobs.$ jobs
[1]+  Stopped                 gzip -9 emacs 
$
Man kan aktivera ett stoppat jobb, och låta det fortsätta i bakgrunden, med kommandot bg.$ bg %1
[1]+ gzip -9 emacs &
$ ls
bellman   dikter/   emacs*    emacs.gz  lenngren           Jag väntar en stund...
$ ls
bellman    dikter/    emacs.gz*  lenngren
[1]+  Done                    gzip -9 emacs
$ rm emacs.gz
$
Jag lät det stoppade jobbet köra vidare i bakgrunden genom att ge kommandot bg %1. Medan jobbet var igång kunde jag använda skalet; i körningen ovan gav jag kommandot ls samtidigt som komprimeringskommandot körde i bakgrunden. Jag väntade en sedan stund. Efter att jag gett kommandot ls igen, skrev skalet ut att bakgrundsjobbet var klart. Förmodligen blev det klart några sekunder tidigare, medan jag väntade. Skalet meddelar ju inte användaren genast när ett bakgrundskommando är klart, utan först när det får tillfälle att skriva ut en ny prompt.

3.3 In- och utdata

Information och data kan skickas till ett program medan det kör. Programmet kan också skicka ut data. För att kunna hantera flödet av data har varje programkörning tre informationskanaler: standard indata, standard utdata och standard felmeddelanden. Vi ska nu berätta hur man med hjälp av skalet kan hantera dessa kanaler.

Det som skrivs ut efter att man gett ett kommando kallas för kommandots utdata. Vanligtvis skrivs utdatan direkt till skärmen, men det händer ibland att man vill spara den i en fil. Detta är lätt gjort: man skriver bara ''> filnamn'' någonstans i kommandot, lämpligen i slutet av det.$ date
tor feb  5 16:36:00 CET 1998
$ date > datum
$ ls
bellman   datum     dikter/   lenngren
$ cat datum
tor feb  5 16:36:08 CET 1998
$
Ovan lät jag först date skriva ut tid och datum. Som vanligt kom utskriften direkt på skärmen. Sedan körde jag date igen, men med utdatan riktad till filen datum. Därefter hade jag fått en fil med namnet datum, och vid en kontroll med cat såg jag att utskriften från date hade hamnat där.

Vi ger ännu ett exempel:$ echo Hej > hejfil
$ ls
bellman   datum     dikter/   hejfil    lenngren
$ cat hejfil
Hej
$
Jag lät här echo skriva ordet ''Hej'' till en fil med namnet hejfil.

Man kan ''slå ihop'' två eller flera filer genom att låta cat skriva ut deras innehåll medan utdatan är riktad till en ny fil.$ cat hejfil datum > nyfil
$ ls
bellman   datum     dikter/   hejfil    lenngren  nyfil
$ cat nyfil
Hej
tor feb  5 16:36:08 CET 1998
$
Om man använder > filnamn för att styra utdatan från något kommando till en existerande fil filnamn, så raderas innehållet i denna fil innan utdatan skrivs till den.$ date > nyfil
$ cat nyfil
tor feb  5 16:55:27 CET 1998 
$
Filens nuvarande innehåll behålls, och kommandots utdata läggs till på slutet, ifall man skriver >> i stället för >:$ echo och hå. >> hejfil
$ cat hejfil
Hej
och hå.
$

Det är skillnad mellan ett kommandos utdata och dess eventuella felmeddelanden. Vill man skicka felmeddelandena till en fil, så ska man skriva 2> fil i stället för bara > fil.$ cp datum
cp: destinationsfil saknas
Försök med "cp --help" för mer information.
$ cp datum > utdata
cp: destinationsfil saknas
Försök med "cp --help" för mer information.
$ cat utdata
$ cp datum 2> utdata
$ cat utdata
cp: destinationsfil saknas
Försök med "cp --help" för mer information.
$
Först gav jag det ofullständiga kommandot cp datum, och fick genom ett felmeddelande påpekat för mig att kommandot var felaktigt. Därefter försökte jag dirigera om utdatan till filen utdata, men detta påverkade inte felmeddelandet. I tredje försöket lyckades jag däremot få felmeddelandet att hamna i filen utdata genom att använda 2> i stället för >.

Från vissa kommandon kan det komma både utdata och felmeddelanden. Man kan då skicka utdatan åt ett håll och felmeddelandena åt ett annat.$ wc lenngren bellmna
     19     118     649 lenngren
wc: bellmna: Filen eller katalogen finns inte
     19     118     649 total
$ wc lenngren bellmna > utdata 2> felmeddelande
$ cat utdata
     19     118     649 lenngren
     19     118     649 total
$ cat felmeddelande
wc: bellmna: Filen eller katalogen finns inte
$
Ovan ''råkade'' jag stava ett filnamn fel. Först behandlar wc filen lenngren, därefter skrivs ett felmeddelande ut, och sedan ges en sammanfattning. Jag körde därefter samma kommando igen, med utskriften riktad till filen utdata och felmeddelandena riktade till filen felmeddelande och kontrollerade att allting hade hamnat rätt.

Om utdatan och felmeddelandena ska hamna på samma ställe, så ska man omdirigera dem med tecknen >&:$ wc lenngren bellmna >& båda
$ cat båda
     19     118     649 lenngren
wc: bellmna: Filen eller katalogen finns inte
     19     118     649 total
$
Här körde jag kommandot ytterligare en gång, med utskrift och felmeddelanden riktade till filen båda.

I vissa kommandon förekommer sekvenser i stil med 2>&1, som riktar felmeddelandena åt samma håll som utskriften är riktad åt. Tecknen &1 kan läsas som ''den plats som utdatan just nu skickas till''. Siffran 1 är associerad med standrad utdata, siffran 2 med standard felmeddelanden och siffran 0 med standard indata. Vi kommer dock inte att ha användning för detta förrän i de senare kapitlen.

Till vissa kommandon får man skriva in text, som då kallas för indata till kommandot. Så är exempelvis fallet med wc om man inte anger något filnamn:$ wc
nu ska
vi se vad som händer.          Här trycker jag C-d
      2       7      29
$
Indatan till kommandot ovan är de två raderna text, som jag skrev in vid tangentbordet. Man markerar att indatan är slut genom att antingen trycka C-d i början av en rad eller trycka C-d två gånger i följd. Men var försiktig: om man trycker C-d när skalet förväntar sig ett kommando, så finns det risk för att man blir utloggad - skalet tror ju då att det är slut på dess indata, kommandona! Utdatan från wc är raden som innehåller siffrorna 2, 7 och 29. Många andra kommandon än wc, tex cat och more, väljer också att vänta på indata om man inte anger något filnamn.

Vill man att indatan ska komma från en fil istället för att matas in från tangentbordet, så skriver man bara ''< filnamn'' före eller efter kommandot.$ wc < bellman
     12      71     372
$ wc bellman
     12      71     372 bellman
$
Kan du se skillnaden mellan de båda kommandona?

Hur många tecken använder date för att ange dagens datum? Vi kan ta reda på detta genom att använda wc på filen datum (naturligtvis orkar vi inte själva räkna tecknen). Alltså: först kör man date och sparar undan dess utdata i en fil, och sedan kör man wc på denna fil. Om det bara är slutresultatet man är intresserad av, så känns det kanske onödigt att gå omvägen via en fil; man vill att utdatan från date ska gå direkt till wc. Detta kan man uppnå genom att skriva ett lodrätt streck, tecknet |, mellan kommandona; då skickar skalet utdatan från första kommandot som indata till det andra.$ wc datum
      1       6      30 datum
$ date | wc
      1       6      30
$

En följd av kommandon som är sammankopplade med | kallas för en rörledning. Rörledningar är utomordentligt användbara. Till exempel, om utdatan från ett kommando är för lång för att få plats på skärmen så att man inte hinner läsa den innan den försvunnit, så kan man låta den gå genom more.$ ls -l /bin | more
-rwxr-xr-x   1 root     root        23968 maj  5 01:36 ae*
-rwxr-xr-x   1 root     root         2716 maj  7 19:32 arch*
-rwxr-xr-x   1 root     root       325548 aug 15 18:56 bash*
-rwxr-xr-x   1 root     root       318612 jun 10 13:00 bash2*
-rwxr-xr-x   1 root     root         9104 maj  9 01:06 cat*
-rwxr-xr-x   1 root     root         9836 maj 10 01:46 chgrp*
-rwxr-xr-x   1 root     root        10348 maj 10 01:46 chmod*
-rwxr-xr-x   1 root     root         9652 maj 10 01:46 chown*
-rwxr-xr-x   1 root     root        23200 maj 10 01:46 cp*
-rwxr-xr-x   1 root     root        46712 feb 25  1997 cpio*
-rwxr-xr-x   1 root     root        23232 maj 30 20:58 date*
--More--

Nu återstår det bara att städa upp efter oss...$ rm datum felmeddelande utdata båda hejfil nyfil
$

   
3.4 Beskydd

Ett antal tecken, bland andra *, har en speciell mening för skalet. Ibland kan detta ställa till problem. Om jag exempelvis vill skriva ut texten ''Det mest använda jokertecknet är *'', hur gör jag då? Så här kan jag inte göra:$ echo Det mest använda jokertecknet är *
Det mest använda jokertecknet är bellman dikter lenngren
$
Problemet är att skalet griper in och ändrar i det kommando jag gav. Hur ska jag göra för att skydda mitt kommando från skalets inblandning? Svaret är att jag ska ge mitt kommando beskydd. Med hjälp av beskydd tar man bort den speciella betydelse vissa tecken har för skalet, och kan därigenom få dessa tecken att ingå i argument till kommandon.

Det enklaste sättet att ge beskydd är att använda tecknet \, det bakvända snedstrecket. Det skyddar det tecken som följer efter det från att tolkas av skalet. Så problemet med utskriften ovan kan lösas så här:$ echo Det mest använda jokertecknet är \*
Det mest använda jokertecknet är *
$
Skalet skriver om kommandot ovan genom att ta bort beskyddet, men det tecken som var skyddat skrivs inte om på något sätt. Att skriva ut ett uttryck som 2 * 3 > 5 är lika enkelt:$ echo 2 \* 3 \> 5
2 * 3 > 5
$
Så här skulle det gå ifall jag inte hade använt beskydd:$ echo 2 * 3 > 5
$ ls
5         bellman   dikter/   lenngren
$ cat 5
2 bellman dikter lenngren 3
$ rm 5
$
Skalet tolkade * som ett jokertecken och riktade utdatan till en fil med namnet 5.

Vi har redan sett att det bakvända snedstrecket har en speciell betydelse om det skrivs sist i en kommandorad; det tolkas som att kommandot ska fortsätta på nästa rad. $ echo Gub\
> ben Noach
Gubben Noach
$
I princip plockar skalet bort det bakvända snedstrecket och nyrad-tecknet från kommandot. Men detta är enda undantaget - alla tecken utom nyrad-tecknet skyddas från skalets inblandning om de föregås av tecknet \. Det bakvända snedstrecket kan till och med skydda sig självt:$ echo Beskydd ges av tecknet \\
Beskydd ges av tecknet \
$

Man kan skydda en hel textsträng genom att omge den med apostrofer. Därigenom slipper man sätta ett bakvänt snedstreck framför varje ''farligt'' tecken i strängen. Skalet skriver om kommandot genom att ta bort apostroferna, men det som stod inom apostroferna lämnas sedan intakt.$ echo 'Tecknen * och \ skyddas så här.'
Tecknen * och \ skyddas så här.
$ echo '2 * 3 > 5'
2 * 3 > 5
$

 Med ett blanktecken menas ett mellanslag eller tabulatorsteg. Om skalet påträffar en följd av nyrad-tecken eller blanktecken, så plockas dessa bort från kommandot. Den enda effekten de har är att de separerar argument.$ cp bellman    Epistel21
$ ls
Epistel21  bellman    dikter/    lenngren
$ rm Epistel21
$
Att jag skrev flera mellanslag mellan bellman och Epistel21 spelade ingen roll, kommandot exekverades på samma sätt som om det bara hade funnits ett enda mellanslag. Skalet noterade att det första argumentet var bellman och det andra var Epistel21, men sedan togs mellanslagen bort. Därefter skickades argumenten till programmet cp, som inte fick veta att det från början fanns flera mellanslag mellan dem. På samma sätt går det här:$ echo bellman    Epistel21
bellman Epistel21
$
Skalet åt upp mellanslagen efter att argumenten hade separerats. Sedan skickades de båda argumenten till echo, som skrev ut dem med ett enda mellanslag som separator.

Men hur ska man då göra om man vill att ett argument ska innehålla blanktecken eller nyrad-tecken? Svaret på denna fråga är naturligtvis att man ska ge dessa tecken beskydd gentemot skalet.$ echo 'bellman    Epistel21'
bellman    Epistel21
$ echo 'Apostrofer kan
> skydda nyrad-tecken.'
Apostrofer kan
skydda nyrad-tecken.
$

Genom att använda beskydd kan man få filnamn som innehåller specialtecken, inklusive mellanslag och nyrad-tecken. Detta rekommenderas inte, för ju konstigare tecken ett filnamn innehåller, desto svårare blir det att hantera filnamnet. Antag att vi ändå, mot bättre vetande, vill kopiera filen bellman och ge kopian namnet Epistel 21. Här är vårt första försök:$ cp bellman Epistel 21
cp: kopiering av flera filer, men sista argumentet (21) är inte en katalog
Försök med "cp --help" för mer information.
$
Det gick inte så bra. Då skalet tolkar kommandot vi gav, tror det att bellman, Epistel och 21 är tre separata argument. Vi måste skydda mellanslaget mellan Epistel och 21 från skalet:$ cp bellman 'Epistel 21'
$ ls
Epistel 21 bellman dikter/ lenngren
$ ls -l
total 4
-rw-------   1 göran    göran         372 feb  3 15:58 Epistel 21
-rw-------   1 göran    göran         372 feb  1 16:48 bellman
drwxrwxr-x   2 göran    göran        1024 feb  1 17:12 dikter/
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
$
Den här gången gick det bättre. Tack vare att blanktecknet var skyddat, delades inte Epistel 21 upp i två argument, utan det sågs som ett enda. Vi måste skydda blanktecknet varje gång vi hänvisar till filen Epistel 21:$ wc Epistel 21
wc: Epistel: Filen eller katalogen finns inte
wc: 21: Filen eller katalogen finns inte
      0       0       0 total
$ wc Epistel\ 21
     12      71     372 Epistel 21
$
Vid kommandot wc Epistel 21 tolkades Epistel och 21 återigen som separata argument, och wc trodde då att det rörde sig om två olika filnamn. I det följande kommandot, där mellanslaget var skyddat (denna gång av ett bakvänt snedstreck), sågs däremot Epistel 21 som ett enda filnamn. När jag nedan raderar filen, demonstrerar jag ett tredje sätt att skydda blanktecknet:$ rm Epistel' '21
$ ls
bellman   dikter/   lenngren
$
Man behöver alltså inte skydda hela texten med apostrofer, bara de ''farliga'' tecknen.

Apostrofer skyddar alla tänkbara tecken, med ett undantag: de kan inte skydda sig själva. Man kan inte ha en apostrof mellan två andra, för då skulle ju den första och den mellersta apostrofen tolkas som ett par. Däremot kan apostrofer skyddas av det bakvända snedstrecket:$ echo tag dig se\'n dito en
tag dig se'n dito en
$

Det beskydd som apostrofer ger förändras ifall man sätter ett dollartecken före den första apostrofen. Uttryck som $'text' är alltså speciella. I sådana uttryck fungerar det bakvända snedstrecket som ett kommando. Det hela går ut på att man ska kunna få vilka tecken som helst att ingå i argument. Till exempel skrivs \n om till ett nyrad-tecken och \t till ett tabulatorsteg.$ echo $'den skönsta nymf, som åt dig ler,\n\tinunder armen tag.'
den skönsta nymf, som åt dig ler,
        inunder armen tag.
$
I tabell 3.2 anges vilka specialtecken som kan skrivas så här. På liknande sätt kan skriva vilket tecken som helst genom att ange dess oktalkod. I tabell 3.1 visas oktalkoden för samtliga skrivbara tecken i den teckenuppsättning, ISO-8859-1, som används i Sverige. Oktalkoden består av tre siffror. De två första står i kolumnen till vänster i tabellen medan den tredje, som ska ersätta symbolen x, står i den översta raden. (Den siffra som står under varje tecken kallas för dess decimalkod. Vi kommer inte att ha användning för decimalkoderna här.) Till exempel har tecknet ë oktalkod 353, och namnet ''Citroën'' kan skrivas så här:$ echo $'Citro\353n'
Citroën
$


 
Tabell: Teckenuppsättningen ISO-8859-1.
 
  0 1 2 3 4 5 6 7
04x
 {\isofont\symbol{'40}}  
 32  
 {\isofont\symbol{'41}}  
 33  
 {\isofont\symbol{'42}}  
 34  
 {\isofont\symbol{'43}}  
 35  
 {\isofont\symbol{'44}}  
 36  
 {\isofont\symbol{'45}}  
 37  
 {\isofont\symbol{'46}}  
 38  
 {\isofont\symbol{'47}}  
 39  
05x
 {\isofont\symbol{'50}}  
 40  
 {\isofont\symbol{'51}}  
 41  
 {\isofont\symbol{'52}}  
 42  
 {\isofont\symbol{'53}}  
 43  
 {\isofont\symbol{'54}}  
 44  
 {\isofont\symbol{'55}}  
 45  
 {\isofont\symbol{'56}}  
 46  
 {\isofont\symbol{'57}}  
 47  
06x
 {\isofont\symbol{'60}}  
 48  
 {\isofont\symbol{'61}}  
 49  
 {\isofont\symbol{'62}}  
 50  
 {\isofont\symbol{'63}}  
 51  
 {\isofont\symbol{'64}}  
 52  
 {\isofont\symbol{'65}}  
 53  
 {\isofont\symbol{'66}}  
 54  
 {\isofont\symbol{'67}}  
 55  
07x
 {\isofont\symbol{'70}}  
 56  
 {\isofont\symbol{'71}}  
 57  
 {\isofont\symbol{'72}}  
 58  
 {\isofont\symbol{'73}}  
 59  
 {\isofont\symbol{'74}}  
 60  
 {\isofont\symbol{'75}}  
 61  
 {\isofont\symbol{'76}}  
 62  
 {\isofont\symbol{'77}}  
 63  
10x
 {\isofont\symbol{'100}}  
 64  
 {\isofont\symbol{'101}}  
 65  
 {\isofont\symbol{'102}}  
 66  
 {\isofont\symbol{'103}}  
 67  
 {\isofont\symbol{'104}}  
 68  
 {\isofont\symbol{'105}}  
 69  
 {\isofont\symbol{'106}}  
 70  
 {\isofont\symbol{'107}}  
 71  
11x
 {\isofont\symbol{'110}}  
 72  
 {\isofont\symbol{'111}}  
 73  
 {\isofont\symbol{'112}}  
 74  
 {\isofont\symbol{'113}}  
 75  
 {\isofont\symbol{'114}}  
 76  
 {\isofont\symbol{'115}}  
 77  
 {\isofont\symbol{'116}}  
 78  
 {\isofont\symbol{'117}}  
 79  
12x
 {\isofont\symbol{'120}}  
 80  
 {\isofont\symbol{'121}}  
 81  
 {\isofont\symbol{'122}}  
 82  
 {\isofont\symbol{'123}}  
 83  
 {\isofont\symbol{'124}}  
 84  
 {\isofont\symbol{'125}}  
 85  
 {\isofont\symbol{'126}}  
 86  
 {\isofont\symbol{'127}}  
 87  
13x
 {\isofont\symbol{'130}}  
 88  
 {\isofont\symbol{'131}}  
 89  
 {\isofont\symbol{'132}}  
 90  
 {\isofont\symbol{'133}}  
 91  
 {\isofont\symbol{'134}}  
 92  
 {\isofont\symbol{'135}}  
 93  
 {\isofont\symbol{'136}}  
 94  
 {\isofont\symbol{'137}}  
 95  
14x
 {\isofont\symbol{'140}}  
 96  
 {\isofont\symbol{'141}}  
 97  
 {\isofont\symbol{'142}}  
 98  
 {\isofont\symbol{'143}}  
 99  
 {\isofont\symbol{'144}}  
 100  
 {\isofont\symbol{'145}}  
 101  
 {\isofont\symbol{'146}}  
 102  
 {\isofont\symbol{'147}}  
 103  
15x
 {\isofont\symbol{'150}}  
 104  
 {\isofont\symbol{'151}}  
 105  
 {\isofont\symbol{'152}}  
 106  
 {\isofont\symbol{'153}}  
 107  
 {\isofont\symbol{'154}}  
 108  
 {\isofont\symbol{'155}}  
 109  
 {\isofont\symbol{'156}}  
 110  
 {\isofont\symbol{'157}}  
 111  
16x
 {\isofont\symbol{'160}}  
 112  
 {\isofont\symbol{'161}}  
 113  
 {\isofont\symbol{'162}}  
 114  
 {\isofont\symbol{'163}}  
 115  
 {\isofont\symbol{'164}}  
 116  
 {\isofont\symbol{'165}}  
 117  
 {\isofont\symbol{'166}}  
 118  
 {\isofont\symbol{'167}}  
 119  
17x
 {\isofont\symbol{'170}}  
 120  
 {\isofont\symbol{'171}}  
 121  
 {\isofont\symbol{'172}}  
 122  
 {\isofont\symbol{'173}}  
 123  
 {\isofont\symbol{'174}}  
 124  
 {\isofont\symbol{'175}}  
 125  
 {\isofont\symbol{'176}}  
 126  
 
  0 1 2 3 4 5 6 7
24x
   
 160  
 {\isofont\symbol{'241}}  
 161  
 {\isofont\symbol{'242}}  
 162  
 {\isofont\symbol{'243}}  
 163  
 {\isofont\symbol{'244}}  
 164  
 {\isofont\symbol{'245}}  
 165  
 {\isofont\symbol{'246}}  
 166  
 {\isofont\symbol{'247}}  
 167  
25x
 {\isofont\symbol{'250}}  
 168  
 {\isofont\symbol{'251}}  
 169  
 {\isofont\symbol{'252}}  
 170  
 {\isofont\symbol{'253}}  
 171  
 {\isofont\symbol{'254}}  
 172  
 {\isofont\symbol{'255}}  
 173  
 {\isofont\symbol{'256}}  
 174  
 {\isofont\symbol{'257}}  
 175  
26x
 {\isofont\symbol{'260}}  
 176  
 {\isofont\symbol{'261}}  
 177  
 {\isofont\symbol{'262}}  
 178  
 {\isofont\symbol{'263}}  
 179  
 {\isofont\symbol{'264}}  
 180  
 {\isofont\symbol{'265}}  
 181  
 {\isofont\symbol{'266}}  
 182  
 {\isofont\symbol{'267}}  
 183  
27x
 {\isofont\symbol{'270}}  
 184  
 {\isofont\symbol{'271}}  
 185  
 {\isofont\symbol{'272}}  
 186  
 {\isofont\symbol{'273}}  
 187  
 {\isofont\symbol{'274}}  
 188  
 {\isofont\symbol{'275}}  
 189  
 {\isofont\symbol{'276}}  
 190  
 {\isofont\symbol{'277}}  
 191  
30x
 {\isofont\symbol{'300}}  
 192  
 {\isofont\symbol{'301}}  
 193  
 {\isofont\symbol{'302}}  
 194  
 {\isofont\symbol{'303}}  
 195  
 {\isofont\symbol{'304}}  
 196  
 {\isofont\symbol{'305}}  
 197  
 {\isofont\symbol{'306}}  
 198  
 {\isofont\symbol{'307}}  
 199  
31x
 {\isofont\symbol{'310}}  
 200  
 {\isofont\symbol{'311}}  
 201  
 {\isofont\symbol{'312}}  
 202  
 {\isofont\symbol{'313}}  
 203  
 {\isofont\symbol{'314}}  
 204  
 {\isofont\symbol{'315}}  
 205  
 {\isofont\symbol{'316}}  
 206  
 {\isofont\symbol{'317}}  
 207  
32x
 {\isofont\symbol{'320}}  
 208  
 {\isofont\symbol{'321}}  
 209  
 {\isofont\symbol{'322}}  
 210  
 {\isofont\symbol{'323}}  
 211  
 {\isofont\symbol{'324}}  
 212  
 {\isofont\symbol{'325}}  
 213  
 {\isofont\symbol{'326}}  
 214  
 {\isofont\symbol{'327}}  
 215  
33x
 {\isofont\symbol{'330}}  
 216  
 {\isofont\symbol{'331}}  
 217  
 {\isofont\symbol{'332}}  
 218  
 {\isofont\symbol{'333}}  
 219  
 {\isofont\symbol{'334}}  
 220  
 {\isofont\symbol{'335}}  
 221  
 {\isofont\symbol{'336}}  
 222  
 {\isofont\symbol{'337}}  
 223  
34x
 {\isofont\symbol{'340}}  
 224  
 {\isofont\symbol{'341}}  
 225  
 {\isofont\symbol{'342}}  
 226  
 {\isofont\symbol{'343}}  
 227  
 {\isofont\symbol{'344}}  
 228  
 {\isofont\symbol{'345}}  
 229  
 {\isofont\symbol{'346}}  
 230  
 {\isofont\symbol{'347}}  
 231  
35x
 {\isofont\symbol{'350}}  
 232  
 {\isofont\symbol{'351}}  
 233  
 {\isofont\symbol{'352}}  
 234  
 {\isofont\symbol{'353}}  
 235  
 {\isofont\symbol{'354}}  
 236  
 {\isofont\symbol{'355}}  
 237  
 {\isofont\symbol{'356}}  
 238  
 {\isofont\symbol{'357}}  
 239  
36x
 {\isofont\symbol{'360}}  
 240  
 {\isofont\symbol{'361}}  
 241  
 {\isofont\symbol{'362}}  
 242  
 {\isofont\symbol{'363}}  
 243  
 {\isofont\symbol{'364}}  
 244  
 {\isofont\symbol{'365}}  
 245  
 {\isofont\symbol{'366}}  
 246  
 {\isofont\symbol{'367}}  
 247  
37x
 {\isofont\symbol{'370}}  
 248  
 {\isofont\symbol{'371}}  
 249  
 {\isofont\symbol{'372}}  
 250  
 {\isofont\symbol{'373}}  
 251  
 {\isofont\symbol{'374}}  
 252  
 {\isofont\symbol{'375}}  
 253  
 {\isofont\symbol{'376}}  
 254  
 {\isofont\symbol{'377}}  
 255  


 
Tabell 3.2: Hur specialtecken skrivs.
 
\a alarmsignal
\b $\longleftarrow$
\e Esc
\f sidframmatningstecken
\n nyrad-tecken
\r vagnretur
\t tabulatorsteg
\v vertikalt tabulatorsteg
\\ bakvänt snedstreck
\ nnn tecken från tabell 3.1

Slutligen vill vi nämna att det finns ett tredje och sista sätt att beskydda tecken och teckensträngar, nämligen att omge dem med citationstecken. Citationstecken fungerar ungefär som apostrofer, men de ger inte ett lika säkert skydd. Varken det bakvända snedstrecket, dollartecknet, den bakvända apostrofen ` eller citationstecknet självt skyddas av citationstecken. Det finns två skäl till att man ibland använder citationstecken som skydd. Det ena skälet är att man därigenom kan skydda en textsträng som innehåller apostrofer. Det andra, som vi ska återkomma till i kapitel 6, är att man med hjälp av citationstecken kan få skalet att göra vissa, och undvika övriga, av de vanliga omskrivningarna.$ echo "tag dig se'n dito en"
tag dig se'n dito en
$
Om ett citationstecken ska skrivas ut, så måste det skyddas - antingen av ett bakvänt snedstreck eller av apostrofer:$ echo 'Säg "hej" till farbror Göran!'
Säg "hej" till farbror Göran!
$
Vill man ha ett dollartecken, ett bakvänt snedstreck eller ett nytt citationstecken mellan två citationstecken, så ska man ett bakvänt snedstreck som skydd.$ mening="En \"meningslös\" mening med \\ och \$"
$ echo $mening
En "meningslös" mening med \ och $
$

  
4. Fönstersystemet X

4.1 Introduktion till X

Det enklaste sättet att starta fönstersystemet X är att logga in via XDM; då kommer man automatiskt in i X. Figur 1.1 visar hur inloggningsskärmen kan se ut. På datorer som inte kör XDM måste man starta X manuellt med kommandot startx (eller möjligtvis xinit) från prompten efter att man loggat in genom en textterminal.

Vad som sker efter att man startat X beror på hur systemet är konfigurerat. I de flesta fall startas dock en så kallad fönsterhanterare. Fönsterhanteraren tillhandahåller bland annat en meny med vars hjälp man kan starta X-program, eller X-klienter som de kallas enligt gängse terminologi.

Bilden från en X-klient visas i ett (eller kanske flera) så kallade fönster på bildskärmen. Oftast är fönstren rektangulära. Bakgrunden på bildskärmen kallas för rotfönstret. Om man kör flera klienter samtidigt, så kan det bli många fönster på skärmen. I figur 4.5 finns det tre fönster (förutom rotfönstret) på bildskärmen, varav två delvis överlappar varandra.

Vanligtvis körs ett X-skal igång automatiskt när man startat X. Ett X-skal är helt enkelt ett fönster i vilket man kör sitt skal, så att man kan ge kommandon precis som vid skalet i en vanlig textterminal. Fönstret som X-skalet kör i kallas ibland för en terminalemulator eller för en pseudoterminal. Om inget X-skal startas automatiskt, kan man förmodligen starta ett med hjälp av fönsterhanterarens meny. Hur man hittar menyn beror återigen på konfigurationen, men ofta ska man flytta pekaren (den markör man styr med musen) till rotfönstret och trycka på en av musknapparna. Figur 4.1 visar hur menyn kan se ut. För att få fram ett X-skal ska man välja xterm eller något av de alternativ som finns under rubriken XShells.

  Bilden: Fönsterhanterarens meny. meny.gif

  Bilden: Programmet xclock. xclock.gif

Ta fram ett X-skal nu! Ge kommandot$ xsetroot -solid Darkred
$
vid prompten i X-skalet. Om det hela fungerar, så blir rotfönstret mörkrött. I stället för Darkred kan man ta färger som Green, Yellow, White, Midnightblue eller någon av de hundratals andra som finns angivna i filen /usr/lib/X11/rgb.txt (kommandot showrgb skriver ut dem).

Programmet xclock visar helt enkelt en klocka i ett fönster. Ge kommandot xclock & i ett X-skal för att prova programmet! Figur 4.2 visar hur det ser ut. Det är viktigt att man skriver tecknet & efter xclock. Skälet är att prompten i X-skalet inte återvänder förrän ett givet kommando är klart om man inte avslutar kommandot med &. Kommandot xsetroot tar mycket kort tid för datorn att köra, så man behöver inte starta det som en bakgrundsprocess. Om man råkar glömma tecknet & efter ett kommando som kör i evighet, så blir X-skalet oanvändbart. Man kan naturligtvis starta ett nytt skal då. Men man kan också avsluta processen med C-c. Eller man kan tillfälligt stoppa den med C-z och därefter köra igång den i bakgrunden med kommandot bg, varefter X-skalet åter blir användbart.

X tillhandahåller en klippbuffert. Detta innebär att man kan måla över ett stycke text i ett fönster genom att föra pekaren över texten med vänsterknappen intryckt. Texten kan klistras in någon annanstans genom att man trycker på musens mittknapp.

Vad som händer inuti ett fönster, och hur det ser ut där, sköter den enskilda klienten; samspelet mellan de olika fönstren kontrolleras av fönsterhanteraren. Det går att köra X utan någon fönsterhanterare, men då förloras mycket av funktionaliteten. Det finns många fönsterhanterare att välja mellan. Man kan bara köra en fönsterhanterare i taget, men det går utmärkt att stoppa en fönsterhanterare, och köra igång en annan, mitt i en X-session. Systemadministratören kan konfigurera de flesta av fönsterhanterarens funktioner, och varje användare kan ändra konfigurationen för sin egen del. Därför kan det vara svårt att förutsäga exakt vilka tjänster som erbjuds av fönsterhanteraren. Men vi ger ändå några exempel på vad man kan förvänta sig:

  xcalc-utan.gif

Bilden: Programmet xcalc, utan dekoration.

xcalc-med.gif

Bilden: Programmet xcalc, dekorerat av fönsterhanteraren FVWM2.

  xfontsel-twm.gif

Bilden: Programmet xfontsel, dekorerat av TWM.

xfontsel-olvwm.gif

Bilden: Programmet xfontsel, dekorerat av OLVWM.

xfontsel-fvwm95.gif

Bilden: Programmet xfontsel, dekorerat av FVWM95.

4.2 Standardflaggor

Det finns ett antal standardflaggor som många, om än inte alla, X-klienter kan ta emot. En förteckning över dem finns i tabell 4.1. Med deras hjälp kan man till exempel ange storlek och position för klientens fönster samt välja färg på text och bakgrund. Vi ska i detta avsnitt ge en inledande beskrivning av hur standardflaggorna kan användas.


 
Tabell 4.1: Standardflaggor
 
Flagga Funktion Resursnamn
-display display Välj display att visa fönstren i .display
-geometry geometristräng Bestäm storlek och position .geometry
-background färg Sätt bakgrundsfärgen till färg *background
-bg färg Sätt bakgrundsfärgen till färg *background
-foreground färg Sätt förgrundsfärgen till färg *foreground
-fg färg Sätt förgrundsfärgen till färg *foreground
-font fontsträng Välj typsnitt för text *font
-fn fontsträng Välj typsnitt för text *font
-reverse Växla för- och bakgrundsfärg .reverseVideo: on
-rv Växla för- och bakgrundsfärg .reverseVideo: on
+rv Växla inte för- och bakgrundsfärg .reverseVideo: off
-title titel Välj titel .title
-iconic Starta som ikon .iconic: True
-borderwidth bredd Sätt rambredden till bredd punkter .borderWidth
-bw bredd Sätt rambredden till bredd punkter .borderWidth
-bordercolor färg Sätt ramens färg till färg *borderColor
-bd färg Sätt ramens färg till färg *borderColor
-name namn Ge klienten namnet namn .name
-xrm resurssträng Läs in resurs  
-xnlLanguage lokal Välj lokal .xnlLanguage
-selectionTimeout tid Sätt timeout till tid millisekunder .selectionTimeout
-synchronous Sätt på synkront debug-läge .synchronous: on
+synchronous Stäng av synkront debug-läge .synchronous: off
-xtsessionID id Sätt ID .sessionID

Grafiken i X är baserad på punkter. Detta är något som kan vålla problem eftersom punkterna är olika stora på olika bildskärmar. Om antalet punkter är stort i förhållande till skärmens storlek, så säger man att skärmen är högupplösande. Antal punkter som visas på skärmen kan till exempel vara 1024x768, dvs 1024 punkter horisontalt och 768 punkter vertikalt. En högupplösande skärm kan visa 1600x1200 eller fler punkter, medan en lågupplösande kan visa 640x480 eller tom ännu färre punkter. Punkterna på en högupplösande skärm är mindre än punkterna på en lågupplösande skärm, så ett fönster som är tex 600 punkter brett och 400 punkter högt blir mindre på en högupplösande skärm än på en lågupplösande. Ett tolvpunkters typsnitt blir också mindre på en högupplösande skärm.

Lyckligtvis tillåter de flesta X-klienter att man ändrar storleken på fönstren. Därigenom kan man välja ett utseende som passar den egna bildskärmen. För att ändra fönsterstorleken tar man hjälp av fönsterhanteraren. Men man kan ofta också ange önskad storlek med flaggan -geometry redan när klienten startas. Fönstrets storlek specificeras som breddxhöjd. Vilken enhet som används för bredd respektive höjd avgör den enskilda klienten. Oftast anges de i antal punkter, men för vissa textbaserade program som Emacs och X-skalet xterm anges de i antal rader respektive kolumner text.

Standardstorleken för klockan xclock är 164x164 punkter. För att få storleken 150x100 punkter startar vi programmet i stället med kommandot xclock -geometry 150x100 &. Det går också att ange fönstrets position på skärmen. Först anger man horisontalt läge. För att placera fönstret tex 210 punkter från vänster skriver man +210. Detta innebär att avståndet mellan bildens vänstra kant och fönstrets vänsterkant är 210 punkter. Om man i stället skriver -175, så placeras fönstret 175 punkter från höger, dvs antalet punkter från fönstrets högerkant till bildens högra kant blir 175. På motsvarande sätt anges det vertikala läget. Tex betyder -50 att fönstrets nedre kant ska vara 50 punkter från bildens nedre kant. Kommandot xclock -geometry -20+10 & placerar klockfönstret nära det övre högra hörnet av bildskärmen, 20 punkter till vänster och 10 punkter ner. Naturligtvis kan man ange både storlek och position för en klient. Tex ger kommandot$ xclock -geometry 100x100+0-0 &
$
en klocka av storlek 100x100 i det nedre vänstra hörnet.

Ofta tillåter X-klienterna att man använder förkortningar i flaggorna ifall det inte ger upphov till tvetydigheter. Förutom geometry finns det ingen flagga som xclock använder och som börjar på ''g'', så kommandot ovan kan också skrivas xclock -g 100x100+0-0 &. Om det tex hade funnits en flagga med namnet getstr, så skulle man kunna förkorta -geometry till -geo men inte till -g eller -ge.

Med förgrunden i ett fönster menas vanligtvis texten. Vissa utritade detaljer som tex tim- och minutstrecken i xclock kan också höra till förgrunden. Det som inte är förgrund kallas för bakgrund. Många X-program tillåter att man anger för- och bakgrundsfärg med flaggorna -fg respektive -bg. Prova tex$ xclock -fg DeepSkyBlue -bg Yellow &
$
Flaggan -rv växlar förgrundsfärg mot bakgrundsfärg och omvänt. Som exempel rekommenderas återigen xclock, dvs prova kommandot xclock -rv. När du fått för många klockor på skärmen kan det vara lämpligt att ge kommandot killall xclock.

Programmet xsetroot är till för att ändra rotfönstrets egenskaper - inte enbart dess färg. Man kan också använda det för att byta utseende samt för- och bakgrundsfärg på pekaren. Prova tex$ xsetroot -cursor_name pirate -bg Black -fg Yellow
$
Flytta sedan pekaren till rotfönstret, så ser du pekaren ''Pirate'' i gult med svart ram. Namnen på alla tillgängliga pekare räknas up i tabell 4.2. Kommandot xfd -fn cursor ger ett fönster med bilder av samtliga dessa markörer.

 
Tabell: Namnen på alla pekare.
 
X_cursor arrow based_arrow_down based_arrow_up
boat bogosity bottom_left_corner bottom_right_corner
bottom_side bottom_tee box_spiral center_ptr
circle clock coffee_mug cross
cross_reverse crosshair diamond_cross dot
dotbox double_arrow draft_large draft_small
draped_box exchange fleur gobbler
gumby hand1 hand2 heart
icon iron_cross left_ptr left_side
left_tee leftbutton ll_angle lr_angle
man middlebutton mouse pencil
pirate plus question_arrow right_ptr
right_side right_tee rightbutton rtl_logo
sailboat sb_down_arrow sb_h_double_arrow sb_left_arrow
sb_right_arrow sb_up_arrow sb_v_double_arrow shuttle
sizing spider spraycan star
target tcross top_left_arrow top_left_corner
top_right_corner top_side top_tee trek
ul_angle umbrella ur_angle watch
xterm      

Med hjälp av flaggan -fn kan man välja fonter (eller typsnitt) för den text som skrivs ut av X-klienterna. Man kan också välja fonternas storlek, lutning och tjocklek etc.

Programmet xlsfonts skriver ut vilka fonter som finns tillgängliga. Som synes nedan finns det 1403 stycken fonter på min dator. Därför tar jag bara med de första 10 raderna av utskriften från xlsfonts.$ xlsfonts | wc
1403 1643 77534
$ xlsfonts | head
-adobe-courier-bold-i-normal-0-0-0-0-m-0-iso8859-1
-adobe-courier-bold-o-normal-0-0-100-100-m-0-iso8859-1
-adobe-courier-bold-o-normal-0-0-75-75-m-0-iso8859-1
-adobe-courier-bold-o-normal-10-100-75-75-m-60-iso8859-1
-adobe-courier-bold-o-normal-10-100-75-75-m-60-iso8859-1
-adobe-courier-bold-o-normal-11-80-100-100-m-60-iso8859-1
-adobe-courier-bold-o-normal-11-80-100-100-m-60-iso8859-1
-adobe-courier-bold-o-normal-12-120-75-75-m-70-iso8859-1
-adobe-courier-bold-o-normal-12-120-75-75-m-70-iso8859-1
-adobe-courier-bold-o-normal-14-100-100-100-m-90-iso8859-1
Brutet rör
$
Varje rad ovan är namnet på ett typsnitt. För att se hur typsnittet font ser ut kan man ge kommandot xfd -fn font. Som exempel på hur fonter används tar vi programmet xclock, men med digital tidsvisning i stället för analog. Kommandot xclock -digital & startar en digitalklocka som uppdateras en gång i minuten. (Flaggan kan, som nedan, förkortas till -d.) Prova det kommandot, och jämför sedan tex med följande kommando:

xclock -d -fn -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1
Naturligtvis är det är otympligt att skriva så långa kommandon. Ett enklare sätt är att använda resurser, något vi berättar om i avsnitt 4.4. Men vad betyder de olika fälten i fontnamnet? Man kan skriva jokertecknet * i ett fält om det inte spelar någon roll vilket värde det blir. I så fall får man den första fonten som X hittar och som matchar den fontsträng man angett.

Många av fälten hänger ihop, värdena i vissa fält beror på värdena i andra. Därför kan man inte bara dikta upp en fontsträng och tro att det ska finnas en sådan font. I stället kan man försöka finna en font men hjälp av xlsfont. Men det finns ett annat, bättre sätt: programmet xfontsel, se figur 4.4. Programmet startas med kommandot xfontsel &. Inne i programmet får man via menyer välja värden i de olika fälten, och så fort något värde ändrats får man se ett smakprov av tecken från den font man valt. Man får bara välja bland existerande fonter. När man funnit en font man är nöjd med kan man klicka på knappen ''Select'' i xfontsel-fönstret. Då kopieras fontsträngen in i X-klippbufferten, så att man kan klippa in den tex i ett X-skal eller i en textredigerare.

Några flaggor gäller fönstrens dekoration. Vilken effekt dessa har beror på hur fönsterhanteraren väljer att hantera dem. Tex är flaggan -bd Green en önskan om att ramen kring fönstret ska vara grön, och -bw 10 att ramen ska vara 10 punkter bred. Men de flesta fönsterhanterare struntar i denna anvisning och ritar sin egen ram i stället. (Prova att köra utan någon fönsterhanterare, och ge tex kommandot xterm -bd Green -bw 10 &.) Flaggan -title ger fönstret en ny titel, som fönsterhanteraren skriver ut i titelraden på vissa av fönstren. Som exempel kan man ta xterm -title X-skal &.

Flaggan -iconic uttrycker en önskan om att fönstret ska startas som en ikon. Vilken effekt denna flagga får beror på fönsterhanteraren. Ett exempel är xterm -iconic &.

Vad flaggan -display används till berättar vi i avsnitt 4.3. Flaggorna -xrm och -name återkommer vi till i avsnitt 4.5.

   
4.3 X över nätverk

Användaren för in data och signaler till X genom ett tangentbord och ett pekdon. Vanligtvis är pekdonet en mus med tre knappar. Kommunikationen från X till användaren sker genom bildskärmen. En uppsättning av ett tangentbord och en mus som kontrollerar en eller flera bildskärmar kallas för en display. Om det finns fler än en bildskärm i en display, så behöver skärmarna inte visa samma bild, men de styrs som sagt av samma tangentbord och mus. De flesta displayer har dock bara en bildskärm, som i figur 4.5.

  
Figur: En typisk display. Datorn den är kopplad till finns utanför bilden.
\fbox{\includegraphics[width=0.7\textwidth]{../Bilder/X.eps}}

X-klienter har inte tillgång till displayen direkt, utan all kommunikation går via X-servern, ett så kallat systemprogram som kör på den dator displayen är kopplad till. Servern avläser signaler från tangentbord och mus och förmedlar dem till klienterna, och på begäran från klienterna ritar servern fönster och annan grafik på bildskärmen via grafikkortet.

X tillåter att man har flera displayer kopplade till en och samma dator. Man måste i så fall köra en separat X-server för varje display. Då varje display kräver sitt eget tangentbord och sin egen mus är det ovanligt att datorer har mer än en display. Men liksom en enda terminal kan tjäna som flera virtuella terminaler som man växlar mellan genom att trycka Ctrl och Alt samt någon av tangenterna F1 till F6, kan man också ha flera virtuella displayer som man växlar mellan genom att trycka Ctrl och Alt samt någon av tangenterna F7, F8 osv. Det krävs mycket minne för att datorn ska kunna hantera flera virtuella displayer. Den första displayen på den lokala datorn heter :0 (dvs ett kolon följt av siffran noll). Den andra displayen heter :1, den tredje :2 osv. Återigen bör vi poängtera att man oftast bara har en display per dator, och att den heter :0. Om mer än en bildskärm är kopplad till samma display, så skiljer man på de olika skärmarna genom att ange en punkt och skärmens nummer efter diplayens namn. Den första skärmen har nummer noll, den andra har nummer ett osv. Den andra bildskärmen på tredje displayen heter alltså :2.1. Om man utelämnar bildskärmens nummer, och skriver tex :2, så förutsätts det att man menar bildskärm nummer noll. Om man vill referera till en display som finns på en annan dator än den lokala, så anger man bara datorns namn eller IP-nummer före kolonet, tex fasolt:0 eller fasolt.turangalila.se:2.1 eller 192.168.1.1:0.0.

För att starta en X-server på (den virtuella) displayen :1 ska man från en virtuell terminal ge kommandot startx - :1 eller tex X -indirect localhost :1 -bpp 16.

X är ett nätverksbaserat fönstersystem. Klienterna kan köra på en annan dator än den som servern kör på och som bildskärmen är kopplad till. Under X kan man köra några klienter lokalt (dvs på den dator man sitter vid och som servern kör på), och andra klienter på flera andra datorer om man så vill. Klienterna utnyttjar då datorkraft från de andra datorerna, medan själva bilden som klienterna visar upp skickas till den egna bildskärmen. Kommunikationen mellan klienterna och servern går över nätverket, i ett slags språk (kallat för X-protokollet) som är oberoende av datorarkitektur och operativsystem. Så det har ingen betydelse vilken slags dator klienterna kör på; en X-klient på tex en Sun-dator kan visa sin bild på en Linuxdator och vice versa. Användaren behöver inte vara medveten om att vissa program kör på en avlägsen dator, man kan inte se någon skillnad jämfört med om de kör lokalt. Inte heller behöver programmen ''vara medvetna om'' att deras bild visas på en annan dator. Därför kan det sägas att X är integrerat med nätverket på ett genomskinligt sätt.

  
Figur: X över nätverket.
\fbox{\includegraphics[width=0.7\textwidth]{../Bilder/Xremote.eps}}

I figur 4.6 sitter jag vid datorn fafner. Jag kör en X-klient lokalt (fönster A). Dessutom kör jag ett X-program på fasolt, och detta program har instruerats att visa sin bild på displayen fafner:0 (fönster B). Vilken användare som helst på vilken dator som helst på nätverket, inklusive Internet, kan starta en X-klient på min display ifall jag tillåter det. Omfattande säkerhetsanordningar finns dock inbyggda i X för att skydda användare från obehöriga intrång.

Det enklaste och i särklass bästa sättet att köra X över nätverket är att från den dator man sitter vid (låt oss anta att den heter fafner) logga in på den andra datorn (vi antar att den heter fasolt) med programmet ssh. Därefter kan man starta X-klienter på fasolt, och bilden hamnar automatiskt på displayen på fafner.$ ssh fasolt -l kobjer
kobjer@fasolt's password:
Last login: Thu Feb 12 00:01:36 1998 from fafner.turangali
Linux fasolt 2.0.31 #1 Sat Nov 1 15:52:35 CET 1997 i586 unknown
$ xclock &
[1] 12636
$
Så enkelt var det att logga in som användaren kobjerfasolt och starta X-klienten xclock.

Programmet ssh finns inte installerat på alla datorer, så ibland fungerar inte metoden ovan. Då tvingas man logga in på fasolt med telnet eller rlogin. Man bör vara medveten om att dessa program skickar all information i klartext över nätverket, så det är lätt för inkräktare att avlyssna kommunikationen. I motsats till ssh gör inte telnet och rlogin automatiskt två nödvändiga inställningar. Den ena av dessa inställningar är att fasolt ska instrueras att visa sin bild på displayen fafner:0. Detta kan man göra genom att ge kommandot export DISPLAY=fafner:0fasolt eller genom att använda standardflaggan -display fafner:0 varje gång en X-klient startas på fasolt. Den andra inställningen är att användaren kobjerfasolt måste känna till en hemlig kod för displayen fafner:0 för att få tillgång till den. Vi kan ta reda på hur den hemliga koden ser ut genom att på fafner ge kommandot xauth list fafner:0. Efter att ha loggat in på fasolt med telnet använder vi xauth igen för att överlämna koden till fasolt:$ xauth list fafner:0
fafner.turangalila.se:0 MIT-MAGIC-COOKIE-1 5f3c426a0e2335427a6f6e3641616761 $ telnet fasolt
fasolt login: kobjer
Lösenord:
Linux fasolt 2.0.31 #1 Sat Nov 1 15:52:35 CET 1997 i586 unknown
$ xauth add fafner:0 . 5f3c426a0e2335427a6f6e3641616761
$ xclock -display fafner:0 &
[1] 12646
$
Genom att ta hjälp av X-klippbufferten slipper man skriva av koden ''för hand''.

Så här går det om man inte för över den hemliga koden:$ rlogin fasolt -l kobjer
Lösenord:
Linux fasolt 2.0.31 #1 Sat Nov 1 15:52:35 CET 1997 i586 unknown
$ xclock -display fafner:0
Xlib: connection to "fafner:0.0" refused by server
Xlib: Invalid MIT-MAGIC-COOKIE-1 key
Error: Can't open display: fafner:0
$

Den hemliga koden lagras i en fil med namnet .Xauthority i ens hemkatalog. I lokala nätverk brukar hemkatalogen vara densamma oberoende av vilken dator man loggar in på, och i så fall slipper man föra över den hemliga koden - den är ju då redan tillgänglig på de andra datorerna.

På datorer som inte heller kan hantera hemliga koder återstår bara den sämsta metoden, nämligen xhost. Den kan bara användas ifall säkerhet inte spelar någon som helst roll. Om jag på fafner ger kommandot xhost +fasolt, så får alla användare på fasolt tillgång till min display. Då kan jag logga in på fasolt och starta mina X-klienter. Därefter bör jag från fafner ge kommandot xhost -fasolt så att inga fler X-klienter kan komma åt min display från fasolt. (Ibland krävs också att man skapar en .rhosts-fil, se man rhosts. Detta är ungefär detsamma som att rulla ut röda mattan för hackers.) Kommandot xhost + ger hela nätverket obegränsad tillgång till den egna displayen, något som dock inte rekommenderas...

  
4.4 Resurser

Vi har sett att man med hjälp av flaggor på kommandoraden kan välja sådant som de olika fönstrens storlek, bakgrundsfärg och färg på text och annan förgrund. Det blir dock på tok för jobbigt om man måste skriva in alla flaggor varje gång. Och visst finns det ett bättre sätt att konfigurera X-klienterna! Inställningarna görs med hjälp av ett oerhört kraftfullt system som kallas för resurser. Som vanligt i UNIX-sammanhang är detta system tyvärr också oerhört komplicerat, och det krävs tålamod om man ska kunna lära sig behärska det.

Vi ska ta programmet xclock som ett exempel i denna inledande diskussion av resurser. I bruksanvisningen till xclock (ge kommandot man xclock för att läsa den) står det bland annat följande:

X DEFAULTS
       This program uses the Clock widget.  It understands all of
       the core resource names and classes as well as:

       width (class Width)
               Specifies the width of the clock.  The default for
               analog clocks is 164 pixels; the default for digi­
               tal clocks is whatever is needed to hold the clock
               when displayed in the chosen font.

       height (class Height)
               Specifies  the  height  of the clock.  The default
               for analog clocks is 164 pixels; the  default  for
               digital  clocks  is whatever is needed to hold the
               clock when displayed in the chosen font.

       update (class Interval)
               Specifies the frequency in seconds  at  which  the
               time should be redisplayed.

       foreground (class Foreground)
               Specifies the color for the tic marks. The default
               is depends on whether reverseVideo  is  specified.
               If   reverseVideo  is  specified  the  default  is
               lwhite, otherwise the default is black.

       hands (class Foreground)
               Specifies the color of the insides of the  clock's
               hands. The default is depends on whether reverseV­
               ideo is specified.  If reverseVideo  is  specified
               the  default  is  lwhite, otherwise the default is
               black.

       highlight (class Foreground)
               Specifies the color used to highlight the  clock's
               hands. The default is
                depends on whether reverseVideo is specified.  If
               reverseVideo is specified the default  is  lwhite,
               otherwise the default is black.

       analog (class Boolean)
               Specifies whether or not an analog clock should be
               used instead of a digital  one.   The  default  is
               True.

       chime (class Boolean)
               Specifies  whether or not a bell should be rung on
               the hour and half hour.

       padding (class Margin)
               Specifies the amount of internal padding in pixels
               to be used.  The default is 8.

       font (class Font)
               Specifies  the  font  to  be  used for the digital
               clock.  Note that variable width  fonts  currently
               will not always display correctly.
Vi ska undan för undan förklara vad allt detta innebär. Men först ska vi naturligtvis visa vad man kan ha för nytta av det! Skapa därför en fil, som tex kan heta resurstest, och som innehåller följande:
xclock.clock.analog: True
xclock.clock.height: 80
xclock.clock.width: 80
xclock.clock.padding: 4
xclock.clock.foreground: Black
xclock.clock.hands: Black
xclock.clock.highlight: Black
xclock.clock.update: 1
xclock.clock.chime: True
Den första raden anger att vi vill att klockan ska vara analog i stället för digital. De båda följande raderna ger storleken (antal punkter) för det fönster klockan ska visas i. Den fjärde raden anger avståndet (antal punkter) från kanten av klockans bild till kanten av det fönster klockan ritas i. Sedan kommer färgen på de små streck som markerar timmar och minuter på urtavlan. Raderna sex och sju ovan anger färg för klockans visare; en inre färg (rad sex) och en kantfärg (rad sju). Det åttonde raden anger intervallet (i antal sekunder) mellan uppdateringarna av klockan. Den sista raden anger att klockan ska tuta till varje hel- och halvtimme.

För att få xclock att följa dessa anvisningar ska vi läsa in filen till X med kommandot xrdb -load resurstest. Gör det, och starta sedan en ny xclock. Ser du att klockfönstret har blivit mindre och att det nu finns en sekundvisare? Ändra nu några av värdena i filen resurstest - välj några snyggare färger eller en annan storklek. Ge därefter åter kommandot xrdb -load resurstest och starta en ny klocka. När du har funnit de inställningar du tycker bäst om, vill du kanske att de ska finnas tillgängliga varje gång du loggar in på nytt? Lägg i så fall in raderna från filen resurstest i en fil med namnet .Xresources i din hemkatalog - om det finns en sådan fil, så läses den nämligen in då du startar X. Det finns en annan resursfil, /etc/X11/Xresources, som systemadministratören sköter. Den är gemensam för alla användare, och den läses in före den privata .Xresources. Filerna heter iallafall så på Debiansystem. På din dator kanske namnen är något annorlunda; granska startfilerna för X eller fråga systemadministratören för att få veta exakt. Nåväl, skriv nu in resurserna för X-clock i din privata resursfil, gå ur X och starta om. Kör sedan xclock och kontrollera att dina ändringar har trätt ikraft.

I de resursfiler som xrdb ska läsa in förekommer rader av formen

resursvariabel: värde
Resursvariabeln kan tex heta xclock.clock.hands, och värdet kan tex vara Black.

Syntaxen för namnet på en resursvariabel är

klient.objekt.delobjekt (...) .attribut
Resursvariabeln är uppbyggd av två eller fler ord som separeras av punkter. Det första ordet är ett namn på klienten, vanligtvis identiskt med namnet på det kommando som startar klienten - som tex xclock. Man kan ta reda på klientnamnet för ett visst fönster genom att ge kommandot xprop och sedan klicka på fönstret. Då spottas en sida text ut. Texten ger information om fönstret, bla en rad i stil med
WM_CLASS(STRING) = "xclock", "XClock"
Detta betyder att man kan använda såväl xclock som XClock som namn för klienten. Många fönsterhanterare har en liknande funktion; om man tex väljer FvwmIdent i fönsterhanterarens meny och sedan klickar på något fönster, så får man information om detta fönster.

Det sista ordet i resursvariabeln, attributet, är namnet på en egenskap hos klienten (eller någon del av klienten). I vårt exempel förekommer attribut som height (höjden), update (tidsintervall i sekunder mellan successiva uppdateringar av klockan) och hands (visarnas färg).

Mellan första och sista ordet av namnet på en resursvariabel kan det förekomma ett eller flera ord, som anger vilken del av klienten som resursvariabeln syftar på. I stället för ''del'' säger man ofta ''objekt'' eller, som i bruksanvisningen ovan, ''widget''. I exemplet med xclock förekommer det enda objektet clock. Men i mer komplicerade fall, som vi diskuterar i avsnitt 4.5, kan det finnas flera objekt. Objekt kan också vara sammansatta av flera mindre delar, delobjekt, som i sin tur kan ha flera delar osv.

Resursvariabelns värde kan vara en textsträng, tex Black. Eller det kan vara ett tal, som 80. Det kan också vara ord som True (dvs sann) eller False (dvs falsk).

När man läser in en resursfil med xrdb -load, tas alla tidigare resursdefinitioner bort. Om man i stället använder xrdb -merge resursfil, så behålls alla gamla definitioner och de nya läggs till de gamla. Oftast ska man använda -merge i stället för -load.

Man får använda jokertecknet * i resursnamnen. Vanligtvis är tex

xclock*hands: Green
xclock*chime: False
att föredra framför att skriva ut resursvariablernas namn fullständigt.

Katalogen /usr/lib/X11/app-defaults innehåller resursfiler som ger grundkonfigurationer för ett antal olika X-klienter. Man kan få tips om hur klienten kan konfigureras genom att titta där. Om man vill ändra något värde som står där, så ska man klippa ut definitionen därifrån, klista in den i sin .Xresources och ändra värdet till det man önskar.

  
4.5 Klasser och objekt

Vi ska i detta avsnitt berätta slutet på sagan om X-resurser.

Ett bra exempel på en X-klient med mer sammansatta objekt är xfontsel. Hela dess fönster betraktas som ett objekt med namnet pane. Detta objekt är sammansatt av fem delobjekt, nämligen commandBox, fieldBox, fontName, viewPort och grip.

Det första delobjektet, commandBox, omfattar den översta raden av fönstret. Detta delobjekt har i sin tur tre delar. En del (med namnet quitButton) är rutan till vänster där det står ''quit''. En annan del (med namnet ownButton) är rutan där det står ''select''. Den tredje och sista delen (med namnet countLabel) är texten längst till höger, den som säger ''4 names match'' i bild 4.4.

Det andra delobjektet, fieldBox, omfattar fönstrets andra rad. Det har i sin tur femton delobjekt. Fjorton av delobjekten (med namnen field0, field1 osv) representerar de olika fälten i en fontsträng. Varje sådant fält föregås av ett streck (eller ett minustecken). Det femtonde delobjektet, med namnet dash, representerar dessa streck. Exempelvis gör resursdefinitionerna

xfontsel.pane.fieldBox.field0.background: Blue
xfontsel.pane.fieldBox.field0.foreground: White
xfontsel.pane.fieldBox.field0.label: varumärke
att fältet längst till vänster blir blått med texten ''varumärke'' i vitt. Prova dem gärna - dvs läs in dem med xrdb och starta sedan en ny xfontsel!

Tredje delobjektet av pane är fontName. Det omfattar tredje raden i xfontsel-fönstret, där den valda fontens namn finns utskrivet.

Det fjärde delobjektet, viewPort, omfattar den nedre halvan av fönstret. I detta fält finns en exempeltext som är skriven med den valda fonten.

Det femte delobjektet, grip, består av de små handtagen till höger i xfontsel-fönstret. Med deras hjälp kan man ändra storleken på de olika delarna av fönstret genom att man flyttar pekaren till någon av dem, håller in musens vänsterknapp och samtidigt flyttar musen uppåt eller nedåt.

Programmet editres kan rita diagram över objektstrukturen för X-klienter. I figur [*] visas en liten del av objektstrukturen för xfontsel. Några attribut för field0 finns också med där.

  
Figur: Del av objektstrukturen för xfontsel.
\begin{figure}
\small \psset{angleB=90, angleA=-90, levelsep=36pt, armB=14pt,
...
...Name}}
\Tr{\psshadowbox{\rule{0pt}{6pt}viewPort}}
}
\end{center}\end{figure}

Antag att vi vill ha vit text och blå bakgrund på alla de fjorton fält som är delobjekt i fieldBox. Det går visserligen att sätta värden för varje fält för sig, på samma sätt som vi gjorde med det första fältet, men det är otympligt. Det finns dock ett smidigare sätt. Varje objekt (och varje delobjekt) tillhör nämligen en klass. En klass innehåller ibland bara ett, ibland flera objekt. De fjorton fälten field0, field1 osv bildar tillsammans en klass med namnet MenuButton. Om vi skriver MenuButton i stället för field0, så gäller definitionen för alla de fjorton fälten. Prova alltså det här:

xfontsel.pane.fieldBox.MenuButton.background: Blue
xfontsel.pane.fieldBox.MenuButton.foreground: White

Klassnamn börjar alltid med stor bokstav, objektnamn börjar aldrig med stor bokstav. Därigenom blir det lätt att skilja mellan klasser och objekt. Detta förklarar den märkliga formen på objektnamn som fieldBox: det måste börja med liten bokstav eftersom det annars skulle se ut som ett klassnamn. När objektnamn är sammansatta av flera ord, tex ''field'' och ''box'', brukar alla orden utom det första skrivas med stor bokstav.

Men om vi nu vill att fälten ska ha blå bakgrund, med undantag för det sjunde fältet som ska ha röd bakgrund för att synas bättre? Det är enkelt, lägg bara till en rad som säger att field6 ska ha röd bakgrund. Om man anger värden både för klassen och för ett enskilt objekt, så gäller det värde man satte för objektet. Detta följer en allmän regel för hur resurser tolkas: om flera olika definitioner påverkar samma attribut, så prioriteras den resurssträng som är mest specifik.

Prova nu att läsa in resursdefinitionerna

xfontsel.pane.fieldBox.MenuButton.background: Blue
xfontsel.pane.fieldBox.MenuButton.foreground: White
xfontsel.pane.fieldBox.field6.background: Red
xfontsel.pane.fieldBox.field6.label: storlek
Följden blir att alla fält utom det sjätte får blå bakgrund och vit text. Det sjätte fältet får röd bakgrund samt texten ''storlek'' i vitt.

Också attributen tillhör klasser. Från utdraget av bruksanvisningen till xclock kan vi se att attributen foreground, hands och highlight alla tillhör klassen Foreground. Definitionen xclock.clock.Foreground: Blue gör att hela förgrunden i xclock, dvs tim- och minutstreck samt visarna, blir blå. Om vi dessutom gör definitionen xclock.clock.hands: Green, så blir det inre av visarna grönt medan tim- och minutstreck samt kanten på visarna förblir blått.

Antag nu att vi vill att hela bakgrunden i xfontsel ska vara blå. Ett sätt att uppnå detta är att ta alla resursvariabler med attribut background och ge dem värdet Blue. Så här blir det då:

xfontsel.pane.commandBox.background: Blue
xfontsel.pane.commandBox.countLabel.background: Blue
xfontsel.pane.commandBox.ownButton.background: Blue
xfontsel.pane.commandBox.quitButton.background: Blue
xfontsel.pane.fieldBox.background: Blue
xfontsel.pane.fieldBox.dash.background: Blue
xfontsel.pane.fieldBox.MenuButton.background: Blue
xfontsel.pane.fontName.background: Blue
xfontsel.pane.viewPort.sampleText.background: Blue
Detta är onödigt komplicerat. Återigen finns ett enklare sätt: man kan använda jokertecknet *. Då räcker det att göra så här:
xfontsel*background: Blue
Som synes i tabell 4.1, så har flaggan -bg Blue exakt samma effekt.

Den första av definitionerna

xfontsel*background: Blue
xfontsel.pane.fieldBox.field6.background: Red
säger att all bakgrund ska vara blå, den andra säger att bakgrunden i sjunde fältet i fontsträngen ska vara röd. Så vilken färg blir det i det sjunde fältet? Jo, den blir röd - helt i linje med principen att den mer specifika definitionen ska ha företräde.

Mycket ofta använder man jokertecknet helt enkelt för att slippa skriva ut objektnamnet - antingen för att man inte orkar kolla upp exakt vad det heter, eller för att det blir för långt att skriva. Så till exempel brukar man föredra

xfontsel*field0.label: varumärke
xfontsel*field6.label: storlek
framför att skriva ut resursvariablernas namn fullständigt. Det finns ytterligare ett skäl till detta: om det kommer en ny version av tex xclock, så kanske man lägger in ytterligare någon nivå i objekthierarkin - kanske nivån face mellan clock och attributet. I så fall ändras resursvariablernas namn, till xclock.clock.face.hands osv. Då fungerar fortfarande xclock*hands, och man slipper göra ändringar i sina resursfiler.

 

Vanligtvis är det så att alla egenskaper hos en X-klient som kan ställas in med hjälp av flaggor på kommandoraden, också kan ställas in med hjälp av resurser. Däremot kan man ofta göra saker med resurser som inte går att göra med flaggor.

Två av standardflaggorna från tabell 4.1 har med resurser att göra, nämligen -xrm och -name.

Flaggan -xrm läser helt enkelt in en resursdefinition. Ett exempel är$ xfontsel -xrm 'xfontsel*field6.label: storlek' &
$
Skälet till att man kan behöva använda flaggan -xrm är just att det kan finnas vissa egenskaper som kan ställas in med hjälp av resurser men inte med hjälp av flaggor. Genom att använda -xrm slipper man skriva in resursdefinitionen i en fil.

Flaggan -xrm används inte särskilt ofta. En annan standardflagga, -name, är desto intressantare. I stället för att orda så mycket om den ska vi ge ett exempel. Läs först in definitionerna

xclock.clock.analog: True
xclock.clock.hands: Green
xclock.clock.update: 1
digitalur.clock.analog: False
digitalur.clock.font: -*-courier-bold-r-*--18-*-75-75-m-*-iso8859-1
med hjälp av xrdb. De tre första definitionerna gäller programmet xclock medan de båda sista gäller en X-klient med namnet digitalur. Ge nu kommandona$ xclock &
$ xclock -name digitalur &
$
Vad händer? Jo, först får vi en analog klocka med gröna visare som uppdateras varje sekund. Det andra kommandot ger en digital klocka med den font vi angav i den femte raden ovan. Kommandot xclock -name digitalur & startade en xclock-process, men den fick namnet digitalur istället för xclock. Därigenom gällde de båda sista resursdefinitionerna (i stället för de tre första) för den processen.

Poängen med flaggan -name är att den kan användas som i exemplet ovan. Den behövs då man har två (eller fler) standardkonfigurationer för en viss X-klient, och vill ha än den ena, än den andra konfigurationen. Då ger man helt enkelt varje konfiguration ett namn, som tex digitalur, och tar med flaggan -name digitalur vid de tillfällen då man föredrar just den konfigurationen.

En defekt med digitalklockan är att den bara uppdateras en gång i minuten. Vi skulle vilja att den också uppdateras varje sekund. Raden digitalur.clock.update: 1 ordnar detta. Men det finns ett annat sätt, som vi nu ska beskriva. Vi minns att varje komponent i namnet på en resursvariabel tillhör någon klass. Detta gäller också för den första komponenten i resursvariabeln, dvs klientens namn. Varje X-klient har alltså ett klassnamn.

Varje xclock-process tillhör klassen XClock, oavsett om processen heter xclock, digitalur eller något annat. Detta fungerar precis som då attributen foreground, hands och highlight alla tillhör klassen Foreground.

Om vi läser in definitionerna

xclock*analog: True
xclock*hands: Green
XClock*update: 1
digitalur*analog: False
digitalur*font: -*-courier-bold-r-*--18-*-75-75-m-*-iso8859-1
med xrdb och sedan ger kommandot xclock -name digitalur &, så får vi alltså en digital klocka som uppdateras varje sekund.

Man kan ta reda på klassnamn och klientnamn för ett visst fönster genom att ge kommandot xprop och sedan klicka på fönstret. Då spottas en sida text ut. Texten ger information om fönstret, bla en rad i stil med

WM_CLASS(STRING) = "digitalur", "XClock"
Detta betyder att klientnamnet var digitalur och klassnamnet XClock.

4.6 Övningar

Övning. Ändra rotfönstrets färg till något som passar dig.

Övning. Starta en xclock med kommandot xclock -update 1 (utan något avslutande &-tecken). Tryck C-c efter en liten stund. Vad händer? Starta en ny xclock med samma kommando. Tryck C-z. Vad händer? Starta om klockan i bakgrunden.

Övning. Välj en ny pekare för rotfönstret.

Övning. Starta en xcalc av storleken 200x300 punkter en liten bit från bildskärmens nedre vänstra hörn.

Övning. Starta en xcalc med andra för- och bakgrundsfärger än svart och vitt.

Övning. Starta en xterm som använder fonten
-b&h-lucidatypewriter-medium-r-*-sans-18-*-100-100-*-*-iso8859-1.

Övning. Välj en font med hjälp av xfontsel. Starta en xterm med den fonten.

Övning. Starta en digital xclock som använder någon ''proportionell'' font (dvs där tecknen inte behöver vara lika breda).

Övning. Hur går det om man startar en xterm med en proportionell font?

Övning. Skapa en fil som definierar några resursvärden för xclock, läs in filen med xrdb och starta sedan en xclock.

Övning. Lägg in några resursvärden för xclock i din .Xresources, gå ur X och starta X igen. Starta sedan en xclock.

Övning. Sätt värdet av resursen Emacs.geometry till önskad storlek för Emacs-fönstret, tex 60x30. Starta sedan Emacs.

Övning. Ta reda på klientnamnet för Emacs med hjälp av xprop.

Övning. Logga in på en annan dator. Starta programmet xclock där - men se till att bilden hamnar på den skärm du sitter vid.

5. Hur gör man?

5.1 Att söka efter text

Med hjälp av kommandot grep kan man söka efter text i en fil. Syntaxen är grep mönster filnamn. Varje rad i filen filnamn som innehåller texten mönster skrivs ut.$ grep den bellman
när döden ropar: Granne, kom,
den skönsta nymf, som åt dig ler,
$
Kommandot ger ingen utskrift ifall mönstret inte påträffades:$ grep Linux bellman
$
Man får ange flera filer att söka i.$ grep ull bellman lenngren
bellman:från Bacchi buller och tumult,
bellman:     ditt timglas är nu fullt.
lenngren:hans runda armar hull och märg,
lenngren:och magen, kullrig som ett berg,
$
Om man inte anger något filnamn, så söker grep i stället efter mönstret i sin indata.$ cat bellman lenngren | grep ost
Vår prost jag häromdagen såg
sin frukost redan färdig fann
$
För de flesta ändamål klarar man sig med denna kunskap om grep. Många läsare kan alltså hoppa över resten av detta avsnitt, som ger en utförlig beskrivning av hur grep kan göra avancerade sökningar. Vi ska först beskriva några av de flaggor man kan ge grep. Sedan ska vi studera så kallade reguljära mönster som kan hjälpa oss att ge en mycket precis beskrivning av den text vi söker efter.

Om man anropar grep med flaggan -v, så skrivs de rader som inte gav träff ut.$ grep -v e bellman
Så lunka vi så småningom
     ditt timglas är nu fullt.
och du, du yngling, lyd min lag:
$
Tydligen finns det bara tre rader i filen bellman som saknar bokstaven ''e''.

Flaggan -i betyder att grep inte ska skilja mellan stora och små bokstäver:$ grep -i gra bellman
när döden ropar: Granne, kom,
Tycker du, att graven är för djup,
$
Flaggan -n får grep att numrera raderna:$ grep -n du bellman
6:och du, du yngling, lyd min lag:
9:Tycker du, att graven är för djup,
12:     så dör du nöjdare.
$
Flaggan -i, där i är ett heltal, innebär att grep också ska skriva ut de i rader som kommer före respektive efter den rad som matchar mönstret. $ grep -1 glas bellman
när döden ropar: Granne, kom,
     ditt timglas är nu fullt.
Du gubbe, fäll din krycka ner,
$
När man söker i flera filer, skriver grep i början av varje rad ut vilken fil raden kommer från. För att slippa detta ska man ge flaggan -h:$ grep -h ull bellman lenngren
från Bacchi buller och tumult,
     ditt timglas är nu fullt.
hans runda armar hull och märg,
och magen, kullrig som ett berg,
$
Om man enbart vill veta vilka filer som innehåller mönstret, så ska man ge flaggan -l.$ grep -l ull bellman lenngren
bellman
lenngren
$
Texten ''glas'' förekommer bara i filen bellman:$ grep -l glas bellman lenngren
bellman
$
Flaggan -L ger oss i stället de filer som inte innehåller mönstret:$ grep -L glas bellman lenngren
lenngren
$
För att få veta antalet träffar ska vi ge flaggan -c:$ grep -c ull bellman lenngren
bellman:2
lenngren:2
$
Flaggan -q undertrycker all utskrift från grep:$ grep -q vi bellman
lenngren
$

5.2 Att söka efter filer

Med hjälp av find kan man spåra upp filer som har vissa specificerade egenskaper, tex att deras namn ser ut på ett visst sätt, att de har en viss storlek, har ändrats inom en viss tidsperiod osv. Det enklaste sättet att använda find är

find katalog -name mönster
På detta kommando söker find i katalogen katalog, inklusive dess underkataloger, efter filer vars namn matchar mönster. Mönstret får innehålla jokertecken precis som i skalet. Eventuella jokertecknen ska dock inte tolkas av skalet utan av find, så man måste ofta skydda mönstret.

Så här söker vi i katalogen /usr efter filer vars namn innehåller ordet ''quake'':$ find /usr -name '*quake*'
/usr/doc/quake-lib
/usr/doc/xquake
/usr/games/xquake.real
/usr/games/xquake
/usr/lib/menu/xquake
/usr/share/gnome/apps/Games/Arcade/Xquake.desktop
/usr/share/games/quake
$
Om argumentet katalog är rotkatalogen /, så söker find bland samtliga filer i datorn. Om katalog utelämnas, så antar find att man syftar på den nuvarande katalogen.$ find -name 'bell*'
./bellman
./dikter/bellman
$

Genom att kombinera find och grep man söka efter filer som innehåller en viss text. Syntaxen för detta är

findkommando | xargs grepkommando
Filnamnen tas fram av find och skickas med hjälp av programmet xargs till grep, som i sin tur söker efter texten i filerna.$ find -name '*n' | xargs grep ost
./dikter/lenngren:Vår prost jag häromdagen såg
./dikter/lenngren:sin frukost redan färdig fann
./lenngren:Vår prost jag häromdagen såg
./lenngren:sin frukost redan färdig fann
$
Vad som hände ovan var att kommandot find -name '*n' fann de fyra filerna ./bellman, ./lenngren, ./dikter/bellman och ./dikter/lenngren. Därefter kördes kommandot grep ost på dessa filer.

  
5.3 Att använda disketter

Det korrekta sättet att använda disketter är att först montera dem. Hur det går till beskriver vi i avsnitt 7.4. På många system tillåts det inte att användarna på egen hand monterar disketter; i sådana fall tvingas de att använda något som kallas för Mtools. Mtools är en samling kommandon med vars hjälp man kan läsa från, och skriva till, disketter av ''DOS-format''. Detta är det ursprungliga formatet från IBM:s första PC-datorer. Det finns en mängd märkliga restriktioner i samband med DOS-formatet. Bland annat får filnamnen inte se ut hur som helst, och filskyddskoderna bevaras inte. Därför brukar man bara använda DOS-disketter om det finns tvingande skäl, som tex att man inte har rättighet att montera disketter eller att man måste flytta filer till eller från någon dator som inte kör Linux.

I de kommandon som ingår i Mtools kallas den första diskettenheten för a:, den andra för b: osv. Kommandot mdir listar filerna på disketten:$ mdir a:
 Volume in drive A has no label
 Volume Serial Number is 6E8A-0F0B
 Directory for A:/
 
gnulinux dvi     61948 03-01-1998  18:11  gnulinux.dvi
        1 file(s)            61 948 bytes
                          1 395 712 bytes free

$
Bara en fil, gnulinux.dvi finns på disketten. Med hjälp av mcopy kopierar vi filen bellman till disketten:$ mcopy bellman a:
Copying bellman
$ mdir a:
 Volume in drive A has no label
 Volume Serial Number is 6E8A-0F0B
 Directory for A:/
 
gnulinux dvi     61948 03-01-1998  18:11  gnulinux.dvi
bellman            372 03-01-1998  18:11  bellman
        2 file(s)            62 320 bytes
                          1 395 200 bytes free

$
Vi kan också kopiera filer från disketten till datorn med mcopy. Om man inte anger någon destinationskatalog, så placeras kopian i den nuvarande katalogen.$ mcopy a:gnulinux.dvi
Copying gnulinux.dvi
$ ls
bellman       dikter/       gnulinux.dvi  lenngren
$

Kommandot mdel raderar filer på disketten:$ mdel a:gnulinux.dvi
Removing gnulinux.dvi
$ mdir a:
 Volume in drive A has no label
 Volume Serial Number is 6E8A-0F0B
 Directory for A:/
 
bellman            372 03-01-1998  18:11  bellman
        1 file(s)               372 bytes
                          1 457 152 bytes free

$

Man får använda jokertecken:$ mdel a:*
Removing bellman
$ mdir a:
 Volume in drive A has no label
 Volume Serial Number is 6E8A-0F0B
 Directory for A:/
 
File "*" not found
                         1 457 664 bytes free

$
Man bör skydda jokertecknen som gäller filer på disketten från skalet, för det är Mtools som ska tolka dem. Anledningen till att jag struntade i att sätta apostrofer kring a:* ovan är att jag visste att det inte fanns någon fil i den nuvarande katalogen vars namn började med tecknen a:.

Kommandot mlabel kan sätta ett slags namn på disketten:$ mlabel a:dikter
$ mdir
 Volume in drive A is DIKTER
 Volume Serial Number is 6E8A-0F0B
 Directory for A:/
 
File "*" not found
1 457 664 bytes free

$

Disketter har högst begränsad kapacitet. Om man vill spara en mycket stor fil på diskett, så måste filen delas upp i bitar som ryms på var sin diskett. Vi ska nu visa hur det går till. (Ett annat sätt är att använda tar med flaggan -M, se avsnitt 5.4.)

Antag att det finns en stor fil med namnet gnulinux.ps i katalogen /tmp:$ ls -l /tmp/gnulinux.ps
-rw-r--r--   1 göran    göran     2548527 mar  1 11:01 /tmp/gnulinux.ps
$
Vi styckar upp filen i småbitar med kommandot split. Småbitarna sparas i filer med namnen xaa, xab, xac osv i den nuvarande katalogen.$ split -b 1400k /tmp/gnulinux.ps
$ ls -l
total 2567
-rw-------   1 göran    göran         372 feb  1 16:48 bellman
drwxrwxr-x   3 göran    göran        1024 feb 20 13:19 dikter/
-rw-rw-r--   1 göran    göran       61948 mar  1 18:12 gnulinux.dvi
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
-rw-rw-r--   1 göran    göran     1433600 mar  1 19:50 xaa
-rw-rw-r--   1 göran    göran     1114927 mar  1 19:50 xab
$
Flaggan -b betyder att nästa argument anger den maximala storleken för småbitarna. Vi valde storleken 1400 kilobyte. Bokstaven ''k'' efter siffran 1400 i instruktionen ovan står för kilobyte. I stället för ''k'' kan man ange ''b'', som står för block (ett block är en halv kilobyte) eller ''m'', som står för megabyte. Om bokstaven utelämnas, så antas siffran ange antal byte.

Filerna xaa och xab kan nu kopieras till diskett. När de kopierats över till en dator igen kan de sättas samman med hjälp av cat på följande sätt:$ cat xa? > gnulinux.ps
$ ls -l gnulinux.ps
-rw-rw-r--   1 göran    göran     2548527 mar  1 20:12 gnulinux.ps
$ rm gnu* xa?
$

På UNIX-system motsvaras varje hårdvaruenhet av en fil i katalogen /dev. Den första diskettstationen motsvaras av filen /dev/fd0, den andra av /dev/fd1 osv.$ ls -l /dev/fd0
brw-rw----   1 root     floppy     2,   0 maj 28 02:49 fd0
$
Som synes av filrättigheterna måste man be systemadministratören om att få bli medlem av gruppen floppy innan man kan använda diskettenheten.

Disketter måste formatteras innan de kan användas. Formatteringen sker i två steg. Det första steget, lågnivåformatteringen, består i att det lagringsutrymme som finns på disketten struktureras och delas in i spår och sektorer. Det vanliga är att disketter formatteras med 18 spår à 80 sektorer på vardera sidan. Sammanlagt blir det 2880 sektorer. En sektor innehåller 512 byte (en halv kilobyte), så lagringsutrymmet blir det välkända 1440K. (Man kan genom att använda fler spår, fler sektorer på varje spår och större sektorer få in mer på en vanlig diskett, upp till 1992K, men detta rekommenderas inte - bland annat eftersom det oftare blir fel vid avläsningen då.) Kommandot superformat /dev/fd0 formatterar en diskett i första diskettstationen. Om någon sektor är dålig, så får man veta detta; i så fall bör disketten kasseras. Lågnivåformatteringen tar ganska lång tid, men den behöver bara göras en gång för varje diskett.

Efter lågnivåformatteringen måste man skapa ett filsystem på disketten innan den kan användas; detta är det andra steget i formatteringsprocessen. (Om man använder tar eller dd för att läsa från eller skriva till disketten behövs dock inget filsystem.) Vanligtvis använder man endera av minix eller ext2 som filsystem. Ett mindre bra alternativ är filsystemet msdos eller vfat, som används för DOS-disketter.

Så här gör man för att formattera disketten:$ superformat /dev/fd0
Verifying track 79, head 1
mformat -s18 -t80 -h2 -S2 -M512 a:
$
Kommandot superformat lägger automatiskt ett filsystem av typen msdos på disketten. Så här kan vi skapa ett filsystem av typen ext2:$ /sbin/mkfs -t ext2 /dev/fd0
mke2fs 1.10, 24-Apr-97 for EXT2 FS 0.5b, 95/08/09
Linux ext2 filesystem format
Filesystem label=
360 inodes, 1440 blocks
72 blocks (5.00%) reserved for the super user
First data block=1
Block size=1024 (log=0)
Fragment size=1024 (log=0)
1 block group
8192 blocks per group, 8192 fragments per group
360 inodes per group

Writing inode tables: done
Writing superblocks and filesystem accounting information: done
$
Nu är disketten klar att användas. För att UNIX ska kunna skriva till eller läsa från en diskett med de vanliga kommandona cp, ls osv, måste den dock först monteras, se avsnitt 7.4.

Om vi vill byta till ett minix-filsystem, så gör vi så här:$ /sbin/mkfs -t minix /dev/fd0
480 inodes
1440 blocks
Firstdatazone=19 (19)
Zonesize=1024
Maxsize=268966912
$
Observera att det inte är nödvändigt att lågnivåformattera disketten igen!

För att byta till ett msdos-filsystem är det enklast att använda mformat:$ mformat a:
$

  
5.4 Att arkivera filer

Med hjälp av programmet tar kan man slå samman flera filer eller tom katalogstrukturer till en enda fil, en så kallad arkivfil. Poängen med detta är framförallt att det blir lättare att flytta filerna någon annanstans, till exempel via nätet. Ett annat skäl att använda tar kan vara att man vill lagra filerna som en säkerhetskopia. Faktum är att ''tar'' kommer från tape archive, bandarkiv, eftersom det ursprungligen användes för säkerhetskopiering av filer till magnetband.

Genom att ge argumentet -cvf dikter.tar följt av namnen på de filer som ska ingå i arkivet, skapar man ett arkiv med namnet dikter.tar.$ ls
bellman   dikter/   lenngren
$ tar -cvf dikter.tar bellman lenngren
bellman
lenngren
$ ls
bellman     dikter/     dikter.tar  lenngren
$
Flaggan -c står för ''skapa arkiv'' och -v för ''skriv ut information''. Argumentet -f namn talar om att arkivfilen ska heta namn. Vanligtvis brukar man ge arkivfiler namn med ändelsen .tar, men detta är inget krav.

För att titta i en arkivfil använder man flaggan -t i stället för -c.$ tar -tvf dikter.tar
-rw------- göran/göran     372 1998-02-01 16:48 bellman
-rw-rw-r-- göran/göran     649 1998-02-01 15:53 lenngren
$
Så nu kan vi skicka iväg filen dikter.tar och sedan packa upp den för att få en kopia av de båda filerna bellman och lenngren. Man packar upp ett arkiv genom att använda flaggan -x i stället för -c. Låt oss demonstera det hela genom att radera de ursprungliga filerna för att sedan återställa dem från arkivfilen:$ rm bellman lenngren
$ tar -xvf dikter.tar
bellman
lenngren
$ ls -l
total 13
-rw-------   1 göran    göran         372 feb  1 16:48 bellman
drwxrwxr-x   3 göran    göran        1024 feb 20 13:19 dikter/
-rw-rw-r--   1 göran    göran       10240 mar  1 22:47 dikter.tar
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
$

Ofta komprimerar man arkivfilerna med gzip för att de inte ska ta så mycket utrymme. Detta steg kan tar göra automatiskt om man anger flaggan -z. Komprimerade arkiv brukar ha namn med ändelsen .tar.gz eller .tgz. Låt oss göra ett komprimerat arkiv av hela katalogen dikter:$ tar -zcvf dikter.tgz dikter
dikter/
dikter/bellman
dikter/lenngren
$
Filen dikter.tgz är nu ett komprimerat arkiv som innehåller katalogen dikter och alla dess filer. Låt oss kontrollera att detta är korrekt:$ tar -ztvf dikter.tgz
drwxrwxr-x göran/göran       0 1998-02-27 19:43 dikter/
-rw------- göran/göran     372 1998-02-01 17:12 dikter/bellman
-rw-rw-r-- göran/göran     649 1998-02-01 17:12 dikter/lenngren
$
Källkoden till stora program består ofta av åtskilliga filer, och distribueras därför vanligtvis i form av komprimerade arkiv.

 Om man vill spara arkivfilen på diskett, så ska man ange tex /dev/fd0 som arkivfil (se avsnitt 5.3). Allt som tidigare var lagrat på disketten försvinner i så fall. Kommandot tar -Mcvf /dev/fd0 ~ gör en säkerhetskopia av hela den egna hemkatalogen. Eventuellt kan det behövas mer än en diskett, men tack vare flaggan -M tillåter tar att arkivet består av flera ''volymer''. De enskilda volymerna kan vara disketter eller magnetband. När en volym är full stannar tar upp och uppmanar användaren att sätta i en ny.$ tar -Mcvf /dev/fd0 /tmp
tar: Tar bort inledande "/" från absoluta sökvägar i arkivet
tmp/
tmp/gnulinux.ps
Gör iordning volym nummer 2 för /dev/fd0 och tryck vagnretur:
$
För att packa upp arkivet ska man sätta första volymen i diskettenheten och därefter ge kommandot tar -Mxvf /dev/fd0. Meddelanden skrivs ut av tar när det är dags att byta volym.

   
5.5 Processkontroll

I avsnitt 3.2 beskrev vi det gränssnitt till jobbkontroll som Bash erbjuder. I detta avsnitt ska vi ta upp fler sätt att kontrollera och manipulera processer.

Med hjälp av kommandot ps kan man kontrollera vilka processer som är igång på datorn.$ ps
  PID TTY STAT TIME COMMAND
 5596  p0 S    0:00 -bash 
 5599  p2 S    0:01 /usr/bin/xdvi.bin -name xdvi gnulinux.dvi
 5639  p2 S    0:00 gs -sDEVICE=x11 -dNOPAUSE -dSAFER -q - 
 5668  p0 R    0:00 ps 
$
Status för en process (rubriken STAT ovan) kan vara R (Running; igång), S (Sleeping; sysslolös) och T (Stopped; stoppad, eller snarare pausad). Instruktionen som startade respektive process anges i sista fältet av varje rad ovan. Det näst sista fältet visar hur mycket av datorns tid instruktionen tagit i anspråk.

Varje process har ett nummer, dess processidentifikationsnummer (PID), med vars hjälp man referar till processen. De flesta processer har en kontrollterminal, nämligen den terminal processen startades från. Eventuell indata till kommandot läses in från kontrollterminalen, och utdata skrivs ut där. Det andra fältet (under rubriken TTY) ovan anger processens kontrollterminal.

Varje terminal motsvaras av en fil i katalogen /dev. De virtuella terminalerna, som man kommer till genom samtidigt att trycka Ctrl, Alt och en av funktionstangenterna F1 till F6, har filnamn tty1 till tty6, medan pseudoterminaler har filnamn ttyp1, ttyp2 osv. Kommandot ps från sista raden ovan körs i pseudoterminalen /dev/ttyp0.

Flaggorna till ps anges på ett något annorlunda sätt än vanligt (detta kommer att ändras i en kommande utgåva av programmet). Om man ger flaggan u, så skrivs utförligare information ut.$ ps u
USER    PID %CPU %MEM  SIZE   RSS TTY STAT START  TIME COMMAND
göran  5596  0.0  1.9  1832  1228  p0 S    08:32  0:00 -bash 
göran  5599  0.0  3.4  3160  2184  p2 S    08:33  0:01 /usr/bin/xdvi.bi
göran  5639  0.0  3.9  4540  2528  p2 S    10:07  0:00 gs -sDEVICE=x11 
göran  5670  0.0  0.8   908   540  p0 R    10:39  0:00 ps u 
$
Här ser vi också vem som startade processen (USER), när den startades (START), hur många procent av datorns tid (%CPU) respektive minne (%MEM) processen förbrukar, hur stor processen är (SIZE, i kilobyte) samt hur mycket av den som finns i minnet just nu (RSS). Bara de processer man själv startat listas ovan. Använd flaggan a om också alla andras processer ska tas med. De processer som inte har någon kontrollterminal finns inte heller med ovan, utan man måste ge ps flaggan x för att få information om dem.$ ps x
  PID TTY STAT TIME COMMAND
 5580  ?  S    0:01 /usr/X11R6/bin/fvwm2 
 5592  ?  S    0:00 /usr/X11R6/lib/X11/fvwm2/FvwmPager 9
 5594  ?  S    0:00 /usr/X11R6/lib/X11/fvwm2/FvwmButtons
 5595  ?  S    0:00 xclock -update 1 -padding 4 -bg Dark
 5596  p0 S    0:00 -bash 
 5598  ?  S    0:58 /usr/bin/xemacs 
 5599  p2 S    0:01 /usr/bin/xdvi.bin -name xdvi gnulinu
 5639  p2 S    0:00 gs -sDEVICE=x11 -dNOPAUSE -dSAFER -q
 5672  p0 R    0:00 ps x
$
De processer som saknar kontrollterminal har ett frågetecken under rubriken TTY. I utskriften ovan finns fem sådana processer; de utgörs av fönsterhanteraren och program som startats genom den.

Kommandot top ger ungefär samma information som ps. Men top sorterar processerna efter hur mycket processortid de använder, och informationen uppdateras med jämna intervall tills man trycker q. Man skulle kunna säga att top är en ''live-version'' av ps. Det finns åtskilliga X-program som ger motsvarande information, tex Gtop, se figur 5.1.

Här är ett exempel på utdata från programmet top:

 10:53am  up 16 days, 22:07,  3 users,  load average: 0.00, 0.00, 0.10
41 processes: 40 sleeping, 1 running, 0 zombie, 0 stopped
CPU states:  3.7% user,  0.9% system,  0.0% nice, 95.4% idle
Mem:   63356K av,  61184K used,   2172K free,  26664K shrd,   9376K buff
Swap:  96352K av,    316K used,  96036K free                 23856K cached

  PID USER     PRI  NI  SIZE  RSS SHARE STAT  LIB %CPU %MEM   TIME COMMAND
5675 göran     19   0   772  772   592 R       0  3.7  1.2   0:00 top
15350 root       8   0  5268 5260  1596 S       0  0.9  8.3  61:42 XF86_Mach64
    1 root       0   0   232  216   160 S       0  0.0  0.3   0:01 init
    2 root       0   0     0    0     0 SW      0  0.0  0.0   0:00 kflushd
    3 root     -12 -12     0    0     0 SW<     0  0.0  0.0   0:00 kswapd
    4 root       0   0     0    0     0 SW      0  0.0  0.0   0:00 nfsiod
    5 root       0   0     0    0     0 SW      0  0.0  0.0   0:00 nfsiod
    6 root       0   0     0    0     0 SW      0  0.0  0.0   0:00 nfsiod
    7 root       0   0     0    0     0 SW      0  0.0  0.0   0:00 nfsiod
15326 root       0   0   972  972   656 S       0  0.0  1.5   0:00 bash
15324 root       0   0   312  312   232 S       0  0.0  0.4   0:00 getty
  119 root       0   0   580  548   452 S       0  0.0  0.8   0:05 sshd
   14 root       0   0    92   64    52 S       0  0.0  0.1   0:07 update
   85 root       0   0   372  364   272 S       0  0.0  0.5   0:00 syslogd
   87 root       0   0   360  352   180 S       0  0.0  0.5   0:00 klogd
   96 root       0   0   204  196   148 S       0  0.0  0.3   0:00 kerneld
  100 daemon     0   0   344  340   268 S       0  0.0  0.5   0:00 portmap
  102 root       0   0   376  368   300 S       0  0.0  0.5   0:00 inetd
  152 root       0   0   304  284   224 S       0  0.0  0.4   0:00 getty
  126 root       0   0   760  748   356 S       0  0.0  1.1  22:00 rpc.nfsd
  128 root       0   0   568  556   444 S       0  0.0  0.8   0:00 rpc.mountd
  132 nobody     0   0   272  264   204 S       0  0.0  0.4   0:00 dhttpd
  135 daemon     0   0   308  276   220 S       0  0.0  0.4   0:00 atd
  138 root       0   0   348  340   256 S       0  0.0  0.5   0:00 cron
30196 root       0   0  1544 1544  1204 S       0  0.0  2.4   0:00 xdm
  153 root       0   0   296  296   216 S       0  0.0  0.4   0:00 getty
15348 root       0   0   684  672   572 S       0  0.0  1.0   0:00 xdm
 5580 göran      1   0  1284 1284   896 S       0  0.0  2.0   0:01 fvwm2
 5590 root       0   0  2104 2104  1448 S       0  0.0  3.3   0:01 xterm
30205 root       0   0  1436 1436  1196 S       0  0.0  2.2   0:00 xconsole.real
 5592 göran      0   0   720  720   620 S       0  0.0  1.1   0:00 FvwmPager
 5595 göran      0   0  1260 1260  1052 S       0  0.0  1.9   0:00 xclock
 5594 göran      0   0   808  808   696 S       0  0.0  1.2   0:00 FvwmButtons
 5596 göran     15   0  1228 1228   884 S       0  0.0  1.9   0:00 bash
30150 root       0   0  1456 1436   700 S       0  0.0  2.2   2:29 sshd
 5599 göran      0   0  2184 2184  1328 S       0  0.0  3.4   0:01 xdvi.bin.real
 5598 göran      0   0  7464 7464  3056 S       0  0.0 11.7   1:01 xemacs
 5639 göran      0   0  2528 2528  1736 S       0  0.0  3.9   0:00 gs
30152 eva        0   0  1176 1176   860 S       0  0.0  1.8   0:00 bash
30154 eva        0   0  9544 9540  1876 S       0  0.0 15.0   0:48 gimp
30155 eva        0   0  1996 1980   708 S       0  0.0  3.1   0:00 script-fu

  gtop.gif

Man kan ''kommunicera'' med en process genom att skicka signaler till den med hjälp av kommandot kill. Ofta används kill till att avsluta processer, men kommandot kan också ha andra, mindre destruktiva effekter - tex att processen tar en paus eller att den startar om. Bara root och ägaren av en process får skicka signaler till den.

Signaler har såväl namn som nummer. Man kan referera till en signal antingen med dess namn, som tex SIGCONT (eller kortare CONT), eller dess nummer, som 18. Några viktiga signaler ges i tabell 5.2.

Processer kan förbereda sig för att ta emot olika signaler, och ha en åtgärd redo ifall en viss signal skulle komma. Man säger då att processen fångar signalen. Åtgärden kan vara att helt enkelt strunta i signalen, eller tex att spara alla data, skriva ett felmeddelande och avsluta. Om signalen inte fångas, så vidtas en standardåtgärd på processen. Två av signalerna, SIGKILL och SIGSTOP, kan aldrig fångas.


 
Tabell: Några signalnamn
 
Signal Nummer Betydelse Standardåtgärd
SIGHUP 1 Avringd avsluta
SIGINT 2 Avbruten avsluta
SIGSTOP 19 Stoppad stanna
SIGCONT 18 Återupptagen  
SIGKILL 9 Dödad avsluta
SIGTERM 15 Avslutad avsluta
SIGSEGV 11 Segmenteringsfel avsluta

Vi minns att man kan pausa ett förgrundsjobb i Bash genom att trycka C-z. Mer allmänt kan man pausa en process genom att skicka signalen SIGSTOP till den. För att låta processen fortsätta kan man vid ett senare tillfälle skicka signalen SIGCONT till den.$ gzip -9 /tmp/gnulinux.ps &
[1] 5682
$ ps 5682
  PID TTY STAT TIME COMMAND
 5682  p0 R    0:04 gzip -9 /tmp/gnulinux.ps
$ kill -STOP 5682
$ ps 5682
  PID TTY STAT TIME COMMAND
 5682  p0 T    0:14 gzip -9 /tmp/gnulinux.ps

[1]+  Stopped                 gzip -9 /tmp/gnulinux.ps
$ kill -CONT 5682
$ ps 5682
  PID TTY STAT TIME COMMAND
 5682  p0 R    0:21 gzip -9 /tmp/gnulinux.ps
$ ls /tmp
gnulinux.ps.gz
[1]+  Done                    gzip -9 /tmp/gnulinux.ps
$
Ovan körs först en tung process igång. Med hjälp av ps ser vi att processen kör för fullt. Kommandot kill -STOP 5682 skickar signalen SIGSTOP till processen, och en kontroll visar att den faktiskt är pausad (dess status är T). Efter en stund skickas signalen SIGCONT till processen, och då fortsätter den därifrån den stannade. Dess status är återigen R. Senare, efter kommandot ls /tmp, ser vi att processen kört färdigt.

Kommandot kill vill som flagga ha namn eller nummer på en signal att skicka. Om flaggan utelämnas, så skickas signalen SIGTERM. Argumenten består av ett eller flera processidentifikationsnummer, som anger vilken eller vilka processer signalen ska skickas till. Om kill anropas med flaggan -l, så ges en lista över alla signaler.$ kill -l
 1) SIGHUP      2) SIGINT      3) SIGQUIT     4) SIGILL
 5) SIGTRAP     6) SIGABRT     7) SIGBUS      8) SIGFPE
 9) SIGKILL    10) SIGUSR1    11) SIGSEGV    12) SIGUSR2
13) SIGPIPE    14) SIGALRM    15) SIGTERM    17) SIGCHLD
18) SIGCONT    19) SIGSTOP    20) SIGTSTP    21) SIGTTIN
22) SIGTTOU    23) SIGURG     24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM  27) SIGPROF    28) SIGWINCH   29) SIGIO
30) SIGPWR
$

Signalen SIGTERM är en önskan om att processen ska avsluta. Vissa processer fångar denna signal och struntar i den. Andra processer fångar signalen, sparar sina data och städar upp efter sig innan de avslutar. Slutligen finns det processer som inte fångar signalen SIGTERM, och då vidtas standardåtgärden som är att avsluta dem. Om man vill ta bort en process som inte ger med sig på något annat sätt, så kan man skicka SIGKILL till den. Då avslutas processen utan att den först ''tillfrågas''. Detta kan ha obehagliga effekter - om man tex skickar SIGKILL till Emacs, så förlorar man alla ändringar som inte är sparade. Det är alltså bäst att prova SIGTERM först; bara om detta inte hjälper ska man använda SIGKILL.

Antag att vi har följande processer igång:$ ps
  PID TTY STAT   TIME COMMAND
 1485  p1 S      0:00 -bash
 1491  p2 S      0:00 -bash
 1502  p3 S      0:00 xdvi.bin -name xdvi oh.dvi
 1549  p2 R     54:05 dedekind 1232112423
 1598  p1 R      0:00 ps
$
Vi försöker nu ta bort process 1491, ett Bash-skal:$ kill 1491
$ ps 1491
  PID TTY STAT   TIME COMMAND
 1491  p2 S      0:00 -bash
$
Kontrollen med ps visar att Bash-skalet finns kvar. Detta beror på att Bash fångar signalen SIGTERM och struntar i den. Vi måste då ta till SIGKILL:$ kill -9 1491
$ ps 1491
  PID TTY STAT   TIME COMMAND
No processes available.
$
Utskriften från ps visar att processen är borttagen. Process 1549 (som körde under det skal vi nyss tog bort och som nu saknar kontrollterminal) kan vi däremot ta bort med SIGTERM:$ ps 1549
  PID TTY STAT   TIME COMMAND
 1549  ?  R     62:33 dedekind 1232112423
$ kill 1549
$ ps 1549
  PID TTY STAT   TIME COMMAND
No processes available.
$

När man i skalet trycker C-c skickas signalen SIGINT till de processer som är i förgrunden. Detta får vanligtvis processerna att avsluta.

Signalen SIGHUP betyder att processen är ''avringd'', vilket tex kan innebära att man loggat ut. Vissa processer struntar i denna signal, andra avlutar och åter andra läser in sina konfigurationsfiler och startar om. Om man vill att en process ska ligga kvar även efter att man loggat ut, så måste man i vissa fall skydda den mot signalen SIGHUP. Detta gör man genom att starta den med nohup kommando &

Om ett program ''utför en förbjuden åtgärd'', så skickas signalen SIGSEGV till den. Nästan alltid får detta till följd att programmet avslutar och gör en minnesutskrift i en fil med namnet core. Minnesutskriften är till för att programutvecklare ska kunna ta reda på vad som vållade felet.

Kommandot killall är en variant av kill. Skillnaden mellan dessa kommandon ligger i hur man anger vilka processer signalen ska skickas till. I fallet kill anges processernas PID, men till killall anges i stället det kommando som startade processerna. En fördel med killall är att vi slipper ta reda på PID för processerna ifråga.$ xfontsel &
[1] 5874
$ killall xfontsel
[1]+  Avslutad                xfontsel
$

Om en process startar en ny process, så är den nya processen barn till den gamla, och den gamla processen är förälder till den nya. Förälderns PID kallas för processens PPID. Kommandot pstree ritar ut processernas släktträd med utgångspunkt från urmodern init (som har PID 1) eller, om man vill, från en given process.$ ps x
  PID TTY STAT TIME COMMAND
 5580  ?  S    0:04 /usr/X11R6/bin/fvwm2 
 5592  ?  S    0:00 /usr/X11R6/lib/X11/fvwm2/FvwmPager 9
 5594  ?  S    0:00 /usr/X11R6/lib/X11/fvwm2/FvwmButtons
 5596  p0 S    0:00 -bash 
 5598  ?  S    1:48 /usr/bin/xemacs 
 5599  p2 S    0:02 /usr/bin/xdvi.bin -name xdvi gnulinu
 5639  p2 S    0:00 gs -sDEVICE=x11 -dNOPAUSE -dSAFER -q
 5868  ?  S    0:00 xclock -update 1 -padding 4 -bg Dark
 5876  p0 R    0:00 ps x
$ pstree 5580
fvwm2-+-FvwmButtons
      |-FvwmPager
      |-xclock
      `-xterm---bash---pstree
$

Varje process har ett snällhetsvärde som bestämmer hur stor del av processorns tid processen får. Värdet är ett heltal mellan -20 och 19. Ju högre värdet är, desto snällare är processen i den meningen att den kräver mindre av processorkraften. Vanligtvis ärver en process snällhetsvärdet från sin förälder; standardvärdet är 0. Det är bara root som får sänka snällhetsvärdet för en process. Däremot kan vanliga användare höja det för sina processer med kommandot nice. Om en process startas med nice kommando, så ges den ett snällhetsvärde som är 10 enheter större än dess förälder (men högst 19). Om den ska ha tex värdet 5 högre än föräldern, ska den startas med instruktionen nice -n 5 kommando. Om man ger denna instruktion i ett skal med snällhetsvärde 10, så körs alltså kommando med snällhetsvärde 15. Snällhetsvärdet skrivs ut under rubriken ''NI'' i top, se tabell 5.1.

Om en process redan är igång, så kan man ändra dess snällhetsvärde med kommandot renice. Nedan startar vi en process och sätter sedan dess snällhetsvärde till 3.$ xfontsel &
[1] 5935
$ renice +3 5935
5935: old priority 0, new priority 3
$
Vi ändrar snällhetsvärdet till 7:$ renice +7 5935
5935: old priority 3, new priority 7
$
Man får inte sänka värdet, utan bara höja det.$ renice +5 5935
renice: 5935: setpriority: Åtkomst nekas
$

  
6. Skalet Bash

  
6.1 Variabler

Man kan använda variabler i Bash. En variabel är ett namn som har getts ett visst värde. Variabelnamnet får bara innehålla bokstäverna a till z samt siffror och understrykningstecken, och det får inte börja med en siffra. Värdet är helt enkelt en följd av tecken; det finns inga datatyper som tex heltal eller flyttal.

Kommandot namn=värde ger variabeln namn värdet värde. (Det får inte finnas något mellanrum före eller efter likhetstecknet.) Om texten $namn sedan förekommer i ett kommando, så skriver Bash om kommandot genom att byta ut $namn mot värde. För att tilldela variabeln ADRESS värdet goran_a@sslug.dk ska man alltså ge kommandot$ ADRESS=goran_a@sslug.dk
$
Hädanefter vet Bash att ADRESS har värdet goran_a@sslug.dk. Låt oss prova att använda variabeln!$ echo $ADRESS
goran_a@sslug.dk
$
Kommandot echo $ADRESS skrevs här om till echo goran_a@sslug.dk innan det exekverades, se figur 6.1.

  
Figur 6.1: Bash skriver om ett kommando.
\begin{figure}
\begin{center}
\psframebox[framesep=1.1]{
\begin{psmatrix}
\p...
...ows=->]{1,1}{2,1}\tlput{Omskrivning}
\end{psmatrix} }
\end{center}\end{figure}

Jämför med figur 3.1.

Följande kommando skickar innehållet i filen bellman som datorpost till adressen goran_a@sslug.dk:$ mail $ADRESS < bellman
$

Man måste komma ihåg dollartecknet för att Bash ska sätta in variabelns värde:$ echo ADRESS
ADRESS
$
Variabler kan användas inom citationstecken:$ echo "Skicka svaret till Göran <$ADRESS>"
Skicka svaret till Göran <goran_a@sslug.dk>
$
Detta visar att citationstecken inte skyddar mot omskrivning av $namn.

Om värde innehåller blank- eller nyrad-tecken, så måste man beskydda det så att Bash ser det som ett enda ord.$ NAMN=Göran Andersson
bash: Andersson: command not found
$ NAMN='Göran Andersson'
$ echo $NAMN
Göran Andersson
$
I nästa exempel låter vi variablens värde vara det kommando vi ska ge:$ KOMMANDO=date
$ $KOMMANDO
tis feb 17 11:05:48 CET 1998
$
Man kan också lagra flaggor i en variabel:$ FLAGGOR=-l
$ echo Variabeln FLAGGOR har värdet $FLAGGOR
Variabeln FLAGGOR har värdet -l
$ ls $FLAGGOR
total 3
-rw-------   1 göran    göran         372 feb  1 16:48 bellman
drwxrwxr-x   2 göran    göran        1024 feb  1 17:12 dikter/
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
$
Härnäst ska vi ge variabeln NYA_FLAGGOR samma värde som FLAGGOR men med tillägg av -t. (Om man ger ls flaggan -t, så sorteras filerna efter senaste ändringsdatum; de nyaste filerna visas först.)$ NYA_FLAGGOR=$FLAGGOR -t
bash: -t: command not found
$
Det blev fel eftersom vi glömde skydda blanktecknet före -t. Vi gör ett nytt försök, och utnyttjar då att citationstecken skyddar blanktecken men inte dollartecken:$ NYA_FLAGGOR="$FLAGGOR -t"
$ echo $NYA_FLAGGOR
-l -t
$ ls $NYA_FLAGGOR
total 3
drwxrwxr-x   2 göran    göran        1024 feb  1 17:12 dikter/
-rw-------   1 göran    göran         372 feb  1 16:48 bellman
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
$
Nu ska vi ändra värdet av variabeln FLAGGOR genom att till dess nuvarande värde lägga tecknen rt. (Flaggan -r uppmanar ls att lista filerna i omvänd ordning.) Men det finns ett litet problem: vi kan inte skriva FLAGGOR=$FLAGGORrt, för då skulle FLAGGORrt tolkas som ett variabelnamn. Men Bash tillåter att man skriver ${namn} i stället för $namn, så vi kan göra så här:$ FLAGGOR=${FLAGGOR}rt
$ echo $FLAGGOR
-lrt
$ KOMMANDO=ls
$ $KOMMANDO $FLAGGOR
total 3
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
-rw-------   1 göran    göran         372 feb  1 16:48 bellman
drwxrwxr-x   2 göran    göran        1024 feb  1 17:12 dikter/
$

Det finns ett antal variabler som är av en speciell betydelse för Bash. En sådan variabel är PS1, vars värde bestämmer hur prompten ser ut.$ echo $PS1
\$
$ PS1='vad nu? '
vad nu? ls
bellman   dikter/   lenngren
vad nu?
I värdet av PS1 kan man ange ett antal specialtecken, se tabell 6.1. Bla skrivs \h om till datorns namn och \u till det egna användarnamnet.vad nu? PS1='[\u@\h \w]\$ '
[göran@fafner ~]$ cd dikter
[göran@fafner ~/dikter]$ cd /usr/doc
[göran@fafner /usr/doc]$ cd
[göran@fafner ~]$ PS1='\$ '
$
I prompten ovan står tecknet ~ för min hemkatalog.

 
Tabell: Specialtecken som kan användas i prompten.
 
Tecken Skrivs om till...
\h Datorns namn (fram till första punkten)
\H Datorns namn
\u Det egna användarnamnet
\w Sökvägen till den nuvarande katalogen
\W Namnet på den nuvarande katalogen
\$ # om man är root, annars $.
\t Nuvarande klockslag
\T Klockslag (12-timmarsformat)
\@ Klockslag (am/pm-format)
\d Nuvarande datum
\s Skalets kommandonamn
\v Skalets versionsnummer
\V Versionsnumret, mer detaljerat
\a Alarmsignal
\e Esc
\n Nyrad-tecken
\\ Bakvänt snedstreck
\ nnn Tecken ur tabell 3.1
\# Kommandonummer
\! Kommandonummer bland bevarade kommandon

Värdet av variabeln PS2 bestämmer utseendet av den sekundära prompten, den prompt man får när ett kommando sträcker sig över mer än en rad.$ echo $PS2
>
$

En annan variabel som är viktig för Bash är PATH. Den anger en lista av kataloger, separerade av kolon, där Bash ska leta efter program som man anropar i sina instruktioner.$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:.
$
Till exempel använder man programmet wc i instruktionen wc bellman. Programmet wc är inte inbyggt i Bash, utan det finns i filen /usr/bin/wc på min dator. Bash kan hitta det tack vare att katalogen /usr/bin är uppräknad i PATH. Så här går det om vi tar bort /usr/bin från värdet av PATH:$ PATH=/usr/local/bin:/usr/games
$ wc bellman
bash: wc: command not found
$
Vi tvingas nu ange sökvägen till wc:$ /usr/bin/wc bellman
     12      71     372 bellman
$
Den nuvarande katalogen . finns för tillfället inte i värdet av PATH. Därför måste vi ange sökvägen till programmet wc även om vi befinner oss i den katalog som inhyser det:$ cd /usr/bin
$ wc /home/göran/bellman
bash: wc: command not found
$ ./wc /home/göran/bellman
     12      71     372 /home/göran/bellman
$
Vi bör tydligen lägga tillbaka de kataloger som togs bort ur PATH:$ PATH=/usr/bin:/bin:/usr/bin/X11:$PATH:.
$ echo $PATH
$ /usr/bin:/bin:/usr/bin/X11:/usr/local/bin:/usr/games:.
$ cd
$
Katalogerna i PATH genomsöks i tur och ordning. Om det finns två olika program med samma namn, så exekveras därför det av de båda programmen vars ''hemkatalog'' räknas upp först i PATH. Ordningen av katalogerna i PATH har alltså viss betydelse.

6.2 De första programmen

UNIX-skal är inte bara interaktiva kommandotolkar utan även fullfjädrade programmeringsspråk. I detta avsnitt ska vi berätta hur man kan programmera i Bash.

Det är inte svårt att göra egna program i Bash. I princip placerar man bara en följd av Bash-kommandon i en fil. Kommandona utförs i tur och ordning när programmet körs. Låt oss som exempel konstruera ett program som skriver ut texten ''Hej, världen!''. Så här skulle man göra det från prompten:$ echo 'Hej, världen!'
Hej, världen!
$
Men nu ska vi istället skapa en fil som innehåller detta kommando. Filen kallar vi för hejvärlden. I UNIX har man som konvention att på första raden i ett sådant program skriva tecknen #! och därefter ange vilken tolk som ska köra det. Filen ska alltså se ut så här (skapa den med hjälp av Emacs och ge den namnet hejvärlden):

#!/bin/bash
echo 'Hej, världen!'
Nu är det dags att prova programmet. Först måste vi sätta filrättigheterna så att hejvärlden blir ett körbart program:$ ls -l hejvärlden
-rw-rw-r--   1 göran    göran          33 nov 10 00:29 hejvärlden
$ chmod a+x hejvärlden
$ ls -l hejvärlden
-rw-rw-r--   1 göran    göran          33 nov 10 00:29 hejvärlden*
$
Programmet kan nu köras på samma sätt som andra UNIX-program som tex date och cd, vi ska helt enkelt ge kommandot hejvärlden:$ hejvärlden
Hej, världen!
$
För att detta ska fungera måste dock den nuvarande katalogen finnas i sökvägen för program. Vi kontrollerar:$ echo $PATH
/usr/bin:/bin:/usr/bin/X11:/usr/local/bin:/usr/games:.
$
Ja, punkten i slutet av PATH står för den nuvarande katalogen. Om den nuvarande katalogen inte funnits med, så hade jag varit tvungen att ange sökvägen till hejvärlden, tex så här:$ ./hejvärlden
Hej, världen!
$
Om det hade funnits ett annat program med namnet hejvärlden i någon av de kataloger som räknas upp före punkten i PATH, så skulle det programmet köras istället. Därför kan det vara värt att skriva ./hejvärlden även om den nuvarande katalogen finns i PATH. Men det bästa sättet är att placera programmen i en katalog med namnet bin i sin hemkatalog och sedan se till att PATH innehåller denna katalog. I så fall kan vi anropa programmen utan att ange sökvägen till dem.$ mkdir bin
$ mv hejvärlden bin
$ PATH=/home/göran/bin:$PATH
$ echo $PATH
/home/göran/bin:/usr/bin:/bin:/usr/bin/X11:/usr/local/bin:/usr/games:.
$ hejvärlden
Hej, världen!
$
(Denna ändring av PATH finns inte kvar när jag loggar in nästa gång. Om ändringen ska vara permanent, så ska den göras i filen .bash_profile - se avsnitt 6.4.) I fortsättningen förutsätter vi att alla program skapas med hjälp av en textredigerare och sparas i katalogen bin. Dessutom ska filerna som innehåller programmen naturligtvis vara exekverbara.

Man kan använda variabler i Bash-program, precis som vid prompten. Vi minns från avsnitt 6.1 att man måste sätta ett dollartecken framför variabelnamnet om man vill referera till variabelns värde. Vi ska nu skriva ett program som frågar efter ett namn och därefter ger en artig hälsning. Så här kan det se ut:

#!/bin/bash
echo 'Ange ditt namn:'
read NAMN
echo "Hej, $NAMN!"
Kommandot read NAMN läser in en text och sätter värdet av variabeln NAMN till denna text. På sista raden av programmet använder vi citationstecken i stället för apostrofer, för annars byter Bash inte ut $NAMN mot värdet av NAMN. Vi antar att programmet finns sparat i en exekverbar fil med namnet hej i katalogen bin. Låt oss prova:$ hej
Ange ditt namn:
Göran
Hej, Göran!
$
Det gick ganska bra. Men det vore bättre om man fick skriva sitt namn på samma rad som innehåller texten ''Ange ditt namn:''. Ett sätt att uppnå detta är att ge echo flaggan -n, för då skriver echo inte ut något nyrad-tecken efter texten. Ett annat sätt är att med hjälp av flaggan -p (''prompt'') låta read skriva ut texten. Vi ändrar programmet till följande:  
#!/bin/bash
read -p 'Ange ditt namn: ' NAMN
echo "Hej, $NAMN!"
Därefter kör vi programmet igen:$ hej
Ange ditt namn: Göran Andersson
Hej, Göran Andersson
$

Man kan också låta de egna Bash-programmen läsa in argument. Det första argumentet skrivs $1, det andra $2 osv. Vi gör en ny version av programmet hej, som tar emot namnet i form av ett argument istället:  

#!/bin/bash
echo "Hej, $1!"
Då fungerar det så här:$ nyhej Kalle
Hej, Kalle!
$ nyhej Nisse Nilsson
Hej, Nisse!
$
I det andra fallet ovan är ''Nisse'' det första argumentet och ''Nilsson'' det andra; programmet skriver bara ut det första argumentet.

Man kan säga att 1 fungerar som en variabel i Bash, och dess värde (som man får genom att skriva $1) är det första argumentet. Det finns ett antal andra sådana så kallade parametrar i Bash. Parametrarna 2, 3... står för det andra respektive tredje argumentet osv. (För att få värdet av tex det trettonde argumentet måste man skriva ${13}. Om man skriver $13, så tolkas det som värdet av den första parametern följt av en trea.) Parametern 0 står för det nollte argumentet, som är det namn programmet anropades under. Parametern * står för samtliga argument, från det första till det sista, separerade av mellanslag, och # står för antalet argument. Här är ett exempelprogram.

#!/bin/bash
echo "Totalt $# argument."
echo "Det trettonde argumentet är ${13}."
Programmet talar först om hur många argument det fick, och skriver sedan ut sitt trettonde argument. Vi sparar programmet under namnet argument i katalogen bin.$ argument 1 2 3 4 5 6 7 8 9 10 11 12 Tretton 14
Totalt 14 argument.
Det trettonde argumentet är Tretton.
$ argument 1 2 3 4 5 6 7 8
Totalt 8 argument.
Det trettonde argumentet är .
$
Vid den andra körningen ovan fanns det färre än tretton argument, och då har parametern 13 inget värde.

Vi ändrar programmet nyhej till följande:

#!/bin/bash
echo "Hej, $*!"
Nu skriver programmet ut samtliga argument i stället för bara det första.$ nyhej Kalle
Hej, Kalle!
$ nyhej Nisse Nilsson
Hej, Nisse Nilsson!
$

6.3 Miljön

Antag att vi har gett en viss variabel ett värde. Om vi sedan startar ett nytt program, känner det då till variabelns värde? Låt oss prova! Vi ger följande program namnet arv:

#!/bin/bash
echo "Variabeln TEST har värdet <$TEST>."
Sedan ger vi variabeln TEST värdet definierad, och kör därefter arv.$ TEST=definierad
$ arv
Variabeln TEST har värdet <>.
$
Nej, utskriften visar att vårt program inte känner till värdet av TEST. För att variabeln ska gå i arv till programmet måste vi först exportera den:$ export TEST
$ arv
Variabeln TEST har värdet <definierad>.
$
Kommandot export TEST exporterar variabeln TEST. När vi sedan kör programmet arv, så känner det till värdet av TEST.

Man kan ge kommandot printenv (eller export utan argument) för att ta reda på vilka variabler som är exporterade:$ printenv
TEST=definierad
HOSTNAME=fafner
MANPATH=/usr/man:/usr/X11R6/man:/usr/local/man
PS1=\$
USER=göran
MACHTYPE=i486-debian-linux-gnu
LC_MESSAGES=sv_SE
LC_TIME=sv_SE
LOGNAME=göran
SHLVL=1
SHELL=/bin/bash
HOSTTYPE=i486
OSTYPE=linux-gnu
TERM=linux
HOME=/home/göran
PATH=/home/göran/bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:.
$

Men vad är det för mening med att exportera variabler? Vi besvarar denna fråga genom att ge ett exempel. I utskriften ovan ser vi att variabeln LC_TIME är markerad för export. Variabeln anger vilka konventioner vi vill att datumangivelser ska följa. Variabeln påverkar bland andra programmet date. Det nuvarande värdet av LC_TIME är sv_SE, som står för ''svenska i Sverige''. Men vi ändrar nu värdet till da_DK och anropar date:$ LC_TIME=da_DK
$ date
søn feb 22 12:13:38 CET 1998
$
Effekten blev att dagens datum skrevs på danskt vis i stället för svenskt. Vi provar nu värdet de_AT, ''tyska i Österrike'':$ LC_TIME=de_AT
$ date
Son Feb 22 12:13:52 CET 1998
$
Denna gång fick vi utskriften på tyska. Genom att exportera variabler kan man överföra information till diverse program. Därigenom slipper man förmedla informationen via en massa argument varje gång programmen anropas.

Med process menas en programkörning. Om en process startar en annan process, så sägs den förra processen vara förälder till den senare, och den senare processen sägs vara barn till den förra. Om vi kör igång ett program från Bash, så är alltså Bashprocessen förälder till den nya programkörningen.

När en process startas, får den något som kallas för dess miljö. Miljön är en uppsättning variabler med definierade värden. Man skulle kunna säga att miljön är processens arvsanlag eller gener, för miljön går i arv från föräldraprocess till barnprocess. Det är inte så att det finns en enda miljö som är gemensam för alla program, utan varje process har som sagt en egen miljö som den tilldelas (ärver) av föräldern då den startas.

Vad som sker när vi exporterar en variabel i skalet är helt enkelt att variabeln placeras i miljön. Om vi sedan startar ett nytt program från Bash, så kopieras miljön till den nya programkörningen. Därför är samtliga exporterade variabler tillgängliga för de program vi startar.

Om ett Bash-skal ärver några miljövariabler från sin föräldraprocess, så gör Bash-skalet genast egna variabler som motsvarar dessa miljövariabler. Varje miljövariabel är alltså samtidigt en vanlig variabel, en så kallad skalvariabel, i Bash.

 Det finns några miljövariabler, som kallas för lokaler, som används för att definiera vilket språk och vilka kulturella konventioner man vill att programmen ska hålla sig till. En av lokalerna är LC_TIME. De andra heter LC_MESSAGES, LC_COLLATE, LC_CTYPE, LC_NUMERIC och LC_MONETARY. Om man vill att datorn ska använda det svenska språket såsom det talas i Sverige, och följa svenska konventioner, så ska man sätta dessa till sv_SE. Om variabeln LC_ALL har något värde, så används detta i stället för de enskilda lokalernas värden. Om varken LC_ALL eller någon av lokalerna har något värde, så används i stället värdet av variablen LANG.

För att ta bort namn från miljön ska man anropa export med flaggan -n:$ export -n LC_TIME
$ date
Sun Feb 22 18:29:46 CET 1998
$
Efter kommandot export -n LC_TIME är variabeln LC_TIME inte längre en del av miljön. Därför kunde date inte se dess värde, och vi fick utskriften på engelska.

Kommandot export namn=värde har samma effekt som namn=värde; export namn.$ export LC_ALL=sv_SE
$ date
sön feb 22 18:32:53 CET 1998
$

Nedan definierar vi två variabler och markerar den ena för export. Därefter startar vi ett nytt Bashskal i syfte att undersöka ifall de båda variablerna ärvs. Det nya skalet markeras med en inramning. Ett sådant ''skal i skalet'' kallas för ett subskal. Det kan liknas vid en ''berättelse i berättelsen'', något som ofta förekommer på film.$ NAMN='Göran Andersson'
$ ADRESS=goran_a@sslug.dk
$ export ADRESS
$ bash

$ echo "Namnet är <$NAMN>"
Namnet är <>
$ echo "Adressen är <$ADRESS>"
Adressen är <goran_a@sslug.dk>
$ exit
exit
$ Over ser vi att variabeln ADRESS, som exporterats till miljön, ärvdes. Variabeln NAMN, som inte var en del av miljön, var däremot odefinierad i det nya skalet.

Antag att en process, tex ett nytt Bash-skal som ovan, har ärvt en viss variabel från föräldern genom miljön. Antag vidare att variabeln ändras i den nya processen. Påverkas då motsvarande variabel hos föräldern? Nej, processen har ju sin egen miljö som är helt fristående från förälderns miljö. Mänskliga gener går i arv från föräldrar till barn men inte omvänt; förälderns gener påverkas inte ifall barnets gener senare förändras. Detsamma gäller för olika processer och deras miljöer, vilket följande exempel understryker:$ pwd
/home/göran
$ PS1='vad nu? '
vad nu? bash

vad nu? echo "Prompten är <$PS1>"
Prompten är <vad nu? >
vad nu? PS1='befall: '
befall: ADRESS=eva@turangalila.se
befall: cd /etc
befall: pwd
/etc
befall: exit
exit
vad nu? echo "$NAMN <$ADRESS>"
Göran Andersson <goran_a@sslug.dk>
vad nu? PS1='\$ '
$ pwd
/home/göran
$
Ovan ändras värdet av PS1, och sedan startas ett nytt skal. Värdet av PS1 ärvs uppenbarligen. Sedan ändras PS1 och ADRESS, och det nya skalet avslutas. Hos föräldern har varken PS1 eller ADRESS påverkats. Att den nuvarande katalogen ändrades i subskalet påverkade inte heller vad som var den nuvarande katalogen i det ursprungliga skalet. Om man har två skal igång samtidigt (tex på två olika virtuella terminaler) och exporterar en variabel i det ena skalet, så blir den inte därmed synlig i det andra skalet.

  
6.4 Startfiler

När ett Bash-skal startats vid inloggningen, innan första prompten skrivs ut, läser Bash in och exekverar de kommandon som finns i filen /etc/profile om den existerar.$ cat /etc/profile
ulimit -c unlimited
umask 022

source /etc/environment
$
Den första raden möjliggör för program som ''kraschar'' att lämna minnesutskrifter efter sig i filer med namn core för att man ska kunna ta reda på vad som gått snett. Den andra raden specificerar vilken filskyddskod som ska tilldelas nya filer och kataloger. Den sista raden använder Bash-kommandot source filnamn, som gör att filen filnamn läses in och exekveras. I filen /etc/environment definieras ett antal miljövariabler:$ cat /etc/environment
PATH='/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:.'
MANPATH='/usr/man:/usr/X11R6/man:/usr/local/man'
PS1='\u@\h \w\$ '
export PATH PS1 MANPATH
$
Dessa variabeldefinitioner skulle kunna göras i /etc/profile, men ofta är det praktiskt att på detta sätt samla dem i en separat fil, så att definitionerna kan användas också på andra ställen än i Bash - tex i startfilen för fönstersystemet X. Filen /etc/profile skapas av systemadministratören, men exekveras för alla användare på datorn. Varje användare kan också ha en egen startfil för Bash, .bash_profile (om den inte existerar, så försöker Bash med .bash_login eller .profile i stället). Den privata startfilen körs efter den gemensamma, så varje användare får själv sista ordet i fråga om tex värdena på miljövariabler.$ cat .bash_profile
umask 002
export PS1='\$ '
export LC_MESSAGES=sv_SE
export LC_TIME=sv_SE
source .bashrc
$ cat .bashrc
alias ls='ls -NF --color'
$
I min privata startfil för Bash ändrar jag värdena på umask och PS1 samt sätter lokalerna LC_MESSAGES och LC_TIME till sv_SE. Sedan körs kommandona i filen .bashrc - i detta fall bara ett enda kommando som ger ett alias för ls. (Ett lämpligt tillägg till filen är kommandot PATH=/home/göran/bin:$PATH.)

Om man själv startar ett nytt Bash-skal med kommandot bash, så är detta inte vad Bash kallar för ett login-skal, och då exekveras inte /etc/profile och .bash_profile. Skälet till detta är att man i dessa filer främst gör grundläggande inställningar av miljövariablerna. Dels är det onödigt att köra dem igen eftersom miljön ändå ärvs av det nya skal man sstartar, och dels har man kanske ändrat några miljövariabler och vill inte att de ska ändras tillbaka. För skal som inte är login-skal körs .bashrc i stället för /etc/profile och .bash_profile. Då alias (se avsnitt 6.5) inte ärvs genom miljön kan det vara lämpligt att definiera dem i .bashrc. Ofta vill man att .bashrc ska köras också för login-skal, men då måste man själv se till att så sker genom att ha med raden source .bashrc i sin .bash_profile.

   
6.5 Omskrivningar

Vi har sett några fall där Bash skriver om ett kommando på olika sätt innan det slutligen exekveras. Till exempel skrivs *.tex om till en alfabetiskt sorterad lista av de filer i den nuvarande katalogen vars namn slutar på ''.tex''. I detta avsnitt ska vi diskutera åtskilliga andra typer av omskrivningar som Bash kan göra med en kommandorad.

Tecknet ~ skrivs om till namnet på den egna hemkatalogen i konstruktioner som ~/dikter. Omskrivningen sker bara om tildetecknet kommer i början av ett argument.$ echo Min hemkatalog heter ~
Min hemkatalog heter /home/göran
$ cd ~/dikter
$ pwd
/home/göran/dikter
$
På motsvarande sätt skrivs ~eva om till namnet på eva:s hemkatalog ifall det finns en användare med namnet eva.$ echo ~eva/post
/home/eva/post
$ echo hej~
hej~
$ echo ~=
~=
$
I de båda sista exemplen skedde ingen omskrivning.

Om kommandoraden innehåller ett uttryck av formen $(kommando), så skriver Bash om detta till utdatan från kommandot kommando.$ echo Dagens datum är $(date)
Dagens datum är tis feb 17 18:30:20 CET 1998
$
Figur 6.2 visar vad som hände när kommandot ovan exekverades: argumentet $(date) skrevs om till tis feb 17 18:30:20 CET 1998 innan echo anropades.

  
Figur 6.2: Ett kommando i kommandot.
\begin{figure}
\begin{center}
\psframebox[framesep=1.1]{
\begin{psmatrix}
\p...
...ows=->]{1,1}{2,1}\tlput{Omskrivning}
\end{psmatrix} }
\end{center}\end{figure}

Så här kan man få date att ange dagens datum på formen ååmmdd:$ date +%y%m%d
980217
$
I nästa exempel gör vi en kopia av filen bellman. Namnet på kopian märks med dagens datum:$ cp bellman kopia.$(date +%y%m%d).bellman
$ ls
bellman               kopia.980217.bellman  lenngren
$
I gamla Bourne-skalet skrev man `kommando` i stället för $(kommando). Av kompatibilitetsskäl fungerar detta också i Bash.

Om ett argument i kommandoraden inleds med tecknet #, så stryks detta tecken och allt som kommer efter det på samma rad innan kommandot utförs. Man använder detta för att kunna skriva kommentarer till kommandon.$ date # Detta är en kommentar!
sön okt 12 23:05:29 CEST 1997
$ echo hej #på dej
hej
$ echo hej#på dej
hej#på dej
$

Ett argument av formen prefix{ord1,ord2,...}suffix skriver Bash om till orden prefixord1suffix prefixord2suffix osv. Alla ord som räknas upp inom klamrarna bildar alltså nya ord genom att prefix och suffix läggs till i början respektive på slutet.$ echo Sål{d,l,t}
Såld Såll Sålt
$ echo H{e,a}l{,a,t}!
Hel! Hela! Helt! Hal! Hala! Halt!
$ echo kap{1,2,5{A,B},23}
kap1 kap2 kap5A kap5B kap23
$

Som vi redan sett kan Bash hålla reda på alias för kommandon. Om man till exempel har gett kommandot alias ls='ls -NF -color' och sedan vid prompten skriver ett kommando som inleds med ls, så byter Bash ut ''ls'' mot ''ls -NF -color'' innan kommandot exekveras. (Alias är ett primitivt men trots det ganska användbart hjälpmedel. Betydligt kraftfullare är möjligheten att skriva funktioner i Bash.Följande exempel visar hur man kan få rm att automatiskt ta med flaggan -i:$ alias rm='rm -i'
$ rm bellman
rm: ta bort "bellman"? n
$ ls
bellman               kopia.980217.bellman  lenngren
$

6.6 Omdirigering

Det är inte svårt att skicka text som indata till ett program. Man kan helt enkelt låta echo skriva ut texten till programmet via en rörledning. Men det finns ett annat sätt, som ibland är bekvämare, nämligen att omdirigera indatan med tecknen <<. I följande exempel skickar vi texten Eva Thulin som indata till programmet hej från sida [*]:$ hej <<EOF
> Eva Thulin
> EOF
Hej, Eva Thulin!
$
Efter tecknen << skriver man ett godtyckligt ord. Ofta väljer man EOF, som står för ''End Of File''. Därefter skriver man den text som ska utgöra kommandots indata. Man avslutar med att på en ny rad skriva samma ord som man skrev efter tecknen <<. På detta sätt kan indatan till ett kommando skrivas i anslutning till själva kommandot.

#!/bin/bash
TAL=10
cat <<- EOF
        Hej, $1!
        Idag är det den $(date).
        Visste du att $TAL/2=$((TAL/2))?
        Hejdå!
EOF

Programmet hejsan ovan skriver ut fyra rader text med hjälp av cat. För att göra programmet lättläst har vi satt ett tabulatorsteg före varje rad av texten. Minustecknet efter tecknen << är en signal till skalet att dessa tabulatorsteg ska tas bort innan texten skickas genom cat.$ hejsan
Hej, Göran!
Idag är det den sön feb 22 19:01:17 CET 1998.
Visste du att 10/2=5?
Hejdå!
$
Körningen ovan visar att några av de vanliga omskrivningarna kan användas i den text som omdirigeras med <<. Om man beskyddar någon del av ordet efter <<, så undviker skalet dessa omskrivningar.

#!/bin/bash
cat <<- 'EOF'
        Hej, $1!
        Idag är det den $(date).
EOF

Programmet ovan ger följande utskrift:$ hejsan
Hej, $1!
Idag är det den $(date).
$

Man kan koppla samman ett antal kommandon genom att låta dem omges av parenteser. Därigenom får kommandona en gemensam in- och utdata. Betrakta tex följande två kommandon:$ echo -n 'Dagens datum är ' ; date
Dagens datum är sön feb 22 19:18:57 CET 1998
$
Om vi vill skicka utskriften från dessa kommandon genom wc, så kan vi inte göra så här:$ echo -n 'Dagens datum är ' ; date | wc
Dagens datum är       1       6      29
$
Vad som hände var att bara det sista kommandots utdata skickades till wc. Bash prioriterar nämligen operatorn | högre än ;. Med hjälp av parenteser går det bättre:$ (echo -n 'Dagens datum är ' ; date) | wc
      1       9      45
$
Kommandon som kopplats samman med hjälp av parenteser exekveras i ett subskal. Detta innebär att eventuella förändringar av variabelvärden eller nuvarande katalog mellan parenteserna inte är bevarade efter att kommandona kört färdigt. Om man vill att kommandona ska köras i det nuvarande skalet i stället för i ett subskal, så ska man använda klamrar i stället för parenteser; det sista kommandot måste då avslutas med ett semikolon.$ pwd
/home/göran/dikter
$ ( cd /etc ; ls -l passwd )
-rw-r--r--   1 root     root         1291 nov 25 14:37 passwd
$ pwd
/home/göran/dikter
$ { cd /tmp ; ls ; }
emacs.gz
$ pwd
/tmp
$
Ändringen av den nuvarande katalogen skedde i ett subskal i det första exemplet, och då bevarades inte ändringen. Den andra gången bevarades dock ändringen.

Ett annat skäl att koppla samman kommandon så här är att de då kan köras efter varandra i bakgrunden. Nedan startar vi två jobb i bakgrunden; det första väntar i 30 sekunder och skriver därefter ut vilka filer som finns i den nuvarande katalogen, medan det andra dekomprimerar filen emacs.gz.$ (sleep 30 ; ls ) &
[1] 7842
$ gunzip emacs.gz &
[2] 7844
$ jobs
[1]-  Running                 ( sleep 30; ls -NF --color ) &
[2]+  Running                 gunzip emacs.gz & 
$ echo Nu emacs*
är det klart
Nu är det klart
[1]-  Done                    ( sleep 30; ls -NF --color )
[2]+  Done                    gunzip emacs.gz
$
Vid kommandot jobs ovan ser man att de båda jobben är i full gång. Men medan jag skrev in nästa kommando, kom utskriften av ls från första jobbet emellan. Obekymrad om detta fullbordade jag mitt kommando, echo Nu är det klart. Innan nästa prompt skrevs ut gav Bash besked om att de båda jobben var klara. (Faktum är att jobb nummer två måste ha blivit klart före jobb nummer 1 eftersom ls inte skrev ut emacs.gz.)

6.7 Övningar

Övning. Ändra prompternas utseende till något som passar dig.

Övning. Skapa ett alias med namnet ll för ls -l. Prova det!

Övning. Beräkna 1048575/341 i Bash.

7. Mer om UNIX

  
7.1 Grupper och identiteter

Antag att några av användarna på datorn samarbetar om att skriva en bok. Då ska just dessa användare, men inga andra, ha rätt att ändra i de filer som innehåller materialet till boken. Ibland är det alltså önskvärt att mer än en användare, men ändå inte alla, ska tilldelas vissa befogenheter över en fil eller katalog. Detta är möjligt tack vare något som kallas för grupper. En grupp består av ett antal användare på systemet. Det kan till exempel finnas en grupp med namnet dikt, vars medlemmar arbetar med att sammanställa en diktsamling.

Om kommandot groups anropas utan argument, så skrivs det ut vilka grupper man själv är medlem i.$ groups
göran floppy dikt
$
Jag är tydligen medlem i grupperna göran, floppy och dikt.

Om groups anropas med ett eller flera användarnamn som argument, så får man veta vilka grupper var och en av dessa användare är medlem i.$ groups eva göran
eva : eva staff dikt
göran : göran floppy dikt
$
Användaren eva är alltså medlem i grupperna eva, staff och dikt.

Varje användare tillhör minst en grupp. Men även om man kan vara medlem i flera grupper, så ''befinner man sig'' vid varje tillfälle bara i en grupp. Den grupp användaren direkt efter inloggningen befinner sig i kallas för hennes login-grupp. På Debian GNU/Linux-system finns det för varje användare en grupp vars namn är detsamma som hennes användarnamn och i vilken hon själv är den enda medlemmen. Vanligtvis är det denna grupp som är hennes login-grupp. (Skälet till att alla användare har en ''egen grupp'' är att det därmed blir möjligt för dem att slentrianmässigt ge gruppen samma rättigheter till sina filer och kataloger som de själva har.)

Kommandot id ger information om grupper och identiteter:$ id
uid=1000(göran) gid=1000(göran) grupper=1000(göran),25(floppy),101(dikt)
$
Det kan vara värt att veta, att UNIX internt identifierar en användare inte med hjälp av hennes användarnamn, utan med hjälp av ett nummer som kallas för hennes användaridentitet (uid). Likaså representeras grupper internt med ett nummer som kallas för gruppidentiteten (gid). Min användaridentitet är 1000 och jag befinner mig just nu i gruppen med gruppidentitet 1000. Med hjälp av kommandot newgrp kan man byta nuvarande grupptillhörighet.$ newgrp dikt
$ id
uid=1000(göran) gid=101(dikt) grupper=1000(göran),25(floppy),101(dikt)
$
För att återgå till den förra grupptillhörigheten ska man ge kommandot exit.$ exit
$ id
uid=1000(göran) gid=1000(göran) grupper=1000(göran),25(floppy),101(dikt)
$

Flaggan -d anger att kommandot ls ska ge information om kataloger snarare än om filerna de innehåller.$ ls -ld dikter
drwxrwxr-x   3 göran    göran        1024 feb 20 13:19 dikter/
$
Jag ändrar nu grupptillhörigheten för katalogen dikter från göran till dikt. Kommandot för detta är chgrp.$ chgrp dikt dikter
$ ls -ld dikter
drwxrwxr-x   3 göran    dikt         1024 feb 20 13:19 dikter/
$
Hädanefter kan eva, som också är medlem i gruppen dikt, skapa och radera filer i katalogen dikter.

Kommandot för att byta ägare för en fil är chown, men det är bara root som byta ägare för filer - annars skulle man ju kunna ''stjäla'' filer från andra! Likaså måste man tillhöra en viss grupp för att kunna associera filer eller kataloger med gruppen.

Om man vill att kataloger och filer ska få grupptillhörigheten dikt direkt när de skapas, så ska man se till att man befinner sig i denna grupp.$ newgrp dikt
$ mkdir kopior
$ ls -ld kopior
drwxrwxr-x   2 göran    dikt         1024 mar  8 13:49 kopior/
$ exit
exit
$

För att byta användaridentitet till användarnamn ska man ge kommandot su användarnamn. Om argumentet användarnamn är root, så kan det utelämnas.$ su
Lösenord:
root@fafner# id
uid=0(root) gid=0(root) grupper=0(root)
root@fafner# exit
exit
$
De personer som är systemadministratörer på datorn brukar arbeta som ''vanliga användare'' en stor del av tiden. När de behöver utökade rättigheter går de över till användaridentiteten root genom att ge kommandot su.

7.2 Länkar

Antag att vi flyttar filen dikter.tgz till katalogen kopior:$ ls
bellman     dikter/     dikter.tgz  kopior/     lenngren
$ mv dikter.tgz kopior/nr1
$
Detta kan vålla problem eftersom vissa kanske förväntar sig att finna dikter.tgz där den fanns tidigare. För att undvika sådana problem kan man göra en symbolisk länk från den ursprungliga till den nya positionen för filen. En symbolisk länk är väsentligen ett meddelande om ny adress - ''jag finns inte här längre, jag finns där''. Symboliska länkar används flitigt på UNIX-system, bland annat för att man ska kunna vara säker på att alla program ska hitta de filer de behöver.

Kommandot för att skapa en symbolisk länk är ln -s.$ ln -s kopior/nr1 dikter.tgz
$
Ovan skapade jag en symbolisk länk till filen kopior/nr1. Länkfilen, som heter dikter.tgz, listas av ls ungefär som en vanlig fil:$ ls
bellman      dikter/      dikter.tgz@  kopior/      lenngren
$
Att dikter.tgz är en symbolisk länk markeras ovan med tecknet @ efter dess namn. Då vi begär utförlig information om dikter.tgz, ser vi att den är en hänvisning till kopior/nr1: $ ls -l dikter.tgz
lrwxrwxrwx   1 göran    göran       10 mar  8 22:21 dikter.tgz -> kopior/nr1
$
Det första tecknet i filskyddskoden är l för symboliska länkar.

Vad händer ifall någon försöker använda filen dikter.tgz? Låt oss prova!$ tar -ztvf dikter.tgz
drwxrwxr-x göran/göran       0 1998-02-27 19:43 dikter/
-rw------- göran/göran     372 1998-02-01 17:12 dikter/bellman
-rw-rw-r-- göran/göran     649 1998-02-01 17:12 dikter/lenngren
$
De flesta kommandon skickas automatiskt vidare till den riktiga filen när de använder en symbolisk länk. Detta var vad som skedde vid kommandot ovan; tar kördes i själva verket på filen kopior/nr1. Ett undantag från denna regel är rm: om vi ger kommandot rm dikter.tgz, så raderas länkfilen i stället för test/ny.fil.$ rm dikter.tgz
$ ls
bellman   dikter/   kopior/   lenngren
$ ls kopior
nr1
$
Det kan också förekomma symboliska länkar till kataloger.$ ln -s kopior gammalt
$ ls
bellman   dikter/   gammalt@  kopior/   lenngren
$
Nu är gammalt en symbolisk länk till katalogen kopior. Vi kan göra samma saker med gammalt som vi kan göra med kopior:$ ls gammalt
nr1
$ cd gammalt
$ pwd
/home/göran/gammalt
$ ls -l
total 1
-rw-rw-r--   1 göran    göran         829 mar  1 22:58 nr1
$

Om man raderar en fil eller katalog som det finns symboliska länkar till, så kommer dessa symboliska länkar att peka ut i tomma intet.$ cd
$ rm -r kopior
$ ls gammalt
gammalt@
$ cd gammalt
bash: cd: gammalt: Filen eller katalogen finns inte
$ rm gammalt
$ ls
bellman   dikter/   lenngren
$

 Informationen om en fil (bland annat filens längd, datum för senaste ändring, filen rättigheter samt var på lagringsmediet filen placerats) lagras i något som kallas för en inod. Filens namn lagras inte i inoden. Istället har varje inod ett nummer, dess inodsnummer. Kopplingen mellan ett filnamn och mosvarande inod görs genom att systemet i den katalog där filen finns lagrar enbart filens namn och motsvarande inodsnummer - själva inoden kan vara lagrad på någon annan plats. Vilket inodsnummer som hör till ett givet filnamn skrivs ut om man anger flaggan -i till ls.$ ls -i
 157097 bellman    157098 dikter/    157138 lenngren
$
Utskriften betyder att filen bellman har inodsnummer 157097 osv. På sätt och vis kan man hävda att inoden är filen, medan filens namn är ett slags länk, en hård länk, till inoden. Ingenting hindrar att man har mer än en hård länk till en och samma fil/inod. Därigenom kan en fil ha flera olika namn. Kommandot för att skapa en ny hård länk till en fil är ln (utan flaggan -s).$ ln bellman sång
$ ls -i
 157097 bellman    157098 dikter/    157138 lenngren   157097 sång
$
Nu är sång en hård länk till filen bellman, något som syns genom att de båda filerna har samma inodsnummer i utskriften ovan. Detta innebär att sång och bellman är två olika (men helt likvärdiga) namn för en och samma fil/inod.

När ls anropas med flaggan -l, skrivs antalet hårda länkar till varje fil ut efter dess skyddskod.$ ls -l
total 4
-rw-------   2 göran    göran         372 feb  1 16:48 bellman
drwxrwxr-x   2 göran    göran        1024 feb 27 19:43 dikter/
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
-rw-------   2 göran    göran         372 feb  1 16:48 sång
$
Siffran 2 som anges för sång och bellman i listningen ovan, betyder att det finns två hårda länkar till dessa filer. Låt oss skapa ytterligare en hård länk till dem:$ ln sång fredman
ls -li
total 5
 157097 -rw-------   3 göran    göran         372 feb  1 16:48 bellman
 157098 drwxrwxr-x   2 göran    göran        1024 feb 27 19:43 dikter/
 157097 -rw-------   3 göran    göran         372 feb  1 16:48 fredman
 157138 -rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
 157097 -rw-------   3 göran    göran         372 feb  1 16:48 sång
$
De tre filnamnen fredman, sång och bellman är hårda länkar till inoden med nummer 157097.

Om man raderar en hård länk, så påverkas inte de andra länkarna till samma inod (inoden tas bort först när samtliga länkar till den raderats).$ rm sång
$ ls
bellman dikter/ fredman lenngren
$ rm fredman
$ ls -l
total 3
-rw-------   1 göran    göran         372 feb  1 16:48 bellman
drwxrwxr-x   2 göran    göran        1024 feb 27 19:43 dikter/
-rw-rw-r--   1 göran    göran         649 feb  1 15:53 lenngren
$

Hårda länkar är inte lika vanligt förekommande som symboliska. De används främst när ett program ska fungera på olika sätt beroende på vilket namn det anropas under. Symboliska och hårda länkar är lika såtillvida att de båda kan ge en och samma fil olika namn. Det finns dock några skillnader. För det första kan det inte förekomma hårda länkar mellan filer som ligger på olika fysiska lagringsmedium som tex olika hårddiskpartitioner. Det kan inte heller förekomma hårda länkar mellan kataloger. En fördel med hårda länkar är att olika hårda länkar till en och samma inod är fullständigt likvärdiga - för symboliska länkar fungerar länkfilen i vissa avseenden annorlunda än den verkliga filen.

7.3 Filskyddskoder

Ett alternativt sätt att ange filskyddskoder är att använda siffror. Det fungerar på så sätt att läsrättigheten är värd fyra poäng, skrivrättigheten två poäng och rätten att exekvera en poäng. Genom att lägga samman poängtalen får man en siffra som kan representera skyddskoden. Till exempel motsvarar r-x siffran 4+1=5, r-- motsvarar 4 och --- motsvarar 0.$ chmod 764 bellman
$ ls -l bellman
-rwxrw-r--   1 göran    göran         372 feb  1 16:48 bellman*
$
Den första siffran i talet 764 gäller ägarens rättigheter, den andra gruppens och den tredje de övrigas.

 Det återstår att berätta om en viktig sak i samband med rättigheter. För att ta ett exempel, filen /etc/shadow innehåller alla användares lösenord (om än i krypterad form). Bara systemadministratören har rätt att läsa och ändra den filen. Men när jag byter lösenord måste ju den filen med nödvändighet ändras. Hur går det till? Svaret får vi genom att studera filskyddskoden för passwd. Vi lokaliserar var den fil som innehåller programmet passwd finns med hjälp av kommandot which, och läser sedan av dess skyddskod:$ which passwd
/usr/bin/passwd
$ ls -l /usr/bin/passwd
-rwsr-xr-x   1 root     root        33288 apr 19 10:20 /usr/bin/passwd*
$
Det intressanta här är tecknet s i skyddskoden för passwd. När ett program exekveras, ges det vanligtvis samma befogenheter som den som startade programmet har. Om tex användaren eva kör Emacs, så får denna Emacs-process bara ändra de filer som eva får ändra. Men om det står s i stället för x i skyddskoden för ägaren till en viss fil, så betyder det att när filen körs som ett program, så ges programmet samma rättigheter som filens ägare har. Bokstaven ''s'' står här för ''sätt användaridentitet - setuid''. När programmet passwd körs, så har det alltså samma rättigheter som root, vilket betyder att det får göra nästan vad som helst. (Minsta lilla fel i programmet passwd skulle därför vara ödesdigert. En av de främsta orsakerna till säkerhetsproblem i UNIX-system är när det finns brister i program som har s-biten satt.) På motsvarande sätt kan ett program ha en s-bit i skyddskoden för grupp. Då står ''s'' för ''sätt gruppidentitet - setgid'', och processen tilldelas de befogenheter som den grupp programfilen är associerad med har.

Förutom setuid och setgid finns en tredje specialrättighet som kan tilldelas filer och kataloger, nämligen fastbiten (''sticky bit''). Egentligen är den en kvarleva från UNIX barndom. Om en katalog har sin fastbit satt, så får ingen användare (med undantag för katalogens ägare) radera någon annan användares filer i den katalogen, vilket de vanligtvis får ifall de har skrivrättighet till katalogen. Betrakta till exempel katalogen /tmp:$ ls -ld /tmp
drwxrwxrwt   3 root     root         1024 mar  9 11:25 /tmp/
$
Vi ser att fastbiten är satt eftersom ls skriver bokstaven t sist i skyddskoden. I och med att ''övriga'' har skrivrättighet till /tmp kan jag skapa och radera filer där, men jag kan inte ändra i någon annans filer eftersom fastbiten är satt.

För att sätta fastbiten använder man chmod med argumentet o+t. Setuid sätts med argumentet u+s, setgid med g+s. Specialrättigheter kan också sättas med en siffra. Då är setuid värt 4 poäng, setgid 2 poäng och fastbiten 1 poäng. Poängsumman sätts framför de tre siffror som gäller de vanliga rättigheterna:$ chmod 5764 bellman
$ ls -l bellman
-rwsrw-r-T   1 göran    göran         372 feb  1 16:48 bellman*
$
Siffran 5 är 4+1, vilket betyder att setuid och fastbiten sätts på. Därför står det nu ''s'' och ''T'' i filskyddskoden. Att ls skriver ett stort T beror på att x-biten för ''övriga'' inte är satt. Likaså skrivs stora S när setuid (eller setgid) är satt medan x-biten för ägaren (gruppen) inte är satt. Låt oss återställa skyddskoden för bellman till ett förnuftigt värde:$ chmod 664 bellman
$ ls -l bellman
-rw-rw-r--   1 göran    göran         372 feb  1 16:48 bellman
$

Hur avgörs det vilken skyddskod nya filer får? Svaret på denna fråga får vi av kommandot umask.$ umask
002
$
Värdet som skrivs ut av umask talar om vilka rättigheter som ska tas bort. Den nuvarande inställningen innebär att skrivrättigheten tas bort för ''övriga''. $ mkdir ny.katalog
$ ls -ld ny.katalog
drwxrwxr-x   2 göran    göran        1024 mar  9 13:03 ny.katalog/
$
Som synes är alla rättigheter satta utom skrivrättigheten för ''övriga''. Låt oss ändra värdet så att skrivrättigheten tas bort för gruppen medan alla rättigheter tas bort för ''övriga'':$ umask 027
$ cp bellman ny.fil
$ ls -l ny.fil
-rw-r-----   1 göran    göran         372 mar  9 13:06 ny.fil
$ rm -r ny.*
$
I rättigheterna för ny.fil saknas, förutom de rättigheter som vi ville ta bort, även alla rättigheter att exekvera. Skälet till detta är att nya filer inte automatiskt får exekveringsrättigheter eftersom det inte är säkert att de faktiskt är program som kan exekveras.


 
Tabell 7.1: De olika filtyperna.
 
Tecken Typ av fil
- Vanlig fil
d Katalog
l Symbolisk länk
b Blockspecialfil
c Teckenspecialfil
p FIFO
s Socket

Det första tecknet i den filskyddskod som ls skriver ut, anger vilket slags fil det rör sig om. En förteckning över de olika filtyperna ges i tabell 7.1. De tre första typerna är vi redan bekanta med. De övriga fyra typerna används för olika specialfiler.

Under UNIX kommunicerar man med hårdvaruenheter via en specialfil i katalogen /dev. Ett exempel på detta är filen /dev/fd0.$ ls -l /dev/fd0
brw-rw----   1 root     floppy     2,   0 maj 28 02:49 fd0
$
Tecknet b i som skrivs ut före filskyddskoden anger att det rör sig om en så kallad blockspecialfil. Den första serieporten motsvaras av filen /dev/ttyS0.$ ls -l /dev/ttyS0
crw-rw----   1 uucp     dialout    4,  64 okt 12 20:38 /dev/ttyS0
$
Tecknet c innebär att /dev/ttyS0 är en så kallad teckenspecialfil. Specialfiler av typen FIFO (''först in, först ut'') används för rörledningar. Ett exempel ges av filen /dev/xconsole:$ ls -l /dev/xconsole
prw-r--r--   1 root     root          735 mar  9 12:28 /dev/xconsole
$
Den återstående filtypen är ''socket'', som används i nätverkskommunikation. Som exempel tar vi filen /dev/log:$ ls -l /dev/log
srw-rw-rw-   1 root     root            0 mar  6 13:22 /dev/log=
$

  
7.4 Montering

  
Figur: Filer och kataloger på en diskett.
\begin{figure}
\small \psset{angleB=90, angleA=-90, levelsep=36pt, armB=14pt,
...
...
\Tr{\psshadowbox{\rule{0pt}{6pt}\texttt{RMAIL}}}
}
\end{center}\end{figure}

Antag att jag har en diskett med det innehåll som visas i figur [*]. På disketten finns filerna svamp.html och RMAIL. Dessutom finns där två kataloger, dokument och program, som innehåller ytterligare några filer. Jag vill nu läsa av filerna som finns på disketten. Problemet är att man i UNIX bara kan komma åt filer som finns någonstans i hierarkin av kataloger under rotkatalogen. Men vill man använda en diskett, en extra hårddisk, en CD-skiva eller dylikt, så kan man montera den på en katalog i det ordinarie filsystemet. Om jag tex monterar min diskett på katalogen /floppy, så ser det ut som om de filer och kataloger som finns på disketten ligger i den katalogen i det ordinarie filsystemet. Då kan jag tex ge kommandot more /floppy/svamp.html för att läsa filen svamp.html på disketten, eller kommandot cp /floppy/svamp.html /home/göran för att kopiera den till min hemkatalog.


 
Tabell 7.2: De vanligaste slagen av filsystem.
 
Typ Används främst till
ext2 Linuxfilsystem på hårddisken
iso9660 CD-ROM
minix Linuxfilsystem på diskett
nfs Filsystem som finns på en annan dator
msdos Microsoftfilsystem på hårddisk och diskett
vfat Microsoftfilsystem på hårddisk och diskett

Nu återstår bara att berätta hur ett filsystem monteras. Vid monteringen måste man veta vilken typ av filsystem som finns på disketten. De vanligaste typerna anges i tabell 7.2. Om man har glömt bort vilken typ det är, så kan man prova sig fram - Linux ger ett felmeddelande ifall man gissar fel! Dessutom måste man veta vad diskettenheten kallas. Vanligtvis heter den första diskettenheten /dev/fd0, den andra /dev/fd1 osv. Kommandot för att montera ett filsystem är
mount -t typ enhet katalog
där typ står för den typ av filsystem som ska monteras, enhet för den enhet filsystemet finns på och katalog för den katalog filsystemet ska monteras på. Katalogen katalog måste existera. Den behöver inte vara tom, men det som eventuellt finns i den kan inte kommas åt så länge något är monterat på den. När filsystemet inte längre behöver vara monterat ska det monteras ned igen med kommandot umount katalog, där katalog står för den katalog filsystemet är monterat på. Om min diskett använder typen minix, så blir kommandot för att montera den mount -t minix /dev/fd0 /floppy, och kommandot för att montera ned den är umount /floppy. Men nu kommer vi till det riktigt stora problemet: av säkerhetsskäl är det vanligtvis bara systemadministaratören som får montera kataloger! Systemadministaratören skulle kunna göra så här:root@fafner# mount -t minix /dev/fd0 /floppy
root@fafner# cd /floppy
root@fafner# ls
RMAIL       dokument/   program/    svamp.html
root@fafner# cd
root@fafner# umount /floppy
root@fafner#
Det första kommandot monterar minix-filsystemet, som finns på disketten i första diskettenheten, på katalogen /floppy. Därefter kan man komma åt filerna och katalogerna på disketten som om de tillhörde det ordinarie filsystemet. Det sista kommandot monterade ned disketten. Det går inte att montera ned ett filsystem så länge någon av filerna eller katalogerna där används av något program. Inte heller kan filsystemet monteras ned när någon befinner sig i den katalog det är monterat på. Kommandot cd ovan flyttar root från katalogen /floppy, något som är nödvändigt för att nedmonteringen ska lyckas.

Hur ska jag som vanlig användare komma åt innehållet på min diskett? Jo, kanske har systemadministaratören gjort en inställning som tillåter användarna att montera disketter och CD-skivor, om än under restriktiva former. För att kontrollera om det är så ska vi studera filen /etc/fstab.$ cat /etc/fstab
#<enhet>         <katalog>      <typ>   <flaggor>    <> <pass>
/dev/sda1         /              ext2    defaults     0   1
/dev/sda7         none           swap    sw           0   0
proc              /proc          proc    defaults     0   0
/dev/sda3         /usr           ext2    defaults     0   2
/dev/sda2         /dosc          vfat    defaults     0   0
/dev/fd0          /floppy        vfat    user,noauto  0   0
/dev/fd0          /floppy/minix  minix   user,noauto  0   0
/dev/fd0          /floppy/ext2   ext2    user,noauto  0   0
/dev/cdrom        /cdrom         iso9660 user,ro      0   0
/dev/sda5         /home          ext2    defaults     0   2
fasolt:/home/eva  /home/eva      nfs     defaults     0   0
$
Det är systemadministaratören som skapar filen /etc/fstab. Filen är främst till för att ange vilka filsystem som ska monteras varje gång datorn startas om. Men om ett filsystem ofta monteras på ett visst ställe, så kan det också beskrivas i /etc/fstab, och då kan kommandot för att montera det förenklas - man behöver bara ge kommandot mount katalog ifall det anges i /etc/fstab vad som brukar monteras på katalog. Varje rad (utom de som inleds med tecknet #) motsvarar ett filsystem. Alla utom de som har ordet noauto under rukriken ''flaggor'' monteras automatiskt vid uppstarten. Under rubriken ''enhet'' anges på vilken fysisk enhet filsystemet finns, under ''katalog'' var det ska monteras, under ''typ'' vilken typ av filsystem det gäller. I den näst sista kolumnen ska det stå en nolla (så vitt jag vet används detta inte längre till något). Den sista kolumnen anger ifall man vill att filsystemet ska genomsökas efter eventuella fel innan det monteras. En nolla betyder att det inte ska felsökas, annars ska det stå en etta för filsystemet som ska innehålla rotkatalogen och en tvåa för alla andra. Apropå enhet: hårddiskar brukar heta /dev/hda, /dev/hdb, /dev/sda eller dylikt. Om hårddisken är uppdelad på flera delar (så kallade partitioner), så har delarna nummer 1,2,3 osv. Men vad som intresserar oss nu är ifall det under rubriken ''flaggor'' står user, för i så fall får vanliga användare montera det filsystem som beskrivs på den raden. Av utskriften ovan att döma får jag montera disketter i första diskettenheten. Om filsystemet på disketten är av typen vfat så får det monteras på katalogen /floppy, om det är av typen ext2 så får det monteras på /floppy/ext2 och om typen är minix så får det monteras på /floppy/minix. Så här kan jag alltså göra:$ mount /floppy/minix
$ cd /floppy/minix
$ ls
RMAIL       dokument/   program/    svamp.html
$ cp svamp.html /home/göran
$ cd program
$ ls
Makefile      hejvärlden.c  nim.c
$ pwd
/floppy/minix/program
$ cp /home/göran/bellman .
$ ls
Makefile      bellman       hejvärlden.c  nim.c
$ umount /floppy/minix
umount: /floppy/minix: device is busy
$ cd /floppy
$ umount /floppy/minix
$ ls minix
$
Först monterade jag min diskett som /floppy/minix. Jag kopierade filen svamp.html från disketten till min hemkatalog, och filen bellman från min hemkatalog till katalogen program på disketten. Därefter försökte jag montera ned disketten, men det gick inte eftersom jag befann mig i den katalog disketten var monterad på (eller snarare i en underkatalog till den). När jag gick bort från katalogen /floppy/minix och försökte igen, så gick det bättre. Efter att disketten monterats ned är katalogen /floppy/minix tom. Observera att disketten absolut inte får tas ur diskettenheten så länge den är monterad! Om man brutalt tar ur disketten utan att först montera ned den, så kan det inträffa att vissa filer bara finns med till hälften eftersom Linux ofta sparar saker i minnet en stund innan de skrivs över till disketten.

Med hjälp av kommandot df kan man få reda på hur mycket utrymme som finns fillgängligt på de filsystem som är monterade.$ df -h
Filesystem            Size  Used  Avail  Capacity Mounted on
/dev/sda1             304M   34M   254M     12%   /
/dev/sda3             1.1G  433M   647M     40%   /usr
/dev/sda2             314M  6.6M   307M      2%   /dosc
/dev/sda5             987M  662M   274M     71%   /home
fasolt:/home/eva      191M  161M    19M     89%   /home/eva
/dev/hdc              592M  592M      0    100%   /cdrom
$
Till exempel är femte partitionen på hårddisken /dev/sda monterad på katalogen /home. Utrymmet på partitionen är 987 megabyte, varav 662 megabyte är använt just nu.

II. Applikationer

   
8. Typsättning med LATEX

LATEX är ett professionellt typsättningsprogram. Det är ett av de främsta programmen på UNIX-system, i klass med GCC, Emacs och GIMP. Det är oerhört anvancerat och komplicerat, men i gengäld är LATEX fullständigt överlägset alla ordbehandlingsprogram vad gäller flexibilitet och kvalitet på de dokument som framställs.

LATEX är i första hand till för att typsätta artiklar, uppsatser, brev och böcker. Det går visserligen att tillverka tex reklambroschyrer och visitkort också, men man får inte så mycket hjälp med detta av LATEX.

I motsats till de flesta ordbehandlare är LATEX inte ett WYSIWYG-program. I stället använder man ett språk av samma typ som HTML, det som används i webbsidor. Man kan använda vilken textredigerare som helst för att skapa en LATEX-fil, som kan heta tex hej.tex. Sedan låter man LATEX behandla filen och skapa en ny fil med namnet hej.dvi. Ändelsen .dvi står för ''device independent''. Dvi-filen är i ett grafikformat som kan visas på datorskärmen med programmet xdvi, eller vidarebehandlas för en skrivare. Vanligtvis använder man programmet dvips för att översätta dvi-filen till postscript för att sedan skicka postscriptfilen till en skrivare.

Låt oss säga att hej.tex ser ut så här:

\documentclass[12pt]{article}
\usepackage[swedish]{babel}
\usepackage{t1enc}
\begin{document}
Hej, allihopa! Detta är mitt första \LaTeX-dokument.
\end{document}
Själva texten är det som kommer mellan \begin{document} och \end{document}, allt annat är kommandon till LATEX. Så här typsätter vi dokumentet:$ latex hej.tex
This is TeX, Version 3.14159 (C version 6.1)
(hej.tex
LaTeX2e <1996/12/01>
Babel <v3.6h> and hyphenation patterns for american, swedish, loaded.
(/usr/lib/texmf/tex/latex/base/article.cls
Document Class: article 1996/10/31 v1.3u Standard LaTeX document class
(/usr/lib/texmf/tex/latex/base/size12.clo))
(/usr/lib/texmf/tex/generic/babel/babel.sty
(/usr/lib/texmf/tex/generic/babel/swedish.ldf
(/usr/lib/texmf/tex/generic/babel/babel.def)))
(/usr/lib/texmf/tex/latex/base/t1enc.sty)
No file hej.aux.
[1] (hej.aux) )
Output written on hej.dvi (1 page, 356 bytes).
Transcript written on hej.log.
$ dvips -o hej.ps hej.dvi
This is dvipsk 5.58f Copyright 1986, 1994 Radical Eye Software
' TeX output 1997.10.22:1525' -> hej.ps
<tex.pro>. [1]
$
Nu har vi skapat en postscriptfil hej.ps, som kan skrivas ut på varje postscriptskrivare. Med hjälp av programet ghostscript kan postscriptfiler skrivas ut på nästan vilka skrivare som helst, och de kan dessutom visas på datorskärmen med extra hjälp från programmet gv. Under X kan man titta på dvi-filen med kommandot xdvi hej.dvi & eller på postscript-filen med kommandot gv hej.ps &. Resultatet ser ut ungefär så här:
Hej, allihopa! Detta är mitt första LATEX-dokument.
Det finns oerhört mycket mer att säga om LATEX, men vi får spara det till ett annat tillfälle!

   
9. Textbehandling med SGMLtools

9.1 Vad är SGML?

SGML är en ISO standard (ISO 8879:1986) för att beskriva dokument på ett sätt som datorer och människor kan förstå. Detta betyder att SGML inte är ett filformat som t.ex. TEX, .doc eller LATEXutan ett sätt att beskriva dokumentsstruktur. SGMLtools är en samling program som gör att dokument som har en sådan beskrivning kan tryckas, användas och publiceras.

Den mest använda SGML beskrivningen är HTML, som är konstruerad för att publicera material på internet. Eftersom de flesta dokument som vanliga människor skriver inte skall publiceras på internet så är det ganska naturligt att SGMLtools har valt DocBook som sin dokumenttyps beskrivning. Dokumenttypen DocBook är konstruerad utifrån krav från elektronik- och högteknologiföretag av Davenport gruppen (ArborText, Fujitsu, O'Reilly and Associates, mfl.).

Ett dokument skrivet enligt DocBook standarden kan bearbetas i många program innom SGMLtools används JADE för bearbetningen. JADE är vad man kallar en validerande SGML översättare, detta betyder att den först kollar att det du skrivit följer den SGML dokumenttypsbeskrivning du angivit (i SGMLtool är detta DocBook). Där efter så gör JADE om ditt dokument till objekt vilka skickas igenom ett filter (ett stilark) som gör vissa saker med dokumentet och sedan skickas slutresultatet till en fil.

För att skriva ett dokument så behöver man inte veta så här mycket teori, men det hjälper för att förstå DocBook.

Låt oss säga att hej.sgml ser ut så här:

<!DOCTYPE book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
]>
<book id="hej" lang="svse">
  <title>En intorduktion till DocBook</title>
  <bookinfo>
    <title>En introduktion till DocBook</title>
  </bookinfo>

  <toc></toc>

  <chapter id="kapitel1">

    <title>Hej</title>

    <para>
       Mitt första dokument som är skrivet i DocBook!
    </para>

    <sect1 id="Text">
      <title>DocBook är mycket struktur, lite layout</title>
      <para>
        För att skriva i DocBook måste man i första han tänka på
        struktur och inte på hur saker och ting ser ut. Det är 
        inte upp till dig som författare att bestämma hur texten
        ser ut, utan det avgörs av den som skriver stilbladet.
      </para>
    </sect1>

  </chapter>

</book>
$ sgmltools -b ps hej.sgml
Nu har vi skapat en postscriptfil hej.ps, som kan skrivas ut på varje postscriptskrivare. Med hjälp av programet ghostscript kan postscriptfiler skrivas ut på nästan vilka skrivare som helst, och de kan dessutom visas på datorskärmen med extra hjälp från programmet gv.

9.2 Uppbyggnad av ett SGML dokument

Nu har vi sett ett SGML dokument som är skrivet enligt DocBooks regler, men vad gör de olika raderna egentligen?

<!DOCTYPE book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
]>

Är den första raden i alla dokument som bearbetas av SGMLtools. <!DOCTYPE är ett nyckelord som SGMLtools använder för att hitta vad det första elementet i filen är. Texten PUBLIC berättar för SGMLtools att det är en publik dokumenttyp som finns med i SGMLtools katalog över dokumenttyper. Texten "-//Davenport//DTD DocBook V3.0//EN" talar om för SGMLtools vilken typ av dokument det är.

Denna texten är uppbyggd i ett par sektioner som är åtskilda av // det första är - tecknet. Det är satt där för att visa att DocBook inte är registrerad i ISO katalogen över dokumenttyper, annars skulle det ha varit ett +. Andra delen är ägaren eller tillverkaren av dokumenttypen, detta är altså Davenport gruppen. Nästa del är texten DTD vilket står för document type definition åtföljt av namnet på den dokumenttyp som vi har skrivit dokumentet i. EN betyder engelska, och kommer från att dokumenttypen är skriven på engelska (detta har inget med språket på dokumentet att göra)!

Mellan [ och ]> så kan man skriva utökningar av dokumenttypen som bara gäller i detta dokumentet.

Tidigare så nämde vi element ett element i texten är en logisk sektion som är markerad med s.k. taggar. En tag är T.ex. <book> efter varje tag så följer det oftast något som kallas cdata, det står för character data, och sedan kommer en sluttag, t.ex. </book>, eller en ny tag. I sgml så kan man ibland utelämna taggar om det av sammanhanget klart framgår vad som menas, denna funktion 9.1 är avslagen i DocBook. Det vill säga att varje element består av ett tagpar (logiskt eller uttryckligen), cdata och andra element.

I SGML kan man också skriva kommentarer som inte tolkas av SGML översättaren. Dessa skrivs som <!- lite text ->

9.3 DocBook

Strukturen i DocBook går från att man har ett <set> med <book> och varje <book> innehåller ett eller flera kapitel. Det finns även andra huvudtyper av dokument som article, refset.

En normal DocBook bok har en struktur som påminner om följande:

Book
  Bookinfo
  ToC <!-- Table of contents -->
  LoT <!-- För bilder -->
  LoT <!-- För tabeller -->
  Preface
  Chapter
  Chapter
  Chapter
  Reference
  Appendix
  Appendix
  Glossary
  Bibliography
  Index

Som sagt så är <book> det högsta elementet i en bok. Detta element har attribut som kan sättas till olika värden. Ett mycket använt attribut är lang="svse" detta skrivs <book lang="svse"> och bestämmer att detta är ett dokument som är skrivet på Svenska. Andra attribut som kan vara värda att lägga på minnet är id="textsträng" och role="textsträng", id används för att namnge en del av dokumentet så att man kan referera till den och om man behöver använda en tag på ett sätt den inte är avsedd för använder man role.

I DocBook finns två typer av semantiska element, de som är hirarkiska till sin funktion, och de som är till för att göra logiska block. Det är viktigt att skilja på dessa. Skilnaden är t.ex. en paragraf är ett logiskt block som kan förekomma på många ställen i hirarkin medans en sektion bara kan förekomma i ett kapitel.

9.3.1 Bookinfo

<bookinfo></bookinfo> används för att förse dokumentet med information om författare, publicering, tryckning och upphovsrätt. I kort allt det som gäller boken och som brukar tryckas på insidan av första bladet. Saker som kan vara bra att fylla i är <author></author> som i sin tur innehåller <honorific></honorific>, <firstname></firstname>,
<othername></othername> och <lineage></lineage> vilka innehåller den sedvanliga författar informationen som namn. Honorific motsvarar svenskans akademisk titel, lineage används knappast i Sverige, men skall innehålla tert som jr, dä, dy för att särskilja två författare som har samma namn. Förutom detta så finns också <authorblurb></aurthorblurb> som innehåller lite lös text om författaren innom <para> taggar.

<leagalnotice></leagalnotice> är också bra att känna till den används för att kunna skriva saker som, detta dokument får inte reproduceras. För att indikera vem som har upphovsrätt på verket används <copyright>.

<COPYRIGHT>
<YEAR>1998</YEAR>
<HOLDER>Martin Wahlen</HOLDER>
</COPYRIGHT>
<LEGALNOTICE>
<PARA>
  Detta är ett exempel på Copyright som man kan använda!
</PARA>
</LEGALNOTICE>

Är ett kort exempel på vad en bookinfo kan innehålla.

9.3.2 TOC

<toc></toc> Insättningspunkt för en innehållsförteckning. Här genereras en innehållsförteckning med alla Kapitel, Sektioner och Appendix.

9.3.3 Preface, Chapter och Appendix

Dessa element är uppradade eftersom de utgör själva innehållet i de flesta dokument. Under dessa kan man ha ett antal sektioner. Eller så kan man bara ha lösa paragrafer om man vill det.Elementen skall alltid ha minst en rubrik. Rubriken skrivs innom <title> taggar och är ett logisktblock. Det vill säga att man inte behöver skriva <para></para> runt den text man vill ha som rubrik.

<chapter id="kapitel1">
  <title>Hej</title>
  <para>Detta &auml;r ok</para>
</chapter>

Här införde vi &auml; detta motsvarar ett ä, eftersom man inte kan vara säker på att alla datorer hanterar 8-bitars tecken uppsättning har man valt att låta SGML koda vissa bokstäver som entities. De som används på svenska är &aring, &auml; och &ouml;. Det är även bra att kunna danskans ä nämligen &aeling;.

9.3.4 Listor

Det finns många olika typer av listor i DocBook, men gemensamt har de alla att <listitem><para>Text</para></listitem> är hur listorna är uppbyggda. De två vanligaste typerna av listor är <OrderedList> och ItemizedList.

9.3.5 Bilder

Något som brukar vara ganska populärt när man skriver dokument är figurer och bilder som illustrerar det man diskuterar. I DocBook så används <figure> för detta ändamål. <figure> har ett par viktiga extra attribut, det viktigaste är float, detta attribut har två värden ett eller noll. Ett gör att figuren kan flyttas i texten till det ställe där JADE tycker att den passar bäst. Noll innebär att bilden måste sättas in här i förhållande till texten. I figuren skall det sedan stå en <title> tag med en beskrivande rubrik till bilden.

Efter <title> taggarna kommer det som är intressant nämligen <graphic> taggen. Den har som attribut fileref="filnamn" vilket gör att man kan ta in bilder i sina dokument. Vidare så finns ett attribut, format="format" där format kan vara eps, gif, jpeg, etc

9.3.6 Table

DocBook använder sig av CALS tabeller. Detta är en typ av tabell som finns med i många olika dokumenttyper. En tabell är ju troligen något som inte är så olikt oavsätt vilken typ av dokument man skriver.

CALS tabellen

9.3.7 Index

Man kan ju givetvis generera ett index till sina böcker och skrifter det finns ett flertal taggar för att göra detta. Normalt så använder man

9.3.8 Ett exempel

<!DOCTYPE article PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
]>

<article lang="svse" id="Exempel1">
<artheader>
<author>
<firstname>Martin</firstname>
<surname>Wahlen</surname>
<othername>Viktor</othername>
<authorblurb>
<para>Styrelseledamot i SSLUG och studerande vid Lunds Universitet</para> 
</authorblurb>
</author>
<abstract>
<para>
Detta &auml;r en artikel som visar lite olika finesser i DocBook och i SGML.
</para>
</abstract>
</artheader>
<sect1>
<title>Ett logisktblock &auml;r rubriken</title>
<para>
F&ouml;r att skriva i DocBook beh&ouml;ver man en bra editor. T.ex. VIM (VI 
Improved). Andra alternativ &auml;r emacs med psgml, jed eller n&aring;gon 
av de kommerciella alternativen som FrameMaker+SGML.
</para>
<itemizedlist id="not-in-para">
<listitem><para>Detta är en lista</para></listitem>
<listitem><para>Här kan man ha saker</para></listitem>
<listitem><para>En listitem kan innehålla flera paragrafer</para>
<para>Som den här</para>
</listitem>
<listitem>
<orderedlist>
<listitem><para>Inne i Listan så kan man ha en lista till!<para></listitem>
</orderedlist>
</listitem>
</itemizedlist>
<para>
En lös paragraf kan följa efter listan. Och den kan ha en lista i sin tur
<itemizedlist id="in-para">

<listitem><para>
Litetext
</para></listitem>
</itemizedlist>
</para>
</sect1>
</article>

9.4 Mer om SGML

För att få ännu lite mer förståelse för fördelarna med SGML och att skriva i SGML behöver man lite mer praktiska tips. Innan så nämde vi <!DOCTYPE book PUBLIC "-//Davenport// DTD DocBook V3.0//EN" []> och att man kunde utöka dokument typen genom att skriva till saker innom [] en av de saker som är bra att skriva där är <!ENTITY för mitt namn. Ni kan säkert hitta på fler intersanta användningsområden. För detta. En annan intersant sak är markerade sektioner genom att skriva <[IGNORE[ Lite text ]]> så kan man få SGML översättaren att ignorera stycken. Om man kombinerar detta med det faktum att man kan sätta in ENTITY nykelord i texten så kan man göra mycket modulära dokument. T.ex. så kan man skriva sin CV så att den innehåller alla projekt man har arbetat på och med hjälp av markerade sektioner så kan man aktivera de sektioner som är viktiga för just ett visst arbete.

9.5 Övningar till Läsaren

1.
Skriv en lista över dina favorit skådespelare i DocBook format.
2.
Vilken toptag bör man använda för att skriva en artikel?
3.
Skriv om din CV i DocBook.

Jag rekomenderar att ni även tittar på SGMLtools hemsidan på http://www.sgmltools.org och på DocBook hemsidan http://www.ora.com/davenport

III. Programmeringsmiljön

10. Textbehandling

10.1 Reguljära mönster

I grep och flera andra UNIX-program används något som kallas för reguljära mönster. Syftet med reguljära mönster är helt enkelt att matcha text. Tekniken liknar den som skalet använder för att matcha filnamn, men den är mycket kraftfullare. De följande sidorna beskriver hur reguljära mönster byggs upp.

Reguljära mönster kan innehålla flera symboler, metatecken, som har speciella betydelser. Metatecknen är . \ * [ ^ och $. Det enklaste av dem är punkten. Den matchar ett godtyckligt tecken. (Dock med undantag för nyrad-tecknet. Då grep och andra program som använder reguljära mönster endast betraktar en rad i taget, kan mönstren aldrig innehålla något nyrad-tecken.)$ grep 'n.ta' bellman
den skönsta nymf, som åt dig ler,
     inunder armen tag.
$
Mönstret n.ta matchade här ''nsta'' och ''n ta''. Om mönstret innehåller flera punkter så matchar varje punkt ett godtyckligt tecken; det behöver inte vara samma tecken varje gång:$ grep 'n..a' bellman
från Bacchi buller och tumult,
och du, du yngling, lyd min lag:
den skönsta nymf, som åt dig ler,
     inunder armen tag.
$
Genast undrar man hur man ska kunna matcha en punkt. Svaret är att såväl punkten som de övriga metatecknen skyddas av det bakvända snedstrecket:$ grep 'tag\.' bellman
     inunder armen tag.
$
Om vi utelämnar skyddet för punkten, så matchar den bla blanktecken:$ grep 'tag.' bellman
     inunder armen tag.
nå välan, så tag dig då en sup,
tag dig se'n dito en, dito två, dito tre,
$
Mycket ofta måste man skydda reguljära mönster eftersom de kan innehålla symboler som har en speciell betydelse för skalet. Tex måste mönstret tag\. skyddas för att skalet inte ska plocka bort det bakvända snedstrecket. Vi kommer därför konsekvent att använda apostrofer som beskydd för mönstret, även när det som i fallen n..a och tag. ovan inte är strängt nödvändigt. Om vi vill matcha en apostrof tvingas vi dock göra så här:$ grep \' bellman
tag dig se'n dito en, dito två, dito tre,
$

Reguljära mönster använder hakparenteser för att matcha en lista av tecken. Lyckligtvis är syntaxen densamma som för motsvarande jokertecken i skalet. Mönstret [A-J] matchar alltså en av versalerna från A till J.$ grep '[A-J]' bellman
från Bacchi buller och tumult,
när döden ropar: Granne, kom,
Du gubbe, fäll din krycka ner,
$
Nästa uppgift är att finna de rader i filen bellman som innehåller någon stor bokstav. Detta är lätt, vi kan helt enkelt använda mönstret [A-ZÅÄÖ]. Men det finns ett bättre sätt att ange alla stora bokstäver, nämligen [:upper:]. (I tabell [*] ges en förteckning över de kategorier av tecken som kan anges på liknande sätt.)

 
Tabell: Kategorier som kan användas i listor.
 
Kategori av tecken Beteckning
Bokstäver [:alpha:]
Stora bokstäver [:upper:]
Små bokstäver [:lower:]
Siffror [:digit:]
Hexadecimala siffror [:xdigit:]
Alfanumeriska (alpha/digit) [:alnum:]
Blanktecken [:space:]
Skrivbara samt mellanslag [:print:]
Skrivbara [:graph:]
Skrivbara men ej alfanumeriska [:punct:]
Kontrolltecken [:cntrl:]

$ grep '[[:upper:]]' bellman
Så lunka vi så småningom
från Bacchi buller och tumult,
när döden ropar: Granne, kom,
Du gubbe, fäll din krycka ner,
Tycker du, att graven är för djup,
$
En fördel med att skriva [:upper:] i stället för A-ZÅÄÖ är att avsikten med mönstret tydligare framgår. Än viktigare är att mönstret [:upper:] är oberoende av teckenuppsättning. Det matchar just de tecken som är stora bokstäver i användarens teckenuppsättning (förutsatt att lokalen LC_CTYPE eller LC_ALL är satt, se avsnitt 6.3).

Om man sätter tecknet ^ direkt efter den vänstra hakparentesen, så matchas ett godtyckligt tecken som inte räknas upp i listan. Så här finner vi de rader som innehåller texten ''du'' följt av något annat än kommatecken:$ grep 'du[^,]' bellman
och du, du yngling, lyd min lag:
     så dör du nöjdare.
$
Bara den första raden i filen bellman saknar såväl punkt som kommatecken:$ grep -v '[.,]' bellman
Så lunka vi så småningom
$

Den minsta beståndsdelen i reguljära mönster, som vi kan kalla för en ''atom'', matchar exakt ett tecken. Vi har redan beskrivit atomerna. Tabell [*] ger en sammanfattning av dem.

 
Tabell: Atomerna i reguljära mönster.
 
Atom Reguljärt mönster
Specifikt tecken t \ t om tecknet är något av
  . \ * [ ^ $, annars t
Godtyckligt tecken .
Tecken ur lista [ lista]
Tecken utanför lista [^ lista]
Understryknings- eller alfanumeriskt tecken \w
Motsatsen till \w \W

Men kanske är det fel att påstå att atomerna är den minsta beståndsdelen i reguljära mönster, för det finns ett antal olika sätt att matcha noll tecken - se tabell [*]. Metatecknen $ och ^ matchar början respektive slutet av en rad. Så här finner vi alltså de rader som inleds med ''och'':$ grep '^och' bellman lenngren
bellman:och du, du yngling, lyd min lag:
lenngren:och magen, kullrig som ett berg,
lenngren:och fann likören rätt begärlig.
lenngren:och ropte: Store Gud, vad är vårt usla liv?
$
Man brukar säga att tecknet ^ förankrar mönstret till början av en rad. Analogt förankrar dollartecknet ett mönster till slutet av en rad. Vilka rader slutar med tecknen ''an''?$ grep 'an$' bellman lenngren
lenngren:Vid sängen stod ett bord, där denne andans man
lenngren:Hans vördighet grep saken an
$
Vilka rader i bellman slutar med något annat än punkt eller kommatecken? $ grep '[^.,]$' bellman
Så lunka vi så småningom
och du, du yngling, lyd min lag:
$
I vilka rader är ''t'' det näst sista tecknet?$ grep t.$ bellman lenngren
bellman:från Bacchi buller och tumult,
bellman:     ditt timglas är nu fullt.
lenngren:med klunk för klunk och bit för bit,
$
Vilka rader i filen bellman inleds med stor bokstav?$ grep '^[[:upper:]]' bellman
Så lunka vi så småningom
Du gubbe, fäll din krycka ner,
Tycker du, att graven är för djup,
$

 
Tabell 10.3: Att matcha noll tecken.
 
Ankare Matchar noll tecken...
^ i början av en rad
$ i slutet av en rad
\< i början av ett ord
\> i slutet av ett ord
\b i kanten av ett ord
\B utom i kanten av ett ord

Tecknen \< matchar början av ett ord. Så här söker vi efter ord som börjar på ''dit'':$ grep '\<dit' bellman
     ditt timglas är nu fullt.
tag dig se'n dito en, dito två, dito tre,
$

Efter en atom kan man placera en ''multiplikator'', som upprepar atomen ett antal gånger. En förteckning av multiplikatorerna ges i tabell [*].

Metatecknet * betyder att föregående atom får upprepas hur många gånger som helst (inklusive noll).$ grep 'ul*t' bellman lenngren
bellman:från Bacchi buller och tumult,
bellman:     ditt timglas är nu fullt.
lenngren:matt utstäckt mellan tvenne lakan.
$
Mönstret ul*t matchade såväl ''ult'' som ''ullt'' och ''ut''.$ grep o.*st lenngren
Vår prost jag häromdagen såg
sig hävde upp mot isterhakan.
sin frukost redan färdig fann
$
Mönstret o.*st matchade här ''ost'' och ''ot ist''. Lägg märke till att .* matchar en godtycklig följd av tecken. Vilka av de rader som innehåller texten ''in'' slutar med ett kommatecken?$ grep 'in.*,$' bellman
Du gubbe, fäll din krycka ner,
$
Vilka ord börjar med d och slutar med n?$ grep '\<d[[:alpha:]]*n\>' bellman
när döden ropar: Granne, kom,
Du gubbe, fäll din krycka ner,
den skönsta nymf, som åt dig ler,
$
De ord som gav träff ovan var ''döden'', ''din'' och ''den''.

Multiplikatorn \? upprepar föregående atom noll eller en gång:$ grep 'ul\?t' bellman lenngren
bellman:från Bacchi buller och tumult,
lenngren:matt utstäckt mellan tvenne lakan.
$
Multiplikatorn \+ upprepar föregående atom en eller fler gånger:$ grep 'ul\+t' bellman lenngren
bellman:från Bacchi buller och tumult,
bellman:     ditt timglas är nu fullt.
$

Nu ska vi leta efter en följd av små bokstäver som omges av ett ''n'' och ett ''e'':$ grep 'n[[:lower:]]*e' bellman
när döden ropar: Granne, kom,
Du gubbe, fäll din krycka ner,
     inunder armen tag.
     så dör du nöjdare.
$

 
Tabell 10.4: Multiplikatorerna.
 
Multiplikator Matchar föregående atom...
* noll eller fler gånger
\? noll eller en gång
\+ en eller fler gånger
\{m\} exakt m gånger
\{m,\} minst m gånger
\{m,n\} mellan m och n gånger

Man kan mellan tecknen \{ och \} ange exakt hur många gånger atomen får upprepas. Texten ''nunde'' ur ordet ''inunder'' består av ett ''n'' följt av tre små bokstäver och ett ''e'':$ grep 'n[[:lower:]]\{3\}e' bellman
     inunder armen tag.
$
Härnäst tillåter vi upp till två små bokstäver mellan ''n'' och ''e'':$ grep 'n[[:lower:]]\{0,2\}e' bellman
när döden ropar: Granne, kom,
Du gubbe, fäll din krycka ner,
     inunder armen tag.
$
Ovan fick vi träff på ''nne'', ''ne'' och ''nde''. Mönstret nedan tillåter 1-4 små bokstäver mellan ''n'' och ''e'':$ grep 'n[[:lower:]]\{1,4\}e' bellman
när döden ropar: Granne, kom,
     inunder armen tag.
$
Och så här upprepas atomen två eller fler gånger:$ grep 'n[[:lower:]]\{2,\}e' bellman
     inunder armen tag.
     så dör du nöjdare.
$


 
Tabell: Reguljära mönster.
 
Mönster Betyder
r1\|r2 Mönstret r1 eller mönstret r2
\(r\) Gör en atom av mönstret r,
  spara matchande text i ett register.
\n Innehållet i register n

Reguljära mönster kan skilja mellan olika alternativ. De alternativa mönstren separeras av tecknen \|. Det lodräta strecket \| betyder ''eller'', så det totala mönstret matchar de textsträngar som matchas av något av de alternativa mönstren. Så här finner vi de rader som innehåller något av orden ''du'', ''dig'' eller ''ditt'':$ grep 'du\|dig\|ditt' bellman
     ditt timglas är nu fullt.
och du, du yngling, lyd min lag:
den skönsta nymf, som åt dig ler,
Tycker du, att graven är för djup,
nå välan, så tag dig då en sup,
tag dig se'n dito en, dito två, dito tre,
     så dör du nöjdare.
$
Nu ska vi söka efter rader som innehåller ordet ''är'' efter något av orden ''du'', ''dig'' eller ''ditt''. Vi behöver då använda ''parenteserna'' \( och \).$ grep '\(du\|dig\|ditt\).*\<är\>' bellman
     ditt timglas är nu fullt.
Tycker du, att graven är för djup,
$

Ett reguljärt mönster som omges av tecknen \( och tecknen \) kan behandlas som en atom. Därigenom blir det möjligt att placera en multiplikator på mönstret.$ grep '\(du,\? \)\{2,\}' bellman
och du, du yngling, lyd min lag:
$

Den text som matchas av ett mönster som omges av \( och \), sparas i ett så kallat register. Man kan referera till texten i registret med tecknen \1. Så här finner vi de fall då a, b eller c förekommer två gånger i rad:$ grep '\([abc]\)\1' bellman
från Bacchi buller och tumult,
Du gubbe, fäll din krycka ner,
$
Ovan fick vi träff på ''cc'' i ordet ''Bacchi'' respektive ''bb'' i ordet ''gubbe''. Finns det några ord som slutar med en dubblerad bokstav?$ grep '\([[:alpha:]]\)\1\>' bellman
     ditt timglas är nu fullt.
Du gubbe, fäll din krycka ner,
Tycker du, att graven är för djup,
$
Ja, ''ditt'', ''fäll'' och ''att''.

Om det förekommer flera par av tecknen \( och \) i ett reguljärt mönster, så sparas den matchande texten i upp till nio olika register. Den text som matchar mönstret i parentesen längst till vänster sparas i register 1. Register 2 hör till parentesen näst längst till vänster osv. Innehållet i register n matchas av \n.

10.2 Redigering i förbifarten

Programmet sed är (bland annat) till för att automatisera textredigering. Det skapades genom en enkel bearbetning av editorn ed. Det läser in en text i form av indata eller från en fil, utför redigeringskommandon på en textrad i taget, och skriver sedan ut raden i sin nya form. Den ursprungliga filen ändras inte.

En av de redigeringar sed kan göra är substitutionen ''Byt söktext mot ersättningstext''; det skrivs s/söktext/ersättningstext/. Söktexten kan vara ett reguljärt mönster. I stället för tecknet / kan man välja något annat förutsatt att samma tecken används på alla tre ställen i kommandot.

Flaggan -e anger att nästa argument är ett kommando till sed och inte ett filnamn. Så här byter vi ut ''du'' mot ''DU'' i filen bellman:$ sed -e 's/du/DU/' bellman
Så lunka vi så småningom
från Bacchi buller och tumult,
när döden ropar: Granne, kom,
     ditt timglas är nu fullt.
Du gubbe, fäll din krycka ner,
och DU, du yngling, lyd min lag:
den skönsta nymf, som åt dig ler,
     inunder armen tag.
Tycker DU, att graven är för djup,
nå välan, så tag dig då en sup,
tag dig se'n dito en, dito två, dito tre,
     så dör DU nöjdare.
$
I utskriften har den första förekomsten av ''du'' på varje rad bytts ut mot ''DU''. Det är oftast nödvändigt att skydda redigeringskommandona med apostrofer, så det är bra att göra det till en vana att alltid ha dem med.

Om alla förekomster ska bytas ut, så ska man ange flaggan g i slutet av substitutionskommandot:$ sed -e 's/[dD]u/DU/g' bellman
Så lunka vi så småningom
från Bacchi buller och tumult,
när döden ropar: Granne, kom,
     ditt timglas är nu fullt.
DU gubbe, fäll din krycka ner,
och DU, DU yngling, lyd min lag:
den skönsta nymf, som åt dig ler,
     inunder armen tag.
Tycker DU, att graven är för djup,
nå välan, så tag dig då en sup,
tag dig se'n dito en, dito två, dito tre,
     så dör DU nöjdare.
$
I stället för g kan man ange ett nummer n, och då byts bara den n:te förekomsten på varje rad ut.

Flaggan -n gör så att ingen rad automatiskt skrivs ut:$ sed -ne 's/[dD]u/DU/g' bellman
$
Flaggan p i substitutionskommandot anger att de rader som innehåller söktexten ska skrivas ut:$ sed -ne 's/[dD]u/DU/gp' bellman
DU gubbe, fäll din krycka ner,
och DU, DU yngling, lyd min lag:
Tycker DU, att graven är för djup,
     så dör DU nöjdare.
$
Tecknet & i ersättningstextentexten står för det som matchas av söktexten.$ sed -ne 's/[dD]u/(&)/gp' bellman
(Du) gubbe, fäll din krycka ner,
och (du), (du) yngling, lyd min lag:
Tycker (du), att graven är för djup,
     så dör (du) nöjdare.
$

Programmet åäö nedan ger ett typiskt exempel på hur sed används.

#!/bin/bash
if [ $# -ne 1 ] ; then
  echo "Ange exakt ett argument" 1>&2
  exit 1
fi
if [ ! -f "$1" ] ; then
  echo "Filen $1 finns inte" 1>&2
  exit 1
fi
mv "$1" "$1.kopia"
sed \
-e 's/å/\å/g' -e 's/ä/\ä/g' -e 's/ö/\ö/g' \
-e 's/Å/\å/g' -e 's/Ä/\Ä/g' -e 's/Ö/\Ö/g' \
"$1.kopia" > "$1"

Programmet tar ett argument, som ska vara namnet på en fil. Först kontrolleras det att antalet argument är 1. Om inte, så avslutas programmet med ett näsvist felmeddelande. Likaså avslutas programmet om argumentet inte är namnet på en befintlig fil. Filen kopieras sedan till en ny fil som ges ett namn med ändelsen .kopia. Sedan byts bokstäverna åäöÅÄÖ ut mot textsträngar som motsvarar dem i språket HTML, och resultatet sparas i en fil med samma namn som den ursprungliga. Lägg märke till att &-tecknen i ersättningstexten måste skyddas.

Antag nu att det i katalogen /tmp finns en HTML-fil:$ ls /tmp
test.html
$ cat /tmp/test.html
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Test av åäö</title>
</head>
<body>
<h1>Test av åäö</h1>
Det här är en testfil för
mitt lilla program "åäö"!
</body>
</html>
$
Låt oss köra programmet på filen:$ åäö /tmp/test.html
$ ls /tmp
test.html test.html.kopia
$ cat /tmp/test.html
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Test av &aring;&auml;&ouml;</title>
</head>
<body>
<h1>Test av &aring;&auml;&ouml;</h1>
Det h&auml;r &auml;r en testfil f&ouml;r
mitt lilla program "&aring;&auml;&ouml;"!
</body>
</html>
$
Ett visst mått av felhantering finns i programmet:$ åäö bellmna
Filen bellmna finns inte
$ åäö bellman lenngren
Ange exakt ett argument
$

   
11. Grundläggande Bashprogrammering

11.1 Villkor

När ett program kört färdigt berättar det ifall körningen var lyckad eller misslyckad. Vad det innebär att körningen lyckats varierar från program till program, men oftast innebär ett misslyckande att någon form av fel uppstått. Så hur fungerar det hela? Jo, programmet lämnar ifrån sig ett tal, som kallas dess slutstatus, då det avslutas. Om talet är 0, så anses körningen vara lyckad. Om talet är större än 0, så anses körningen ha misslyckats. Tex kan slutstatus 1 markera en viss typ av fel och slutstatus 2 ett annat slags fel.

I Bash anger parametern ? slutstatus för det senaste kommandot man gett.$ rm kopia.980217.bellmna
rm: kopia.980217.bellmna: Filen eller katalogen finns inte
$ echo $?
1
$ rm kopia.980217.bellman
$ echo $?
0
$
Vid första rm-kommandot ovan uppstod ett fel, och slutstatus var 1. Nästa gång gick det bra, och slutstatus var 0.

Kommandot exit 0 avslutar ett Bashprogram med slutstatus 0. För att ge slutstatus 1 skriver man i stället exit 1 osv. Om siffran utelämnas, så avslutas programmet med samma slutstatus som det senast utförda kommandot.

Kommandot test kan undersöka olika slags villkor. Om villkoret är uppfyllt lämnar test slutstatus 0, annars lämnas slutstatus 1. Tex kontrollerar test 5 -gt 7 ifall 5>7. Här står -gt för ''greater than'' (se tabell 11.4). Som synonym för test kan man använda tecknet [, och då skriver man [ 5 -gt 7 ] istället.$ test 5 -gt 7
$ echo $?
1
$ test 7 -gt 5
$ echo $?
0
$ [ 1 -gt -2 ]
$ echo $?
0
$
Detta innebär att villkoren 7>5 och 1>-2 är uppfyllda, men inte 5>7.

Med hjälp av en if-sats kan man få ett program att vidta olika åtgärder beroende på om ett villkor är uppfyllt eller inte. Syntaxen är

if testkommando
then kommando
fi
Först körs testkommando. Om det lyckades (dvs om det har slutstatus 0) så utförs kommandot kommando. Man kan ha fler radbrytningar om man vill, Bash vet att kommandot inte är komplett förrän man skrivit fi. Det är också möjligt att skriva kommandot på en rad, men då måste man använda semikolon i stället för radbrytning.$ if [ 5 -gt 7 ] ; then echo Hej ; fi
$ if [ 7 -gt 5 ] ; then echo Hej ; fi
Hej
$
I det första fallet ovan var testkommandot lyckat, i det andra fallet var det misslyckat. Kommandot echo Hej utfördes bara i det andra fallet.

Om man vill, så kan man ha raden ''else alternativkommando'' före fi ovan; i så fall utförs alternativkommando ifall testkommandot misslyckades. Dvs if kan användas så här också:

if testkommando
then kommando
else alternativkommando
fi

Vi ändrar vårt program argument till följande:

#!/bin/bash
echo "Totalt $# argument."
if [ $# -gt 12 ] ; then
  echo "Det trettonde argumentet är ${13}."
else
  echo "Det finns inget trettonde argument." 1>&2
  exit 1
fi

Om antalet argument är större än 12, så skrivs det trettonde argumentet ut. Annars skrivs ett felmeddelande ut och programmet avslutas med slutstatus 1.$ argument a b c d e f g h i j k l m n o p q r
Totalt 18 argument.
Det trettonde argumentet är m.
$ echo $?
0
$ argument esike desike luntan tuntan
Totalt 4 argument.
Det finns inget trettonde argument.
$ echo $?
1
$

I tabellerna 11.1, 11.2, 11.3, 11.4 och 11.5 ges en förteckning av de viktigaste testerna som test eller [ kan utföra.

 
Tabell 11.1: Test av filtyp
 
Sats Uppfylld om...
-e fil fil existerar
-f fil fil existerar och är en vanlig fil
-d fil fil existerar och är en katalog
-L fil fil existerar och är en symbolisk länk
-b fil fil existerar och är en blockspecialfil
-c fil fil existerar och är en teckenspecialfil
-p fil fil existerar och är en FIFO
-S fil fil existerar och är en socket


 
Tabell 11.2: Test av filattribut
 
Sats Uppfylld om...
-r fil fil existerar och är läsbar
-w fil fil existerar och är skrivbar
-x fil fil existerar och är exekverbar
-u fil fil existerar och är setuid
-g fil fil existerar och är setgid
-g fil fil existerar och har fastbiten satt
-s fil fil existerar och är icke-tom
fil1 -nt fil2 fil1 är nyare än fil2 med avseende på senaste ändring
fil1 -ot fil2 fil1 är äldre än fil2 med avseende på senaste ändring


 
Tabell: Tester på textsträngar
 
Sats Uppfylld om...
-z sträng längden av sträng är noll
-n sträng längden av sträng inte är noll
sträng längden av sträng inte är noll
sträng1 = sträng2 sträng1 är identisk med sträng2
sträng1 != sträng2 sträng1 inte är identisk med sträng2
sträng1 < sträng2 sträng1 kommer före sträng2 i bokstavsordning
sträng1 > sträng2 sträng1 kommer efter sträng2 i bokstavsordning


 
Tabell: Tester på heltal
 
Sats Uppfylld om...
tal1 -eq tal2 tal1 är lika med tal2
tal1 -ne tal2 tal1 inte är lika med tal2
tal1 -lt tal2 tal1 är mindre än tal2
tal1 -gt tal2 tal1 är större än tal2
tal1 -le tal2 tal1 är mindre än eller lika med tal2
tal1 -ge tal2 tal1 är större än eller lika med tal2


 
Tabell: Tester på villkorssatser
 
Sats Uppfylld om...
! sats sats inte är uppfylld
sats1 -a sats2 sats1 och sats2 är uppfyllda
sats1 -o sats2 sats1 och/eller sats2 är uppfylld

Bash kan skilja mellan olika fall beroende på värdet av en variabel med hjälp av kommandot case. Programmet genus nedan ger ett exempel.

#!/bin/bash
case $1 in
  Anna | Eva | Åsa) echo "$1 är en kvinna." ;;
  Kalle | Nisse | Pelle) echo "$1 är en man." ;;
  * ) echo "Jag vet inte huruvida $1 är en kvinna eller en man." ;;
esac

Så här fungerar programmet:$ genus Eva
Eva är en kvinna.
$ genus Nisse
Nisse är en man.
$ genus Sofia
Jag vet inte huruvida Sofia är en kvinna eller en man.
$
Syntaxen för kommandot case är

case ord in
val | val ...| val ) kommandon ;;
val | val ...| val ) kommandon ;;
...
esac
Mellan case och esac (som är ''case'' baklänges, liksom ''fi'' är ''if'' baklänges) anges ett eller flera fall, separerade av dubbla semikolon. I varje sådant fall får man ange ett eller flera val, separerade av lodräta streck, samt därefter en högerparentes och ett godtyckligt antal kommandon som ska utföras ifall ord är något av dessa val. Bara kommandona från det första fallet där ord förekommer utförs. Liksom filnamn får valen innehålla jokertecken. Det är därför som valet * i programmet genus matchar allt som inte matchas av något av de andra valen.

   
11.2 Upprepningar

Ofta vill man att ett kommando ska utföras flera gånger beroende på vissa villkor. Bash erbjuder några olika möjligheter till sådana upprepningar. Ett alternativ är for, vars syntax ser ut så här:

for variabel in argumentlista
do kommando
done
(Man får som vanligt använda semikolon i stället för radbrytning.) För varje argument i argumentlista antar variabeln variabel detta argument som värde under det att kommando utförs. Här är ett exempel:$ for i in esike desike luntan tuntan
> do echo "Nu har 'i' värdet $i"
> done
Nu har 'i' värdet esike
Nu har 'i' värdet desike
Nu har 'i' värdet luntan
Nu har 'i' värdet tuntan
$

Vill man att variabel ska genomlöpa en följd av tal, så kan man låta seq generera talföljden. Instruktionen seq 5 skriver ut talen från 1 till 5, separerade av nyrad-tecken:$ seq 5
1
2
3
4
5
$
Men hjälp av flaggan -s kan man ange en annan separator:$ seq -s '+' 5
1+2+3+4+5
$
Följden behöver inte börja på 1:$ seq -s ' ' 3 12
3 4 5 6 7 8 9 10 11 12
$
Så här genereras vart sjunde tal mellan 2 och 50:$ seq -s ',' 2 7 50
2,9,16,23,30,37,44
$
Vi använder nu seq i en for-sats:$ ls
Kapitel40A/ bellman lenngren
$ for i in $(seq 4) ; do cp bellman epistel$i.text ; done
$ ls
Kapitel40A/    epistel1.text  epistel3.text  lenngren
bellman        epistel2.text  epistel4.text
$
Filen bellman kopierades till fyra nya filer. Innan for-satsen utfördes skrevs $(seq 4) om till talen från 1 till 4, se avsnitt 6.5. (Skalet åt upp de nyrad-tecken som genererades av seq, se [*].)

När man använder for-satser i Bash-program vill man ofta att variabeln ska genomlöpa varje argument programmet anropades med. Om ''in text'' utelämnas, så sker just detta. Vi ger nu ett program som kopierar var och en av de filer man anger som argument.

#!/bin/bash
for FIL
do
  cp $FIL $FIL.kopia
done

Vi antar att programmet heter kopiera.$ kopiera epistel[3-5].text
$ ls
Kapitel40A/          epistel2.text        epistel4.text
bellman              epistel3.text        epistel4.text.kopia
epistel1.text        epistel3.text.kopia  lenngren
$
Ovan har vi använt jokertecken för att matcha två filnamn, som blir argument till programmet kopiera. I programmet genomlöper variabeln FIL de båda filnamnen. Lägg märke till att det är Bash som tar hand om jokertecknen, vårt program har ingen aning om deras existens!

Ett annat sätt att upprepa kommandon är while-satsen. Dess syntax är

while testkommando
do kommando
done
Först körs testkommando. Om det lyckades, så körs kommando och därefter upprepas det hela. Om testkommando misslyckas, dvs om dess slutstatus är skild från 0, så körs inte kommando utan programmet fortsätter efter done.

En variant av while är until. Dess syntax är

until testkommando
do kommando
done
Här körs först testkommando. Om det misslyckas, så körs kommando och det hela upprepas. Om testkommando lyckas, så avslutas until och programmet fortsätter efter done.

11.3 Fler villkor

Som testkommando i if-, until- och while-konstruktioner kan man ta vilket kommando som helst, det behöver inte vara test. Testet betraktas som uppfyllt om programkörningen lyckades, dvs om dess slutstatus är 0. För tex grep är körningen lyckad om söktexten påträffades, annars misslyckad. Här är ett exempel:

#!/bin/bash
if grep rätt $1 ; then
  echo Ordet \"rätt\" finns i filen $1
else
  echo Ordet \"rätt\" finns inte i filen $1
fi

Programmet undersöker om ordet ''rätt'' finns i en fil vars namn anges med ett argument.$ leta bellman
Ordet "rätt" finns inte i filen bellman
$ leta lenngren
och fann likören rätt begärlig.
Ordet "rätt" finns i filen lenngren
$
Ifall man vill slippa utskriften kan man skicka den till /dev/null; då raderas den helt enkelt. Men i det här fallet är det enklare att ge grep flaggan -q, som anger att man inte vill ha någon utskrift.

Härnäst vill vi köra en fil genom programmet LATEX upprepade gånger tills filen är färdigbehandlad. LATEX skriver ut texten ''Rerun to get cross-references right.'' ifall filen måste köras igen. Följande program löser problemet:

#!/bin/bash
echo 'Kör LaTeX...'
while latex gnulinux.tex | grep 'Rerun to get cross-references right.'
do
  echo 'OK, kör LaTeX igen...'
done

Slutstatus för en rörledning är lika med det sista kommandots slutstatus, så villkoret i while-kommandot ovan är uppfyllt om grep finner den sökta texten.

Om man ger två kommandon separerade av &&, så utförs det andra kommandot om och endast om det första lyckades. Om kommandona istället separeras av ||, så utförs det andra kommandot om och endast om det första misslyckades. Motsvarande funktionalitet kan lätt fås med hjälp av if-satser, men ibland blir dessa konstruktioner mer lättlästa.$ [ -x /bin/date ] && echo Ja
Ja
$ [ -x bellman ] && echo Ja
$ grep Gnu bellman || echo 'Ordet Gnu finns inte i filen'
Ordet Gnu finns inte i filen
$ [ 7 -gt 43 ] && echo Större || echo Mindre
Mindre
$
Det första kommandot ovan undersöker ifall /bin/date är ett körbart program, och skriver i så fall ut ordet ''Ja''. Det andra kommandot gör motsvarande med filen bellman, som dock inte är ett körbart program. Det tredje kommandot söker först efter ordet ''Gnu'' i filen bellman, och eftersom sökningen misslyckades utfördes kommandot echo som skrev ut ett meddelande. Det sista kommandot visar en motsvarighet till if-then-else.

Det finns ännu en variant av if: efter then kommando, och före ett eventuellt else, kan man en eller flera gånger ha konstruktionen

elif nyttestkommando
then nyttkommando
Här är elif ungefär en sammanslagning av else if. Om föregående testkommando misslyckades, så körs nyttestkommando. Ifall detta lyckas, så körs nyttkommando och if-satsen avslutas; annars hoppas nyttkommando över och programmet fortsätter vid nästa elif eller else (om det förekommer något sådant före fi). Ett exempel:
#!/bin/bash
if [ $1 -lt 0 ] ; then
  echo 'Talet är negativt!'
elif [ $1 -lt 10 ] ; then
  echo 'Talet är ensiffrigt.'
elif [ $1 -lt 100 ] ; then
  echo 'Talet är tvåsiffrigt.'
else
  echo 'Talet har fler än två siffror.'
fi

Programmet tar ett heltal som argument, och svarar att talet är negativt eller att det har en, två eller fler siffror. Så här fungerar programmet:$ iftest 43
Talet är tvåsiffrigt.
$ iftest -3
Talet är negativt!
$ iftest 32521
Talet har fler än två siffror.
$ iftest 0
Talet är ensiffrigt.
$

11.4 Hopp

Vi ska nu skriva ett program som raderar filer. Programmet ska fråga efter bekräftelse för varje fil. I programmet använder vi Bashkommandona break och continue. Det förra får Bash att hoppa till kommandot efter done i en while-konstruktion, det senare får Bash att hoppa till kommandot done (men inte förbi det). Så här ser programmet radera ut:

#!/bin/bash
while [ $# -ne 0 ] ; do
  read -p "Radera $1 (j/n/s)? " SVAR
  case "$SVAR" in
    [Jj] | [Jj]a | JA) rm "$1" ;;
    [Nn] | [Nn]ej | NEJ) ;;
    [Ss]) break ;;
       *) echo 'Felaktigt svar, försök igen!' ; continue ;;
  esac
  shift
done

Låt oss prova programmet innan vi tittar närmare på det.$ ls
Kapitel40A/          epistel2.text        epistel4.text
bellman              epistel3.text        epistel4.text.kopia
epistel1.text        epistel3.text.kopia  lenngren
$ radera epistel*
Radera epistel1.text (j/n/s)? j
Radera epistel2.text (j/n/s)? nej
Radera epistel3.text (j/n/s)? nja
Felaktigt svar, försök igen!
Radera epistel3.text (j/n/s)? Ja
Radera epistel3.text.kopia (j/n/s)? ja
Radera epistel4.text (j/n/s)? s
$ ls
Kapitel40A/          epistel2.text        epistel4.text.kopia
bellman              epistel4.text        lenngren
$
För varje fil har man tre val: att radera den, att inte radera den samt att avsluta. Om man skriver j (eller J eller ja eller Ja eller JA), så raderas filen. Om man skriver s eller S, så utförs kommandot break som får Bash att hoppa till efter done varvid programmet avslutas. Om man skriver ett ogiltigt svar, så får kommandot continue Bash att gå tillbaka till do utan att shift utförs, och man får då en ny fråga om samma fil.

Man kan använda break och continue i for-satser, som följande exempel visar:$ cat exempel
#!/bin/bash
for i in $(seq 10 -1 1)
do
[ $i -eq 8 ] && continue
echo "Nu har 'i' värdet $i"
[ $i -eq 6 ] && break
done
$ exempel
Nu har 'i' värdet 10
Nu har 'i' värdet 9
Nu har 'i' värdet 7
Nu har 'i' värdet 6
$

11.5 Att hantera argument

Om man använder kommandot shift i ett Bashprogram, så tas det första argumentet bort. Det som förut var $2 blir då $1, det som var $3 blir $2 osv. Samtidigt minskar $# med ett. Kommandot shift 2 har samma verkan som två stycken shift. Dvs $# minskar med två, det som var $3 blir $1, det som var $4 blir $2 osv. På motsvarande sätt fungerar shift 3, shift 4 osv.

Följande program, som helt enkelt anger alla sina argument, ger ett exempel på hur shift kan användas.

#!/bin/bash
echo "Totalt $# argument."
while [ $# -gt 0 ]
do
  echo "Ett argument är $1."
  shift
done

Genom att köra programmet ovan några gånger ska vi demonstrera hur Bash hanterar argumenten. Vi minns sedan avsnitt 3.4 att oskyddade blank- och nyradtecken separerar argument.$ allaargument ole 'dole doff'
Totalt 2 argument.
Ett argument är ole.
Ett argument är dole doff.
$ allaargument "Hej och hå"
Totalt 1 argument.
Ett argument är Hej och hå.
$
Med hjälp av citationstecken kan man till och med ange den tomma textsträngen som ett argument.$ allaargument Ett tomt "" argument
Totalt 4 argument.
Ett argument är Ett.
Ett argument är tomt.
Ett argument är .
Ett argument är argument.
$
Separationen av argument sker efter eventuella omskrivningar (med undantag för jokeromskrivningar):$ NAMN="Göran Andersson"
$ allaargument namnet är $NAMN
Totalt 4 argument.
Ett argument är namnet.
Ett argument är är.
Ett argument är Göran.
Ett argument är Andersson.
$
Ovan skrevs $NAMN om till Göran Andersson, vilket räknas som två skilda argument. Om variabeln namn inte har getts något värde, så skrivs $namn om till en tom textsträng. Därför räknas $ODEFINIERAD som noll argument i följande exempel:$ allaargument Värdet är $ODEFINIERAD
Totalt 2 argument.
Ett argument är Värdet.
Ett argument är är.
$
Ibland är detta tvärtemot vad man vill. Hur får man Bash att uppfatta uttrycket $variabel som exakt ett argument även efter omskrivningen? Genom att omge det med citationstecken!$ allaargument namnet är "$NAMN"
Totalt 3 argument.
Ett argument är namnet.
Ett argument är är.
Ett argument är Göran Andersson.
$ allaargument Värdet är "$ODEFINIERAD"
Totalt 3 argument.
Ett argument är Värdet.
Ett argument är är.
Ett argument är .
$

 Om man slarvar vid hanteringen av argument, så kan man råka ut för märkliga spratt. Betrakta tex följande program, leta2:

#!/bin/bash
if grep -q $1 $2 ; then
  echo 'Ja, texten finns i filen.'
else
  echo 'Nej, texten finns inte i filen.'
fi

Det första argumentet ska vara en text, det andra ett filnamn. Programmet har till uppgift att ta reda på om texten finns i den angivna filen. Vi har gett grep flaggan -q för att slippa dess utskrift. Antag att filen textfil har följande innehåll:

Det första argumentet ska vara en text, det andra ett filnamn.
Programmet har till uppgift att
ta reda på om texten finns i den angivna filen.
Så här kan det gå när vi kör programmet leta2:$ leta2 'finns inte' textfil
Ja, texten finns i filen.
$
Nu påstår alltså programmet att texten ''finns inte'' förekommer i textfil! Orsaken till detta är att kommandot grep -q $1 $2 skrivs om till grep -q finns inte textfil. Alltså söker grep efter ordet ''finns'' i filerna inte och textfil. Ordet ''finns'' förekommer i filen textfil, så grep får slutstatus noll.

För att rätta till felet måste vi se till att grep betraktar värdet av $1 som exakt ett ord. Vi omger därför $1 med citationstecken. Hur går det om det andra argumentet, filnamnet, innehåller ett mellanslag? Dåligt. Värdet av $2 betraktas då av grep som flera filnamn, och programmet gör fel. Så här ska det se ut i stället:$ cat leta3
#!/bin/bash
if grep -q "$1" "$2" ; then
echo 'Ja, texten finns i filen.'
else
echo 'Nej, texten finns inte i filen.'
fi
$ cp textfil 'konstig fil'
$ leta3 finns 'konstig fil'
Ja, texten finns i filen.
$ leta3 'finns inte' 'konstig fil'
Nej, texten finns inte i filen.
$

Antag nu att vi vill skriva om leta3 så att det kan söka efter en viss text i flera olika filer. Dvs det första argumentet ska vara söktexten, de övriga ska vara filnamn. Då ska grep-kommandot ändras till grep -q "$1" "$2" "$3" ifall man anger två filnamn, till grep -q "$1" "$2" "$3" "$4" ifall man anger tre osv. Hur ska det gå till när vi inte vet hur många argumenten är? Bash tolkar $* som $1 $2 $3 osv fram till sista argumentet, men det duger inte här eftersom argumenten kan innehålla mellanslag. Istället ska vi använda parametern @. Den har samma värde som * utom när den omges av citationstecken: "$@" tolkas som "$1" "$2" "$3" osv, vilket är just vad vi behöver. Kommandot ska alltså skrivas grep -q "$@".

Vi använder följande program för att belysa diskussionen ovan:

#!/bin/bash
echo "Jag fick $# argument, men..."
allaargument $*
allaargument "$*"
allaargument "$@"

Så här går det när vi kör programmet:$ argumenttest ole 'dole doff'
Jag fick 2 argument, men...
Totalt 3 argument.
Ett argument är ole.
Ett argument är dole.
Ett argument är doff.
Totalt 1 argument.
Ett argument är ole dole doff.
Totalt 2 argument.
Ett argument är ole.
Ett argument är dole doff.
$
Programmet allaargument anropades tre gånger. Varje gång vidarebefordrades argumenten, fast det skedde på olika sätt. Först vidarebefordrades de som ole dole doff (allaargument fick då tre argument), sedan som "ole dole doff" (allaargument fick då ett argument) och till sist som "ole" "dole doff" (vilket gav två argument). Det är bara "$@" som bevarar argumenten precis som de var.

Man kan inte ge tex parametern 1 ett nytt värde genom en direkt tilldelning som om det varit en vanlig variabel. Däremot kan ett program ge sig själv de nya argumenten argument med kommandot set argument. Följande program använder set:

#!/bin/bash
allaargument "$@"
set Många "nya, fina" argument
allaargument "$@"

Låt oss prova programmet!$ argumenttest2 hejsan svejsan
Totalt 2 argument.
Ett argument är hejsan.
Ett argument är svejsan.
Totalt 3 argument.
Ett argument är Många.
Ett argument är nya, fina.
Ett argument är argument.
$

Programmet filinformation nedan använder set på ett intressantare sätt:

#!/bin/bash
set $(wc "$1")
echo "Filen $4 har $1 rader och är $3 byte lång."

Som argument ska man ange ett filnamn. Programmet talar om hur många rader och tecken filen innehåller.$ wc /etc/passwd
30 42 1291 /etc/passwd
$ filinformation /etc/passwd
Filen /etc/passwd har 30 rader och är 1291 byte lång.
$
Utskriften från wc $1 har fyra delar: först antal rader, sedan antal ord respektive tecken och till sist filnamnet. Dessa fyra delar sätts som argument, så att de kan plockas ut med $1, $2 osv.

11.6 Strängoperatorer

Som bekant erhålls värdet av variabel då man skriver ${variabel}. Vi ska nu beskriva ett antal strängoperatorer som kan manipulera de värden som erhålls.

Uttrycket ${variabel:-sträng} skrivs om till värdet av variabel om det är definierat och inte tomt, annars till sträng. På detta sätt kan man ha ett värde i reserv när en variabel saknar värde.$ KAND=hej
$ echo ${KAND:-adjö}
hej
$ echo ${OKAND:-adjö}
adjö
$
Reservvärdet adjö användes vid det andra tillfället ovan eftersom variabeln OKAND är odefinierad. Operatorn :- förändrar inte variabelns ursprungliga värde:$ echo $OKAND,$KAND
,hej
$
Operatorn := fungerar som :- bortsett från att variabeln variabel sätts lika med sträng ifall den saknar värde.$ echo ${KAND:=adjö}
hej
$ echo ${OKAND:=adjö}
adjö
$ echo $OKAND,$KAND
adjö,hej
$

Uttrycket ${variabel:?sträng} skrivs om till värdet av variabel om det är definierat och inte tomt, annars skrivs sträng ut som ett felmeddelande (och programkörningen avbryts om detta inträffar i ett program).$ echo "Säg ${KAND:?Värde saknas} till Göran."
Säg hej till Göran.
$ OKAND=
$ echo "Säg ${OKAND:?Värde saknas} till Göran."
bash: OKAND: Värde saknas
$

Uttrycket ${variabel:+sträng} tas bort ifall variabel saknar värde, annars skrivs det om till sträng:$ echo "Värdet är ${KAND:+inte }tomt."
Värdet är inte tomt.
$ echo "Värdet är ${OKAND:+inte }tomt."
Värdet är tomt.
$

För var och en av operatorerna :-, :=, :? och :+ finns en motsvarande operator som skrivs utan kolon. Dessa operatorer undersöker bara om variabeln är odefinierad, inte om värdet är tomt.$ echo "Variabeln är ${OKAND+inte }odefinierad."
Variabeln är inte odefinierad.
$ echo "Variabeln är ${ODEFINIERAD+inte }odefinierad."
Variabeln är odefinierad.
$
Ovan är variabeln OKAND definierad (dess värde är den tomma strängen), medan variabeln ODEFINIERAD inte är definierad.

Uttrycket ${#variabel} skrivs om till antalet tecken i värdet av variabel.$ ORD=skalprogrammering
$ echo ${#ORD}
17
$

Uttrycket ${variabel: n} skrivs om till värdet av variabel men med de första n tecknen borttagna:$ echo ${ORD: 4}
programmering
$
Så här anger man att man vill behålla högst sju respektive högst 20 stycken tecken:$ echo ${ORD: 4: 7}
program
$ echo ${ORD: 4: 20}
programmering
$
Så här väljer vi ut de fyra sista tecknen:$ echo ${ORD: -4}
ring
$
Blanktecknet före talet -4 är viktigt, för annars skulle det förväxlas med operatorn :-.$ echo ${ORD:-4}
skalprogrammering
$
Härnäst vill vi behålla högst tre av de sex sista tecknen i värdet av variabeln ORD:$ echo ${ORD: -6: 3}
mer
$

Uttrycket ${variabel/mönster/sträng} skrivs om till värdet av variabel men med den första, längsta möjliga förekomsten av mönster utbytt mot sträng. Här tolkas mönster som ett mönster precis som när Bash matchar filnamn. I följande exempel byter vi ut den första förekomsten av a mot A:$ echo ${ORD/a/A}
skAlprogrammering
$
Uttrycket ${variabel//mönster/sträng} fungerar som det ovan bortsett från att varje förekomst av mönster byts mot sträng. Så här kan vi alltså ta bort alla vokaler:$ echo ${ORD//[aouåeiyäö]/}
sklprgrmmrng
$

Uttrycket ${variabel#mönster} innebär att den kortaste möjliga förekomsten av mönster i början av värdet av variabel ska tas bort. Så här stryker vi allt fram till det första blanktecknet:$ MENING='Hej, jag heter Göran!'
$ echo ${MENING#* }
jag heter Göran!
$
Operatorn ## fungerar som # bortsett från att det i stället är den längsta möjliga förekomsten av mönster som tas bort:$ echo ${MENING##* }
Göran!
$
Operatorerna % och %% fungerar som # respektive ## bortsett från att man stryker från slutet i stället. Så här kan man alltså byta ändelsen i ett filnamn:$ FIL=uppsats.tex
$ echo ${FIL%.*}.dvi
uppsats.dvi
$
Så här stryker vi allt som kommer efter det inledande decimaltalet:$ FORMEL=3.14+1.41-2.72
$ echo ${FORMEL%%[^0-9.]*}
3.14
$

11.7 Aritmetik

Ordet ''aritmetik'' betyder helt enkelt räkning. I detta avsnitt beskrivs det hur man kan utföra enklare beräkningar med hjälp av Bash.

Ett uttryck av formen $((formel)) skrivs om till det uträknade värdet av formel. Formlerna som beräknas får bland annat innehålla de fyra vanliga räknesätten, som skrivs med operatorerna + (addition), - (subtraktion), * (multiplikation) och / (division).$ echo $((17+23))
40
$ mkdir Kapitel$((17+23))A
$ ls
Kapitel40A/          kopia.980217.bellman
bellman               lenngren
$
Ingen beräkning sker ifall tecknen $(( och )) utelämnas:$ echo 17+23
17+23
$
Man får också använda variabler:$ x=17; y=23
$ echo $((x+y))
40
$
Lägg märke till att man inte behöver skriva dollartecken före variablernas namn när de som ovan förekommer i en beräkning.

Med hjälp av while kan man låta en variabel genomlöpa en följd av tal, som programmet loop nedan visar.

#!/bin/bash
i=5
while [ $i -le 10 ]
do
  echo "Nu har 'i' värdet $i"
  i=$((i+1))
done

Utskriften från loop ser ut så här:$ loop
Nu har 'i' värdet 5
Nu har 'i' värdet 6
Nu har 'i' värdet 7
Nu har 'i' värdet 8
Nu har 'i' värdet 9
Nu har 'i' värdet 10
$

Multiplikationen i formeln 2+3*4 utförs före additionen eftersom det är en konvention att multiplikation och division har högre prioritet än addition och subtraktion. Om man vill att additionen ska utföras först måste man ange parenteser, (2+3)*4=20.$ echo Man räknar lätt ut att 2+3*4=$((2+3*4))
Man räknar lätt ut att 2+3*4=14
$ echo $(( (2+3)*4 ))
20
$

En förteckning av de operatorer som kan användas vid beräkningar i Bash ges i tabell 11.6. Operatorerna är grupperade i prioritetsordning.


 
Tabell 11.6: De aritmetiska operatorerna.
 
Operator Typ
+ - unär
! ~ unär
* / % binär
+ - binär
<< >> binär
< <= > >= binär
== != binär
& binär
^ binär
| binär
&& binär
|| binär
?...: ternär
= += -= *= /= %= <<= >>= &= |= ^= binär

Bash räknar bara med heltal. Detta innebär i synnerhet att om resultatet vid en division inte går jämnt ut, så stryks decimalerna från svaret. Till exempel gäller i Bash att 13/5 är lika med 2. Om 13 hästar ska delas ut till fem systrar, så får systrarna två hästar var. Kvoten är därmed 2. Det blir tre hästar över, och man säger att resten (dvs det antal som blev över) är 3.$ echo $(( 13/5 ))
2
$
Resten vid en division kan beräknas med hjälp av operatorn %.$ echo $(( 13%5 ))
3
$

Bash kan bara hantera heltal av en begränsad storlek; vanligtvis får talen ha högst 9-10 siffror om resultatet ska bli rätt.$ echo $(( 1000*1000 ))
1000000
$ echo $(( 1000000*1000000 ))
-727379968
$
Det korrekta resultatet vid den andra beräkningen ovan är ett tal med 13 siffror, men eftersom Bash (på min dator) inte kan hantera så stora tal gavs ett fullständigt felaktigt resultat.

Om man omger ett heltal med tecknen (( respektive )), så undersöker Bash ifall talet är skilt från noll. Lyckad slutstatus anger att talet var skilt från noll, misslyckad slutstatus att talet var lika med noll.$ (( 5 ))
$ echo $?
0
$ (( 2*3-6 ))
$ echo $?
1
$
Som vi snart ska se kan detta användas som ett alternativ till kommandot test vid jämförelser av heltal.


 
Tabell: Jämförelser.
 
Jämförelse  Uppfylld om...
tal1 < tal2   tal1 är mindre än tal2
tal1 <= tal2   tal1 är mindre än eller lika med tal2
tal1 == tal2   tal1 är lika med tal2
tal1 != tal2   tal1 inte är lika med tal2
tal1 >= tal2   tal1 är större än eller lika med tal2
tal1 > tal2   tal1 är större än tal2

Tabell 11.7 visar hur man kan jämföra två heltal i Bash. Lägg särskilt märke till att likhet skrivs med två likhetstecken eftersom det annars skulle bli en tilldelning. Resultatet av en jämförelse är ett heltal, nämligen 1 (som betyder ''sant'') ifall jämförelsen var uppfylld, annars 0 (dvs ''falskt''). Jämförelsen a==b betraktas alltså som en beräkning vars värde är 1 ifall a=b; värdet är 0 om $a\neq b$.$ echo $(( 5>3 ))
1
$ echo $(( 2*4>=9 ))
0
$
Om man utelämnar dollartecknen ovan, så får man tester i stället för beräkningar:$ if (( 1+1==2 )) ; then echo 'Bash kan räkna!' ; fi
Bash kan räkna!
$

Variabeln RANDOM, som är inbyggd i Bash, ger ett slumptal mellan 0 och 32767.$ echo $RANDOM
12281
$ echo $RANDOM
31550
$ echo $RANDOM
3487
$
Om vi vill ha ett slumptal mellan tex 0 och 100, så kan vi först låta Bash ge oss ett slumptal mellan 0 och 32767, och sedan ta resten vid division av detta tal med 101:$ echo $(( $RANDOM%101 ))
37
$
För att simulera ett tärningskast behöver vi ett slumptal mellan 1 och 6. Vi beräknar då först ett slumptal mellan 0 och 5, och adderar sedan 1 till detta:$ echo $(( $RANDOM%6+1 ))
4
$
Programmet räkna nedan beräknar två slumptal mellan 1 och 10. Programmet frågar efter summan, och det ger sig inte förrän man har svarat rätt.  

#!/bin/bash
TAL1=$(( $RANDOM%10+1 ))
TAL2=$(( $RANDOM%10+1 ))
read -p "Vad är $TAL1+$TAL2? " SVAR
until (( SVAR == TAL1+TAL2 ))
do
  echo 'Fel fel fel!'
  read -p "Ange $TAL1+$TAL2 igen: " SVAR
done
echo 'Rätt svar!'

Om det svar man matar in är fel, så skrivs texten ''Fel fel fel!'' ut, och man får sedan göra ett nytt försök.$ räkna
Vad är 5+7? 8
Fel fel fel!
Ange 5+7 igen: 12
Rätt svar!
$ räkna
Vad är 1+1? 2
Rätt svar!
$
Vid den andra körningen fick vi ett lätt problem, 1+1, och lyckades därmed svara rätt första gången. Därför kördes aldrig kommandona mellan do och done.

Instruktionen declare -i namn anger att variabeln namn är ett heltal. Fördelen med detta är att värdet av namn då beräknas automatiskt, dvs även om tecknen $(( och )) utelämnas. I följande program, som visar ännu ett sätt att låta en heltalsvariabel genomlöpa ett antal värden, har en variabel markerats som heltal:

#!/bin/bash
declare -i tal=1
echo -n 'Några tal:'
while (( tal<=10 )) ; do
  echo -n " $tal"
  tal=tal+1
done
echo '.'

Utskriften från programmet blir

Några tal: 1 2 3 4 5 6 7 8 9 10.

Operatorn += är en kombination av addition och tilldelning. Till exempel har a+=3 värdet a+3, och som en sidoeffekt av beräkningen a+=3 får a värdet a+3. Operatorerna -=, *= osv fungerar på motsvarande sätt. Programmet ovan kan alltså skrivas så här istället:

#!/bin/bash
declare -i tal=0
echo -n 'Några tal:'
while (( (tal+=1)<=10 )) ; do
  echo -n " $tal"
done
echo '.'

Oftast är den förra versionen av programmet att föredra eftersom den senare versionen är mer svårläst.

12. Avancerad Bashprogrammering

12.1 Fält

Bash kan hantera så kallade fält. Ett fält är en slags variabel som kan innehålla många olika värden. Värdena skiljs åt med hjälp av olika nummer som kallas för deras index. Index kan vara allt från 0 och uppåt. Kommandot fält[n]=värde lagrar värdet värde i fältet fält under index n. Vi ger ett exempel:$ MANAD[3]=mars
$
Nu har vi lagrat värdet mars i fälet MANAD och gett det index 3. För att få tillbaka värdet gör man så här:$ echo Nu är det ${MANAD[3]}
Nu är det mars
$
För att referera till värdet med index n i fältet fält skriver man alltså ${fält[n]}.

Låt oss använda ett annat index i samma fält:$ MANAD[4]=april
$
Nu finns de båda värdena mars och april lagrade under index 3 respektive 4 i fältet MANAD.$ echo Efter ${MANAD[3]} kommer ${MANAD[4]}
Efter mars kommer april
$

Man kan tilldela flera fältvärden samtidigt på följande sätt:$ VECKODAG=(måndag tisdag onsdag torsdag fredag lördag söndag)
$
Det första värdet inom parenteserna får index 0, det andra index 1 osv.$ echo Vilodagen heter ${VECKODAG[6]}
Vilodagen heter söndag
$
Om man skriver * eller @ som index, så får man alla värdena i fältet:$ echo ${VECKODAG[*]}
måndag tisdag onsdag torsdag fredag lördag söndag
$
(Detta är analogt med parametrarna * och @, som står för samtliga argument till ett Bashprogram. Citationstecknen har motsvarande verkan i fallet med fält.)

Man kan ange index då flera fältvärden tilldelas samtidigt:$ MANAD=([1]=januari februari [5]=maj juni [12]=december)
$
Värdet januari lagras under index 1. Vi gav inget index för februari ovan, så det lagras under det följande indexnumret, nämligen 2. Värdena maj och juni lagras under index 5 respektive 6. Slutligen lagras december under index 12.$ echo ${MANAD[6]}
juni
$ allaargument "${MANAD[@]}"
Totalt 7 argument.
Ett argument är januari.
Ett argument är februari.
Ett argument är mars.
Ett argument är april.
Ett argument är maj.
Ett argument är juni.
Ett argument är december.
$

Här är ett enkelt program som använder fält:

#!/bin/bash
TAL=([1]=ett två tre fyra fem sex sju åtta nio tio)
echo ${TAL[$1]}

Programmet tar emot ett argument, som ska vara ett tal mellan 1 och 10. Talet skrivs sedan ut med bokstäver:$ skrivtal 3
tre
$
Med hjälp av ett andra fält kan vi lära programmet att skriva ut alla tal upp till 99:

#!/bin/bash
ENTAL=([1]=ett två tre fyra fem sex sju åtta nio tio elva \
       tolv tretton fjorton femton sexton sjutton arton nitton)
TIOTAL=([2]=tjugo trettio fyrtio femtio sextio sjuttio åttio nittio)
if (( $1>19 )) ; then
  echo -n ${TIOTAL[$1/10]}
  set $(( $1%10 ))
fi
echo ${ENTAL[$1]}

Låt oss köra programmet en par gånger innan vi förklarar det:$ skrivtal 57
femtiosju
$ skrivtal 3
tre
$ skrivtal 14
fjorton
$
För att slippa mata in alla de 99 olika värdena i programmet har vi separerat tiotalssiffran från entalssiffran för alla tal från och med 20. När argumentet är 57 skrivs ordet ''femtio'' ut av det första echo-kommandot och ordet ''sju'' av det andra echo-kommandot. Flaggan -n anger att inget nyrad-tecken ska skrivas ut. Därför hamnar orden ''femtio'' och ''sju'' efter varandra på samma rad. Ordet ''femtio'' är värdet med index 5 i fältet TIOTAL. Som index anger vi $1/10, dvs första argumentet delat med 10. Detta avrundas nedåt till ett heltal, och ger därför talets första siffra (förutsatt att talet inte är större än 99). Lägg märke till att beräkningen av index sker automatiskt, dvs man behöver inte använda $((...)). När vi ''förbrukat'' första siffran kastar vi bort den genom att med ett set-kommando ändra första argumentet till det uträknade värdet av $1%10. Procenttecknet står för ''resten vid heltalsdivision'', och resten vid division med 10 är talets sista siffra.

På motsvarande sätt kan vi lära programmet att klara alla tal upp till 999:

#!/bin/bash
ENTAL=([1]=ett två tre fyra fem sex sju åtta nio tio elva \
       tolv tretton fjorton femton sexton sjutton arton nitton)
TIOTAL=([2]=tjugo trettio fyrtio femtio sextio sjuttio åttio nittio)
(( $1>99 )) && echo -n ${ENTAL[$1/100]}hundra && set $(( $1%100 ))
(( $1>19 )) && echo -n ${TIOTAL[$1/10]} && set $(( $1%10 ))
echo ${ENTAL[$1]}

Om talet är tresiffrigt, så skrivs första siffran ut följt av ordet ''hundra'', varefter bara de båda sista siffrorna behålls. Sedan fungerar programmet som tidigare, men det är något mer kompakt skrivet.$ skrivtal 88
åttioåtta
$ skrivtal 388
trehundraåttioåtta
$ skrivtal 701
sjuhundraett
$

12.2 Funktioner

Man kan göra delprogram, funktioner, i sina Bash-program. Här är ett exempel, en ny version av hejvärlden:

#!/bin/bash

function dekorera()
{
  echo '****************************************'
  echo '****************************************'
  echo '** Program: hejvärlden                **'
  echo '** Copyright (c) 1997 Göran Andersson **'
  echo '****************************************'
  echo '****************************************'
}

dekorera
echo "Hej, världen!"

Först definieras funktionen dekorera. Varje gång funktionen anropas i programmet utförs de kommandon som står mellan klamrarna. Ett anrop av funktionen går till precis som ett anrop av till exempel programmet date: man skriver helt enkelt dess namn. Den första raden i själva programmet anropar funktionen. Den andra och sista programraden skriver sedan ut texten ''Hej, världen!''.$ hejvärlden
****************************************
****************************************
** Program: hejvärlden                **
** Copyright (c) 1997 Göran Andersson **
****************************************
****************************************
Hej, världen!
$

Funktioner kan ta argument precis som vanliga program. De är därför mycket mer användbara än alias. I programmet filinformation nedan finns en funktion som tar fyra argument. Dessa argument är tänkta att vara utskriften från wc, så det fjärde argumentet är namnet på en fil medan de tre första är antalet rader, ord och tecken i filen.

#!/bin/bash
function information()
{
  echo "Filen $4 har $1 rader och är $3 byte lång."
}

for i ; do
  information $(wc "$i")
done

Programmet kör wc på varje fil vars namn finns bland dess argument. Utskriften från wc blir argument till funktionen information. I funktionen skrivs det ut hur många rader och tecken det finns i filen.$ filinformation bellman lenngren
Filen bellman har 12 rader och är 372 byte lång.
Filen lenngren har 19 rader och är 649 byte lång.
$

En funktion i ett skalprogram fungerar ungefär som vilket fristående kommando som helst. Skillnaden är att ingen ny process dras igång, funktionen tolkas av samma Bashprocess som tolkar de övriga delarna av programmet. Därför kan man tex använda samma variabler i funktionen som i de övriga delarna av programmet; om en variabel får ett nytt värde i funktionen så påverkas dess värde i huvudprogrammet, och om den nuvarande katalogen ändras med kommandot cd i funktionen, så påverkar denna ändring hela programmet.

Följande program gör samma sak som programmet räkna i avsnitt 11.2. Den har en funktion, skriv_svar, som tar en textsträng som argument. Funktionen skriver ut textsträngen som ett meddelande, och läser sedan in variabeln SVAR.

#!/bin/bash

function las_in_svar()
{
  read -p "$1" SVAR
}

TAL1=$(( $RANDOM%10+1 ))
TAL2=$(( $RANDOM%10+1 ))
las_in_svar "Vad är $TAL1+$TAL2? "
until (( SVAR == TAL1+TAL2 ))
do
  echo 'Fel fel fel!'
  las_in_svar "Ange $TAL1+$TAL2 igen: "
done
echo 'Rätt svar!'

Så här kan vi lära programmet skrivtal alla tal upp till en miljon:

#!/bin/bash

ENTAL=([1]=ett två tre fyra fem sex sju åtta nio tio elva \
       tolv tretton fjorton femton sexton sjutton arton nitton)
TIOTAL=([2]=tjugo trettio fyrtio femtio sextio sjuttio åttio nittio)

function tilltusen()
{
  (( $1>99 )) && echo -n ${ENTAL[$1/100]}hundra && set $(( $1%100 ))
(( $1>19 )) && echo -n ${TIOTAL[$1/10]} && set $(( $1%10 ))
echo -n ${ENTAL[$1]}
}

if (( $1>999 )) ; then
  tilltusen $(( $1/1000 ))
  echo -n tusen
fi
tilltusen $(( $1%1000 ))
echo

Den gamla versionen av skrivtal utgör nu funktionen tilltusen. Om talet har fler än tre siffror, så körs tilltusen med talet delat med 1000 som argument, och ordet ''tusen'' skrivs ut. Därefter körs tilltusen med talets tre sista siffror som argument.$ skrivtal 254388
tvåhundrafemtiofyratusentrehundraåttioåtta
$ skrivtal 7000
sjutusen
$

En funktion som anropar sig själv sägs vara rekursiv. Programmet träd nedan har en rekursiv funktion, kataloger. Programmet ritar ett diagram över underkatalogerna till en given katalog. Om ett argument ges till träd så används detta som startkatalog, annars startar träd i den nuvarande katalogen.

#!/bin/bash

function kataloger()
{
  local FIL KAT M N INDRAG
  declare -i N=0 M=1
  echo -n "$1"
  INDRAG="$2${1//?/ }"
  cd "$1"
  for FIL in * ; do 
    [ -d "$FIL" ] && KAT[N+=1]="$FIL"
  done
  if (( N==0 )) ; then
    echo
  elif (( N==1 )) ; then 
    echo -n '---' 
    kataloger "${KAT[1]}" "$INDRAG   "
  elif (( N>1 )) ; then
    echo -n '-+-'
    kataloger "${KAT[1]}" "$INDRAG | "
    while (( (M+=1)<N )) ; do
      echo -n "$INDRAG |-"
      kataloger "${KAT[M]}" "$INDRAG | "
    done
    echo -n "$INDRAG"' `-'
    kataloger "${KAT[N]}" "$INDRAG   "
  fi
  cd ..
}

kataloger "${1:-.}" ''

Funktionen ska anropas med två argument; det första anger startkatalogen och det andra är en textsträng som ska skrivas ut före varje katalognamn. Funktionen använder ett antal lokala variabler. En lokal variabel är unik för varje enskild körning av funktionen. Lokala variabler får alltså vara ifred inom funktionen, de kan inte ändras någon annanstans. Låt oss prova programmet!$ träd /usr/local
/usr/local-+-bin
           |-include
           |-lib-+-netscape
           |     |-site_perl---i386-linux
           |     |-texmf---doc
           |     `-xemacs---site-lisp
           |-man
           |-sbin
           `-share---emacs-+-19.34---site-lisp
                           |-20.2---site-lisp
                           `-site-lisp

12.3 Ett spel

I detta avsnitt ska vi ge ett programexempel som är något större än de tidigare. Programmet vi ska skriva är ett spel som går ut på att gissa en hemlig kod som datorn har skapat. Koden består av fem olika bokstäver från A till J. För varje gissning anger datorn hur många rätt man har - dels hur många bokstäver i gissningen som är helt rätt (rätt bokstav på rätt position), dels hur många bokstäver som är delvis rätt (rätt bokstav men fel position). En spelomgång kan se ut så här:$ spel
Ange 5 symboler (bland ABCDEFGHIJ): ABCDE
      0 Guld,   2 Silver
Ange 5 symboler (bland ABCDEFGHIJ): ABFGH
      0 Guld,   3 Silver
Ange 5 symboler (bland ABCDEFGHIJ): ACIGH
      1 Guld,   1 Silver
Ange 5 symboler (bland ABCDEFGHIJ): BDIFG
      2 Guld,   3 Silver
Ange 5 symboler (bland ABCDEFGHIJ): DFIBG
      2 Guld,   3 Silver
Ange 5 symboler (bland ABCDEFGHIJ): GDIBF
      5 Guld,   0 Silver
Rätt svar!
Hejdå, och tack för den här gången!
$
Antalet ''guld'' är antalet av de gissade bokstäverna som är helt rätt, medan antalet ''silver'' är antalet bokstäver som är delvis rätt.

När man ska skriva ett sådant program är det lämpligt att börja med att strukturera det. Detta innebär att man funderar ut vad som behöver göras och delar upp problemet i mindre delproblem som kan lösas separat. Ifall problemet/programmet är mycket stort, så brukar man först dela upp det i stora delproblem; dessa delproblem delas sedan upp i mindre delproblem och så vidare.

Så vad ska vårt spelprogram göra? Jo, först måste det skapa den hemliga koden. Därefter ska användaren få skriva in sin gissning, och programmet ska beräkna och skriva ut antalet poäng. Om gissningen inte var exakt rätt, så ska användaren få göra en ny gissning. De svåraste uppgifterna är förmodligen följande:

För var och en av dessa uppgifter skapar vi en funktion som löser respektive uppgift. Vi behöver alltså en funktion med namnet skapa_hemlig_kod, en med namnet las_in_gissning och en med namnet berakna_poang.

Man kan modifiera spelreglerna och tillåta en kod där bokstäverna inte är olika. Koden behöver inte heller bestå av exakt fem bokstäver, fyra eller sex bokstäver kunde gå lika bra. Och varför ska det vara just tio olika bokstäver att välja mellan? Dessutom skulle man kunna använda andra symboler än bokstäver i koden, till exempel siffror. Vi vill skriva programmet så att det blir lätt att ändra spelreglerna. För att uppnå detta inför vi ett antal variabler vars värden anger spelreglerna. Variabeln KODLANGD ska ange kodens längd, som i vår exempelkörning är 5. Variabeln SYMBOLER ska ange vilka symobler man får välja bland. Variabeln OLIKA_SYMBOLER ska vara ''Ja'' om koden måste bestå av olika symboler, annars ''Nej''. Ett standardtrick är att först definiera JA som 1 och NEJ som 0, och sedan definiera OLIKA_SYMBOLER som antingen JA eller NEJ.

Den hemliga koden lagras i en global variabel med namnet HEMLIG_KOD och vars värde ska vara en textsträng bestående av KODLANGD stycken symboler bland dem som anges av variabeln SYMBOLER. Varje gissning lagras på motsvarande sätt i variabeln GISSNING. Så här ser programmet ut:

#!/bin/bash

declare -i JA=1
declare -i NEJ=0
# Längden av den hemliga koden (antal positioner):
declare -i KODLANGD=5
# Ska symbolerna i den hemliga koden vara olika?
OLIKA_SYMBOLER=$JA
# Vilka symboler som ska användas (OBS! 
# Dessa måste vara olika):
SYMBOLER=ABCDEFGHIJ
# Antal symboler att välja bland:
declare -i ANTAL_SYMBOLER=${#SYMBOLER}
# HEMLIG_KOD ska innehålla den hemliga koden,
# GISSNING ska innehålla den senaste gissningen
# Följade ska ange antalet poäng för den senaste gissningen:
declare -i POANG_GULD=0
declare -i POANG_SILVER=0

function skapa_hemlig_kod()
{
  local N SYMB=$SYMBOLER SLUMPTAL NY
  HEMLIG_KOD=''
  declare -i N=0
  while (( (N+=1)<=KODLANGD )) ; do
    # Välj ny symbol slumpmässigt
    SLUMPTAL=$RANDOM%${#SYMB}
NY=${SYMB: $SLUMPTAL: 1}
    HEMLIG_KOD=$HEMLIG_KOD$NY
    # Om symbolerna i den hemliga koden ska vara olika,
    # så ta bort den valda symbolen från SYMB:
    (( OLIKA_SYMBOLER )) && SYMB=${SYMB/$NY/}
  done
}

function las_in_gissning()
{
  read -p "Ange $KODLANGD symboler (bland $SYMBOLER): " GISSNING
  # Stryk alla icke tillåtna tecken från GISSNING:
  GISSNING=${GISSNING//[^$SYMBOLER]/}
  # Finns det rätt antal tecken kvar i GISSNING?
  if (( ${#GISSNING} != $KODLANGD )) ; then
    echo 'Felaktig gissning, försök igen!'
    las_in_gissning
  fi
}

function berakna_poang()
{
  local N KOD=$HEMLIG_KOD S
  declare -i N=0
  # Beräkna först antalet guld.
  POANG_GULD=0
  # Jämför symbol för symbol mellan
  # GISSNING och KOD
  while (( N<${#KOD} )) ; do
    S=${KOD: $N: 1}
    if [ ${GISSNING: $N: 1} == $S ] ; then
       POANG_GULD=POANG_GULD+1
       # Plocka bort den symbol som gav poäng
       # så att den inte senare ger silver också:
       GISSNING=${GISSNING/$S/}
       KOD=${KOD/$S/}
    else
      N=N+1
    fi
  done
  # Beräkna nu antalet silver.
  # För varje symbol i koden, stryk den första eventuella
  # förekomsten av den från GISSNING.
  while (( (N-=1)>=0 )) ; do
    GISSNING=${GISSNING/${KOD: $N: 1}/}
  done
  # Antal silver = antalet strukna symboler i GISSNING:
  POANG_SILVER=${#KOD}-${#GISSNING}
  echo "      $POANG_GULD Guld,   $POANG_SILVER Silver"
}

skapa_hemlig_kod
while (( POANG_GULD != KODLANGD )) ; do
  las_in_gissning
  berakna_poang
done
echo 'Rätt svar!'
echo 'Hejdå, och tack för den här gången!'

12.4 Felhantering

Om ett kommando misslyckas, så fortsätter Bash som vanligt utan att bry sig om felet. Betrakta till exempel följande program:

#!/bin/bash
cp bellmna bellmna.kopia
cp bellman bellman.kopia

Så här går det när vi kör programmet:$ ls bell*
bellman
$ fel
cp: bellmna: Filen eller katalogen finns inte
$ ls bell*
bellman        bellman.kopia
$
Det andra kommandot i programmet kördes trots att det första kommandot misslyckades och gav ett felmeddelande. Ibland vill man att programkörningen ska avbrytas omedelbart när ett fel uppstått. I så fall ska man ha med kommandot set -e i programmet. Vi ändrar det till följande:

#!/bin/bash
set -e
cp bellmna bellmna.kopia
cp bellman bellman.kopia2

Och så provar vi igen:$ fel
cp: bellmna: Filen eller katalogen finns inte
$ ls bell*
bellman        bellman.kopia
$
Den här gången avbröts programmet efter kommandot som misslyckades. Om man vill att fel återigen ska ignoreras, så ska man ge kommandot set +e.

13. Att få något gjort

I detta kapitel introduceras programmet make som kan hjälpa en att få något gjort.

   
13.1 Introduktion

Antag att vi arbetar med ett LATEX-dokument som är uppdelat på flera filer: huvudfilen gnulinux.tex samt definitioner.tex och unix.tex. Innan dokumentet kan tryckas är det flera saker som måste göras. Först ska det typsättas av LATEXmed kommandot latex gnulinux.tex. Då skapas filen gnulinux.dvi. Denna fil översätts sedan till postscript så att skrivaren kan ta emot den. Kommandot dvips -o gnulinux.ps gnulinux.dvi. skapar postscriptfilen gnulinux.ps, som skrivs ut med kommandot lpr gnulinux.ps.

Alla dessa steg är tröttsamma att gå igenom, och det är lätt hänt att man gör fel i något av stegen. Varje gång man gör en ändring i någon av sina LATEX-filer måste dessutom alla eller vissa av stegen göras om. Slutsatsen blir att det borde finnas ett program, tex med namnet make, som gör arbetet åt en. Slutsatsen är korrekt: det finns ett sådant program.

För att få hjälp av make med en viss syssla ska man i en fil med namnet Makefile beskriva sysslan. När denna fil väl är skapad, så räcker det att skriva make mål varje gång man vill få sysslan mål utförd. Denna syssla kan, som i exemplet ovan, vara att typsätta och trycka ett dokument. Det kan också vara att kompilera ett program. Man kan inte få hjälp med att koka kaffe, men faktum är att många vitt skilda sysslor som kan utföras av ett antal Bash-kommandon kan automatiseras med hjälp av make. Frågan är nu hur en Makefile ser ut. Resten av detta kapitel ägnas åt att besvara den frågan.

En Makefile som tar hand om typsättningen som beskrivs ovan kan se ut så här:

gnulinux.dvi: gnulinux.tex definitioner.tex unix.tex
        latex gnulinux.tex

gnulinux.ps: gnulinux.dvi
        dvips -o gnulinux.ps gnulinux.dvi

utskrift: gnulinux.ps
        lpr gnulinux.ps
Filen ovan innehåller tre regler. Den första regeln säger att filen gnulinux.dvi skapas från filerna gnulinux.tex, definitioner.tex och unix.tex med kommandot latex gnulinux.tex. Den andra regeln talar om hur gnulinux.ps skapas från gnulinux.dvi. Den sista regeln anger hur filen gnulinux.ps skrivs ut. Låt oss prova!$ ls
Makefile definitioner.tex gnulinux.tex unix.tex
$ make utskrift
latex gnulinux.tex
This is TeX, Version 3.14159 (C version 6.1)
(gnulinux.tex
LaTeX2e <1996/12/01>
Babel <v3.6h> and hyphenation patterns for american, swedish, loaded.
(/usr/lib/texmf/tex/latex/base/book.cls
Document Class: book 1996/10/31 v1.3u Standard LaTeX document class
(/usr/lib/texmf/tex/latex/base/bk12.clo)) (definitioner.tex)
No file gnulinux.aux.
[1] [2] (gnulinux.aux) )
(see the transcript file for additional information)
Output written on gnulinux.dvi (2 pages, 1040 bytes).
Transcript written on gnulinux.log.
dvips -o gnulinux.ps gnulinux.dvi
This is dvipsk 5.58f Copyright 1986, 1994 Radical Eye Software
' TeX output 1997.11.17:1447' -> gnulinux.ps
<tex.pro>. [1] [2]
lpr gnulinux.ps
$
Vid kommandot make utskrift körs först latex, därefter dvips och till sist lpr. Kommandona och deras utdata skrivs ut på skärmen av make.

Låt oss göra en ny utskrift:$ ls
Makefile          gnulinux.aux      gnulinux.log      gnulinux.tex
definitioner.tex  gnulinux.dvi      gnulinux.ps       unix.tex
$ make utskrift
lpr gnulinux.ps
$
Som synes struntade make denna gång i att köra latex och dvips, filen gnulinux.ps skrevs ut direkt. På något magiskt sätt (som vi snart ska förklara) visste make att filen gnulinux.ps var ''up to date'' och att det var onödigt att köra latex igen. Hmm, antag att vi ändrat lite i tex unix.tex och sedan provar igen:$ ls -t
unix.tex          gnulinux.aux      gnulinux.tex
gnulinux.ps       gnulinux.dvi      unix.tex~
gnulinux.log      Makefile          definitioner.tex
$ make utskrift
latex gnulinux.tex
This is TeX, Version 3.14159 (C version 6.1)
(gnulinux.tex
LaTeX2e <1996/12/01>
Babel <v3.6h> and hyphenation patterns for american, swedish, loaded.
(/usr/lib/texmf/tex/latex/base/book.cls
Document Class: book 1996/10/31 v1.3u Standard LaTeX document class
(/usr/lib/texmf/tex/latex/base/bk12.clo)) (definitioner.tex) (gnulinux.aux)
[1] (unix.tex) [2] (gnulinux.aux) )
(see the transcript file for additional information)
Output written on gnulinux.dvi (2 pages, 2328 bytes).
Transcript written on gnulinux.log.
dvips -o gnulinux.ps gnulinux.dvi
This is dvipsk 5.58f Copyright 1986, 1994 Radical Eye Software
' TeX output 1997.11.17:1524' -> gnulinux.ps
<tex.pro>. [1] [2]
lpr gnulinux.ps
$
Nu körde make återigen latex och dvips före lpr.

13.2 Regler

Reglerna i en Makefile är av formen

mål:  delmål1 delmål2 ...
        kommando
        kommando
        ...
Regeln inleds med namnet på ett mål för make. Vanligtvis är namnet på målet identiskt med namnet på en fil som regeln är till för att skapa. Vår första regel i exemplet ovan har som mål gnulinux.dvi, och regeln är till för att skapa filen gnulinux.dvi.

Efter namnet på målet ska man skriva ett kolon, och därefter på samma rad alla eventuella delmål, dvs mål som måste uppnås innan det angivna målet kan uppnås. För att klara av ett delmål letar make först efter en regel som talar om hur det uppnås, och om det finns ett sådant så tar make hand om det först. Om det inte finns en regel för delmålet, så försöker make finna en fil med samma namn som delmålet; finns det inte heller någon sådan fil, så klagar make över att ''det finns ingen regel för att göra målet xxx, som behövs för målet yyy'' och körningen avbryts.

Efter raden som anger målet kan man ha ett eller flera kommandon som ska utföras för att målet ska uppnås. Före varje kommando måste det finnas ett tabulatorsteg. (Det går inte att skriva några mellanslag i stället. I början gör man ofta fel på detta.)

Kommandona utförs inte automatiskt, utan bara om make anser att målet inte är ''up to date''. Det kan finnas olika skäl till att målet måste uppdateras:

När vi i avsnitt 13.1 första gången gav kommandot make utskrift, så betraktade make först delmålet gnulinux.ps. Det fanns en regel för gnulinux.ps, som make gick till. Där betraktades delmålet gnulinux.dvi, så make hoppade till regeln gnulinux.dvi. Delmålen där, de tre LATEX-filerna, fanns det ingen regel för, men däremot fanns de som filer i den aktuella katalogen. Alltså var delmålen till gnulinux.dvi klara, men eftersom det inte fanns någon fil med namnet gnulinux.dvi körde make kommandot i regeln gnulinux.dvi. Därmed var filen gnulinux.dvi skapad och motsvarande mål uppnått. Sedan återvände make till gnulinux.ps och körde kommandot i den regeln eftersom dess delmål hade uppdaterats (men också för att det filen gnulinux.ps inte fanns). Nu var också målet gnulinux.ps klart, och make återvände till målet utskrift vars kommandon kördes eftersom det inte fanns någon fil med det namnet (och för att delmålet hade uppdaterats).

Den andra gången vi gav kommandot make utskrift gick make till delmålen gnulinux.ps och gnulinux.dvi. Målet gnulinux.dvi behövde inte uppdateras eftersom det fanns en fil med detta namn som också var nyare än alla dess delmål. Inte heller gnulinux.ps behövde uppdateras eftersom den var nyare än gnulinux.dvi. Däremot kördes kommandona i målet utskrift eftersom det inte fanns någon fil med namnet utskrift.

Den tredje gången vi körde make utskrift gick make återigen till delmålen gnulinux.ps och gnulinux.dvi. Den här gången var ett av delmålen till gnulinux.dvi nyare än filen gnulinux.dvi, så make körde kommandot i målet gnulinux.dvi. Därmed måste även gnulinux.ps uppdateras före utskriften.

Här är ett nytt exempel:$ make gnulinux.ps
make: `gnulinux.ps' is up to date.
$ touch gnulinux.dvi
$ make gnulinux.ps
dvips -o gnulinux.ps gnulinux.dvi
This is dvipsk 5.58f Copyright 1986, 1994 Radical Eye Software
' TeX output 1997.11.17:1524' -> gnulinux.ps
<tex.pro>. [1] [2]
$
Första gången vi ger kommandot make gnulinux.ps ovan är filen gnulinux.ps nyare än gnulinux.dvi, så make uppdaterar den inte. Men om vi med kommandot touch petar på gnulinux.dvi så att det ser ut som om den ändrats, så blir den nyare än gnulinux.ps och då tror make att gnulinux.ps behöver uppdateras.

13.3 Bluffmål

Om vi hade haft otur och det funnits en fil med namnet utskrift som var nyare än filen gnulinux.ps, så skulle vi inte få vår utskrift. Detta är kanske inte vad man önskar. Men det finns ett sätt att gardera sig mot sådan otur. Om man har med en regel med namnet .PHONY och med utskrift som delmål, så struntar make i om det finns någom fil med namnet utskrift eller inte. Därigenom försäkrar man sig om att kommandona i målet alltid utförs.

Ofta tar man i sin Makefile med ett mål som gör att make kan städa upp efter sig. Detta mål brukar heta clean och har till syfte att radera alla eller en del av de filer som tillverkas automatiskt i de andra målen. I vårt exempel kan man låta clean radera gnulinux.log, gnulinux.aux och alla kopior som Emacs har gjort (de filer vars namn slutar med ett tilde), samt eventuellt också gnulinux.dvi och gnulinux.ps. Målet clean bör naturligtvis också sättas under .PHONY.

Om man ger kommandot make utan att specificera något mål, så tar make hand om det första målet i Makefile. Därför kan man sätta det mål man vanligtvis vill uppnå först. Fast det finns ett annat standardsätt att göra detta på: man brukar ha ett mål med namnet all överst i Makefile, och som delmål till all anges det eller de mål man oftast vill få utförda.

Vi ändrar alltså vår Makefile till

all: gnulinux.ps

.PHONY: all utskrift clean

gnulinux.dvi: gnulinux.tex definitioner.tex unix.tex
        latex gnulinux.tex

gnulinux.ps: gnulinux.dvi
        dvips -o gnulinux.ps gnulinux.dvi

utskrift: gnulinux.ps
        lpr gnulinux.ps

clean:
        rm *~ gnulinux.{dvi,ps,aux,log}
Och så provar vi hur det fungerar!$ ls
Makefile          gnulinux.dvi      gnulinux.tex
definitioner.tex  gnulinux.log      unix.tex
gnulinux.aux      gnulinux.ps       unix.tex~
$ make clean
rm *~ gnulinux.{dvi,ps,aux,log}
$ ls
Makefile          gnulinux.tex
definitioner.tex  unix.tex
$ make
latex gnulinux.tex
This is TeX, Version 3.14159 (C version 6.1)
(gnulinux.tex
LaTeX2e <1996/12/01>
Babel <v3.6h> and hyphenation patterns for american, swedish, loaded.
(/usr/lib/texmf/tex/latex/base/book.cls
Document Class: book 1996/10/31 v1.3u Standard LaTeX document class
(/usr/lib/texmf/tex/latex/base/bk12.clo)) (definitioner.tex)
No file gnulinux.aux.
[1] (unix.tex) [2] (gnulinux.aux) )
(see the transcript file for additional information)
Output written on gnulinux.dvi (2 pages, 2328 bytes).
Transcript written on gnulinux.log.
dvips -o gnulinux.ps gnulinux.dvi
This is dvipsk 5.58f Copyright 1986, 1994 Radical Eye Software
' TeX output 1997.11.18:0050' -> gnulinux.ps
<tex.pro>. [1] [2]
$ make
make: Nothing to be done for `all'.
$
Ja, make clean rensar bort alla skräpfiler. Och make uppdaterar målet gnulinux.ps via all. Sista gången vi körde make ovan var gnulinux.ps redan uppdaterad, och all fick inget att göra.

13.4 Kommandon

Om något kommando i en Makefile misslyckas, så stoppar make där. Detta är tvärt emot tex Bash, som vanligtvis struntar i alla fel och bara fortsätter. Om vi kör make clean igen, så går det så här:$ make clean
rm *~ gnulinux.{dvi,ps,aux,log}
rm: *~: Filen eller katalogen finns inte
make: *** [clean] Error 1
$
Kommandot rm rapporterar misslyckande eftersom en av filerna som skulle raderas inte fanns. Därmed stannar också make med ett felmeddelande. I det här läget vill vi att make ska strunta i felet. Det finns ett sätt att tala om detta: före kommandot, direkt efter tabulatorsteget, ska man sätta ett streck. Raden blir alltså:

        -rm *~ gnulinux.{dvi,ps,aux,log}
Om vi kör make med den ändringen, så går det så här istället:$ make clean
rm *~ gnulinux.dvi gnulinux.ps gnulinux.aux gnulinux.log
rm: *~: Filen eller katalogen finns inte
make: [clean] Error 1 (ignored)
$
make ignorerar felet, och hade fortsatt om det funnits mer att göra.