Ik Van Linux

Commando's


We hebben ondertussen al heel wat commando's gezien. Geloof me, er zijn er nog veel meer. Sterker nog, er zijn nog veel meer commando's die nog niet eens op je computer geïnstalleerd staan. Laten we heel even stil staan waar Linux zijn commando's heeft staan. Dat kan interessant worden wanneer we onze eigen programma's gaan schrijven bijvoorbeeld.

In Linux is alles een bestand. Het mag daarom geen verrassing zijn wanneer ik zeg dat commando's ook bestanden zijn.
Om te zien waar Linux al zijn programma bestanden vandaan haalt kun je het volgende commando uitvoeren:

echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games

Hiermee wordt de inhoud van de variabele PATH afgedrukt. Dit geeft een regel met directory namen waarin het systeem gaat zoeken naar het commando wat je hebt ingetoetst. De directories in het pad worden van elkaar gescheiden door een dubbele punt. In principe kun je het pad hiermee dus uitbreiden, of juist inkorten.
Dus als je een commando intypt en op Enter drukt gaat het systeem in bovenstaand geval eerst in de directory /usr/local/sbin zoeken naar dat commando. Als hij het daar niet kan vinden kijkt het in /usr/local/bin, dan in /usr/sbin, /usr/bin, /sbin, /bin, /usr/local/games en ten slotte in /usr/games. Zodra een bestand met de naam van het commando in een van die directories gevonden wordt, wordt dat bestand uitgevoerd.
Ik ben een kleine stap overgeslagen. Bash heeft namelijk een aantal ingebouwde commando's. Voor die commando's hoeft dus niet gezocht te worden naar een bestand met die naam. Bash zoekt dus eerst in de lijst met interne commando's en als het ingegeven commando daar niet gevonden wordt gaat het zoeken verder door het pad. De meest gebruikelijke ingebouwde commando's zijn cd en exit. Als het goed is kennen we die al.

Er is een manier om er achter te komen welk bestand uitgevoerd gaat worden voor elk specifiek commando.

which nano
/bin/nano

Met het which (welk) commando, gevolgd door de naam van het commando wat je zoekt, kun je dus zien welk bestand verantwoordelijk is voor het uitvoeren ervan.

Wanneer een ingetypt commando niet gevonden wordt in de ingebouwde lijst van commando's en ook niet in het pad, krijg je een foutmelding:

oeioei
-bash: oeioei: command not found

Je eigen programma

Stel nu dat we zelf een programma gaan schrijven. Ik hoop namelijk dat dat je uiteindelijke doel is met de Raspberry Pi. Want er is niets leukers dan het schrijven van je eigen programma's.
Laten we meteen maar beginnen met een eigen programma, dan zie je meteen hoe het werkt.

Open een nieuw bestand met het commando nano hallo . En plaats daar onderstaande tekst in. Je hoeft nog niet te snappen wat er gebeurt in dat programma. Technisch gezien is het geen programma maar is het een script, maar dat is even niet belangrijk.

#! /bin/bash
echo “Hallo wereld”

Sluit de tekstverwerker met Ctrl-X, Y en Enter.
Om veiligheidsredenen zijn bestanden in Linux standaard niet uitvoerbaar. Dat moeten we dus eerst even fixen.

chmod u+x hallo

Dit commando hebben we reeds eerder gezien bij Gebruikers en rechten. Bovenstaand commando kent het uitvoerrecht toe aan de eigenaar van het bestand. Alleen de eigenaar mag het commando dus gaan uitvoeren.
Dus we hebben het programma nu geschreven en uitvoerbaar gemaakt. Dan gaan we het nu uitvoeren.

hallo
-bash: hallo: command not found

Hmmm, hij kan ons programma niet gevonden krijgen. Natuurlijk, het programma wat we zojuist geschreven hebben staat niet in het opgegeven pad. Mensen die wel eens in DOS gewerkt hebben weten misschien nog dat DOS een programma ook uit kon voeren als het in het huidige pad stond. Linux doet dat, uit veiligheidsoverwegingen, niet.
Maar gelukkig kun je in Linux ook specifiek aangeven waar het programma gevonden kan worden. Dit doe je door het pad voor de programmanaam op te geven.

/home/pi/hallo
Hallo wereld

Hoera, hij doet het! Maar zo'n absoluut pad is wel lastig, als we dat elke keer op moeten geven als we ons programma uit willen voeren. Een absoluut pad begint altijd vanaf de directory root, het begint dus met een / teken. Een relatief pad is een pad wat vanaf de huidige directory begint. Dus als we een programma willen uitvoeren wat in onze huidige directory staat moeten we daar naar verwijzen. Herinner je de twee speciale directories nog? De punt en de punt punt directories bedoel ik. De .. directory brengt ons vanaf de huidige directory een directory terug, richting de root directory. De . directory betekent eigenlijk "deze directory". Dus als we aan willen geven waar ons programma gevonden kan worden typen we gewoon:

./hallo
Hallo wereld

Er valt je misschien nog iets op. Het programma wat we geschreven hebben heeft geen extensie. Het heet dus niet hallo.bat of hallo.exe of zo. Het had wel zo kunnen heten, maar hier heet het gewoon hallo.
In Linux is dat normaal. Linux kijkt gewoon in het bestand om te zien wat hij ermee moet doen. Je had het programma ook gewoon hallo.jpg kunnen noemen. Dat is alleen voor de gebruiker verwarrend, niet voor het systeem. Het feit dat het programma uitvoerbaar is gemaakt, en dat het begint met een hele rare regel, en dat het vindbaar is in het pad of wanneer je expliciet aangeeft waar het te vinden is, is voor Linux voldoende om een bestand als programma te herkennen.

Merk op dat het hele standaard pad bestaat uit directories die niet door een normale gebruiker beschreven mogen worden. Ze hebben allemaal root als eigenaar, en verder mag iedereen de programma's alleen lezen en uitvoeren. Dat is veilig natuurlijk, maar wel ongemakkelijk voor ons, programmeurs (in de dop). Daarom is in de meeste Linux distributies een handhigheidje ingebouwd. Wanneer je in je home directory een nieuwe directory maakt met de naam bin zal die directory automatisch worden toegevoegd aan je pad.

cd
mkdir bin
mv hallo bin/

Dit zijn de voorbereidingen, ga in je home directory staan, maak de bin directory aan en plaats ons uitvoerbare bestand in die directory. De nieuwe directory wordt echter pas aan je pad toegevoegd wanneer je opnieuw inlogt. Log dus even uit (of sluit je terminal venster) en log opnieuw in. Nu kun je jouw eerste programma starten door gewoon de naam in te vullen. Wil je weten waar het programma vandaan komt type je weer:

which
/home/pi/bin/hallo

Gelukkig hoef je maar een keer zo veel moeite te doen om je programma uitvoerbaar te maken. Als je dat eenmaal gedaan hebt kun je het programma zo vaak en zo veel aanpassen als je wilt. Het programma blijft dan gewoon uitvoerbaar.

Je kunt ten slotte het pad ook handmatig aanpassen. Maar dat ga ik verderop nog uitleggen, als we het over variabelen gaan hebben.

Een programma van een ander

En dan krijg je een programma van een ander. Dat is vaak een Bash script, of een Python programma, maar het kan ook een binair gecompileerd programma zijn. Hiervoor geldt eigenlijk hetzelfde. Gewoon zorgen dat de Execute vlag gezet is met het chmod u+x commando en gaan met die bbanaan.
Het is natuurlijk wel belangrijk om na te gaan of het programma wat je wilt starten wel te vertrouwen is. Hoe groot is de kans dat je een legitiem programma aan het draaien bent. Als je het niet vertrouwt moet je op onderzoek uitgaan. Vertrouw je het dan nog niet, dan kun je het programma beter niet draaien.

Maar stel voor dat je het programma wel vertrouwt. Je hebt het al uitvoerbaar gemaakt en je gaat het starten.

./install.sh
bash: ./install.sh: cannot execute: required file not found

Iets soortgelijks is me vandaag overkomen. Wat is hier nu aan de hand? Ik heb even in het bestand gekeken, en vond daar geen rare dingen. De eerste regel begon netjes met:

#! /bin/sh

Dat geeft aan dat het programma uitgevoerd moet worden met de shell interpreter sh. En verderop stonden er ook geen rare dingen in het bestand.

Het heeft even geduurd voordat ik er achter was. Ik heb even twee uitvoerbare bestandjes gemaakt. Bestand nummer 1 geeft de bovengenoemde foutmelding. Bestand nummer 2 werkt wel.

hd bestand1
00000000  23 21 20 2f 62 69 6e 2f  73 68 0d 0a 65 63 68 6f  |#! /bin/sh..echo|
00000010  20 22 44 69 74 20 7a 6f  75 20 65 69 67 65 6e 6c  | "Dit zou eigenl|
00000020  69 6a 6b 20 6d 6f 65 74  65 6e 20 77 65 72 6b 65  |ijk moeten werke|
00000030  6e 2e 22 0d 0a 0d 0a                              |n."....|
00000037

hd bestand2
00000000  23 21 20 2f 75 73 72 2f  73 68 0a 65 63 68 6f 20  |#! /usr/sh.echo |
00000010  22 44 69 74 20 7a 6f 75  20 65 69 67 65 6e 6c 69  |"Dit zou eigenli|
00000020  6a 6b 20 6d 6f 65 74 65  6e 20 77 65 72 6b 65 6e  |jk moeten werken|
00000030  2e 22 0a 0a                                       |."..|
00000034

Het hd commando drukt een zogenaamde hexdump af van het opgegeven bestand. In zo'n hexdump zie je alle bytes van het bestand. Het verschil tussen de twee bestanden heb ik vet gemaakt, bekijk dat maar eens goed.
Bij bestand1 wordt het regeleinde gevormd met de bytecombinatie 0d 0a. Bij bestand2 bestaat het regeleind alleen uit de byte 0a. En dat maakt het verschil tussen niet willen werken en wel willen werken.

De bytecombinatie 0d 0a is typisch voor tekst bestanden die op een Windows systeem geschreven zijn. De vorm waar alleen het byte 0a gebruikt wordt is een bestand wat op een Unix systeem gemaakt is (Linux is daar familie van).

Dus alles wat je nu nog moet doen is het installatiebestand omvormen van Dos naar Unix. Dat doe je met het programma dos2unix. Dat programma is doorgaans niet geïnstalleerd en zul je daarom eerst nog even moeten installeren.

sudo apt install dos2unix

dos2unix bestand1

Hiermee worden de regeleindes omgevormd naar regeleindes voor Linux tekst bestanden. Daarna is het programma gewoon uitvoerbaar geworden.