If God had known we'd need foresight, she would have given it to us.
Eén van de dingen waarin de Unix filosofie zich onderscheidt, is dat de systeemontwerpers geen poging ondernamen elke behoefte die gebruikers zouden kunnen hebben voor te schrijven. In plaats daarvan probeerden ze het voor iedere individuele gebruiker makkelijk te maken om de omgeving aan hun eigen specifieke behoeften aan te passen. Dit wordt hoofdzakelijk bewerkstelligd via configuratiebestanden. Deze staan ook bekend als "init files", "rc files" (voor "run control"), of zelfs "dot files", omdat de bestanden vaak met een punt "." beginnen. Ter herinnering, bestandsnamen die met een "." beginnen, worden normaal gesproken niet door een ls weergegeven.
De belangrijkste configuratiebestanden zijn die door de shell worden gebruikt. Linux's standaardshell is bash en dat is de shell die in dit hoofdstuk wordt behandeld. Voor we bash aan zullen gaan passen, moeten we weten naar welke bestanden bash zoekt.
Bash kan op diverse manieren worden uitgevoerd. Het kan als een loginshell worden uitgevoerd, zo wordt het uitgevoerd wanneer je voor de eerste maal inlogt. De loginshell is de eerste shell waar je mee te maken krijgt.
Een andere manier waarop bash kan worden uitgevoerd is als een interactieve shell. Dit is elke shell die een prompt presenteert aan een mens en wacht op invoer. Ook een loginshell is een interactieve shell. Een wijze waarop je een interactieve niet-login shell kunt krijgen is bijvoorbeeld met een shell binnen xterm Elke shell die op enige andere wijze werd aangemaakt behalve voor het inloggen is geen login-shell.
Ten slotte zijn er nog niet interactieve shells Deze shells worden gebruikt voor het uitvoeren van een bestand met opdrachten, net als in MS-DOS batch files; de bestanden die eindigen op .BAT. Deze shellscripts functioneren als mini-programma's. Terwijl ze gewoonlijk veel langzamer zijn dan gecompileerde programma's is het vaak wel zo dat ze makkelijker zijn te schrijven.
Afhankelijk van het type shell, zullen bij het opstarten van de shell verschillende bestanden worden gebruikt:
Gezien de meeste gebruikers dezelfde omgeving willen, ongeacht voor welk type interactieve shell ze kiezen, of het een loginshell is of niet, zullen we onze configuratie starten met een zeer simpele opdracht in het bestand .bash_profile: "source ~/.bashrc". De source opdracht vertelt de shell het argument als een shellscript te interpreteren. Wat dit voor ons betekent is dat elke keer wanneer .bash_profile wordt uitgevoerd, ook .bashrc wordt uitgevoerd.
We zullen nu gewoon wat opdrachten toe gaan voegen aan het bestand .bashrc. Wil je ooit dat je opdracht wordt uitgevoerd, wanneer je inlogt, dan voeg je deze opdracht toe aan .bash_profile.
Wat is hetgeen je zou willen aanpassen? Ik denk dat ongeveer 90% van de Bash gebruikers het volgende in .bashrc heeft geplaatst:
alias ll="ls -l" |
Die opdracht definieerde een shell alias genaamd ll welke "extraheert" tot de gewone shellopdracht "ls -l" wanneer het wordt aangeroepen door de gebruiker. Dus in de veronderstelling dat Bash die opdracht uit .bashrc heeft gelezen, kun je gewoon ll typen om het effect van "ls -l." te krijgen met slechts de helft van het aantal toetsaanslagen. Wanneer je ll typt en de Return-toets indrukt, onderschept Bash het, omdat het uitkijkt naar aliassen, Bash vervangt het door "ls -l", en retourneert dat ervoor in de plaats. Op het systeem komt geen ll opdracht voor, maar de shell vertaalt de alias automatisch naar een geldig programma. Een aantal aliassen staan in Voorbeeld 10-1. Je zou ze in je eigen .bashrc kunnen plaatsen. Een vooral interessante alias is de eerste. Met die alias, wordt automatisch een -F vlag aangezet, wanneer iemand ls intikt. (De alias probeert zichzelf niet nogmaals te extraheren.) Dit is een gebruikelijke manier om opties toe te voegen die je elke keer gebruikt als je een programma aanroept. Let op het commentaar voorafgegaan door het # teken in Voorbeeld 10-1. Wanneer een # verschijnt, negeert de shell de rest van de regel.
Voorbeeld 10-1. Een aantal voorbeeldaliassen voor bash
alias ls="ls -F" # geef tekens aan het einde van de listing
alias ll="ls -l" # speciale ls
alias la="ls -a"
alias ro="rm *~; rm .*~" # backupbestanden aangemaakt door
# Emacs verwijderen
alias rd="rmdir" # bespaart tikken!
alias md="mkdir"
alias pu=pushd # pushd, popd, en dirs werden niet behandeld
alias po=popd # in deze handleiding---je zou ze op
alias ds=dirs # kunnen zoeken in de manpage van bash
# dit zijn allen slechts toetsenbordsnelkoppelingen
alias to="telnet cs.oberlin.edu"
alias ta="telnet altair.mcs.anl.gov"
alias tg="telnet wombat.gnu.ai.mit.edu"
alias tko="tpalk kold@cs.oberlin.edu"
alias tjo="talk jimb@cs.oberlin.edu"
alias mroe="more" # spellingscorrectie!
alias moer="more"
alias email="emacs -f rmail" # mijn mail reader
alias ed2="emacs -d floss:0 -fg \"grey95\" -bg \"grey50\""
# een manier om emacs aan te roepen
|
Je hebt wellicht wat vreemds ontdekt. Ten eerste liet ik in een paar aliassen de aanhalingstekens achterwege, zoals in pu. Strict genomen, zijn aanhalingstekens niet echt nodig wanneer aan de rechterkant van het is gelijk teken slechts één woord staat.
Het kan echter geen kwaad wel aanhalingstekens te gebruiken, dus laat me je geen slechte gewoonten aanleren. Je moet ze in ieder geval gebruiken als je een alias maakt voor een opdracht met opties en/of argumenten:
alias rf="refrobnicate -verbose -prolix -wordy -o foo.out" |
In de laatste alias tenslotte is het gebruik van aanhalingstekens wat eigenaardig:
alias ed2="emacs -d floss:0 -fg \"grey95\" -bg \"grey50\"" |
Zoals je wellicht al raadde, wilde ik dubbele aanhalingstekens in de opties zelf doorgeven, dus moest ik die laten voorafgaan door een backslash om te voorkomen dat bash zou denken dat het 't einde van de alias was.
Als laatste heb ik twee gebruikelijke typfouten, nl "more" en "moer" in een alias geplaatst voor de opdracht die ik daarmee bedoel, more. Argumenten die je aan een programma doorgeeft, verstoren aliassen niet. Het volgende werkt prima:
/home/larry# mroe hurd.txt |
In feite beslaat, weten hoe je je eigen aliassen maakt, waarschijnlijk minstens de helft van alle aanpassingen aan de shell. Experimenteer er wat mee, zoek uit welke lange opdrachten je veel typt, en maak hier aliassen voor aan. Je zult bemerken dat het 't werken achter de shellprompt veel plezieriger maakt.
Iets anders van belang wat wordt ingesteld in .bashrc zijn omgevingsvariabelen. En wat zijn omgevingsvariabelen? Laten we het eens van een andere kant bekijken: stel dat je de documentatie van het programma fruggle aan het lezen bent en dat je deze zinnen tegenkomt:
Fruggle zoekt normaal gesproken in de homedirectory van de gebruiker naar het configuratiebestand .frugglerc. Als echter de omgevingsvariabele FRUGGLEPATH op een andere bestandsnaam is ingesteld, zal het in plaats daarvan daar zoeken.
Elk programma wordt uitgevoerd in een omgeving, en die omgeving wordt gedefinieerd door de shell die het programma aanriep [1]. Van de omgeving kan worden gesteld dat ze zich "binnen" de shell bevindt. Programmeurs gebruiken een speciale routine om de omgeving te ondervragen en het fruggle programma maakt gebruik van deze routine. Het controleert de waarde van de omgevingsvariabele FRUGGLEPATH. Als blijkt dat die variabele ongedefinieerd is, dan zal het gewoon het bestand .frugglerc in je homedirectory gebruiken. Als het echter gedefinieerd is, zal fruggle de waarde van die variabele gebruiken (wat de naam van een bestand moet zijn welke fruggle kan gebruiken in plaats van het standaardbestand .frugglerc).
Zo kun je de omgeving onder bash wijzigen:
/home/larry# export PGPPATH=/home/larry/secrets/pgp |
De opdracht export kun je zien met als betekenis "Exporteer deze variabele naar de omgeving van waaruit ik programma's zal aanroepen, zodat de waarde ervan zichtbaar is voor die programma's." De naam export is niet zonder reden, zoals je later zult zien.
Deze bepaalde variabele wordt gebruikt door Phil Zimmerman's public-key encryptie programma, pgp. Standaard maakt pgp gebruik van je homedirectory als lokatie om naar bepaalde bestanden te zoeken die het nodig heeft (met encryptiesleutels) en tevens een plaats voor het opslaan van tijdelijke bestanden die het tijdens de uitvoering aanmaakt. Door de variabele PGPPATH op deze waarde in te stellen, vertelde ik het in plaats daarvan de directory /home/larry/secrets/pgp te gebruiken. Ik moest de pgp handleiding lezen om achter de exacte naam van de variabele te komen en wat deze doet, maar het is tamelijk standaard de naam van het programma in hoofdletters te gebruiken, voorafgegaan door het voorvoegsel "PATH".
Het is ook handig om de omgeving te kunnen ondervragen:
/home/larry# echo $PGPPATH /home/larry/.pgp /home/larry# |
Let op het teken "$"; je laat een omgevingsvariabele voorafgaan door een dollar-teken om aan de waarde van de variabele te komen. Had je het getypt zonder dit dollar-teken, dan zou echo simpelweg het opgegeven argument op het scherm hebben weerkaatst:
/home/larry# echo PGPPATH PGPPATH /home/larry# |
De "$" wordt gebruikt om omgevingsvariabelen te evalueren, maar het doet dit alleen in de context van de shell; dat wil zeggen wanneer de shell interpreteert. Wanneer interpreteert de shell? Wanneer je opdrachten achter de prompt intikt of wanneer bash opdrachten inleest vanuit een bestand zoals .bashrc, kan worden gesteld dat het de opdrachten "interpreteert".
Er is nog een andere handige opdracht voor het ondervragen van de omgeving: env. env zal louter een opsomming geven van de omgevingsvariabelen. Het is mogelijk dat de lijst van het scherm afscrollt, vooral als je van X gebruik maakt. Als dit gebeurt, sluis de uitvoer van env dan door naar more: env|more.
Een paar van deze variabelen kunnen nogal nuttig zijn, dus we zullen ze behandelen. Kijk naar onderstaande tabel . Deze vier variabelen worden automatisch gedefinieerd wanneer je inlogt: je stelt ze niet in via .bashrc of .bash_login.
Tabel 10-1. Een aantal belangrijke omgevingsvariabelen
| Variabelenaam | Inhoud | Voorbeeld |
|---|---|---|
| HOME | homedirectory | /home/larry |
| TERM | type terminal | xterm, vt100 of console |
| SHELL | Pad naar je shell | /bin/bash |
| USER | Je loginnaam | larry |
| PATH | Te doorzoeken lijst naar programma's | /bin:/usr/bin:/usr/local/bin:/usr/bin/X11 |
Laten we de variabele TERM eens nader bekijken. Om deze variabele te kunnen begrijpen, moeten we bekend zijn met de historie van Unix. Het besturingssysteem moet bepaalde feiten kennen over je console, om basisfuncties zoals het schrijven van een teken naar het scherm uit te voeren, de cursor naar de volgende regel te verplaatsen, enz. In de begindagen van computers, voegden fabrikanten constant nieuwe features toe aan hun terminals: eerst reverse-video, toen wellicht Europeaanse tekensets, eventueel zelfs primitieve tekenfuncties (denk er aan, dit was voordat er venstersystemen en muizen bestonden). Echter al deze nieuwe functies bezorgden de programmeurs problemen: hoe konden zij weten wat een terminal ondersteunde en wat niet? En hoe konden ze nieuwe features ondersteunen zonder de oude terminals waardeloos te maken?
Onder Unix, was het antwoord op deze vragen /etc/termcap . /etc/termcap bestaat uit een lijst met alle terminals die je systeem kent, en hoe ze de cursor besturen. Als een systeembeheerder een nieuwe terminal aanschafte, hoefde hij slechts de gegevens van die terminal aan /etc/termcap toe te voegen in plaats dat hij Unix geheel opnieuw moest samenstellen. Soms werkt het zelfs nog eenvoudiger. Zo langzamerhand werd de vt100 terminal van Digital Equipment Corporation een pseudo-standaard, en veel nieuwe terminals werden zo gebouwd dat ze deze terminal konden emuleren of zodanig konden functioneren alsof het een vt100 was.
Onder Linux, is de waarde van TERM soms console. Dit is een op vt100 lijkende terminal met een aantal extra features.
Een andere variabele, PATH, is ook beslissend voor het juist functioneren van de shell. Hier is de mijne:
/home/larry# env | grep ^PATH PATH=/home/larry/bin:/bin:/usr/bin:/usr/local/bin:/usr/bin/X11: /usr/TeX/bin /home/larry# |
Je PATH bestaat uit een door dubbele punten gescheiden lijst met die directory's die de shell doorzoekt op programma's, wanneer je de naam van een uit te voeren programma intikt. Wanneer ik bijvoorbeeld ls intik en op de Return-toets druk, dan zoekt Bash als eerste in /home/larry/bin, een directory aangemaakt voor het opslaan van programma's die ik schreef. Ik schreef echter geen ls (in feite denk ik dat het is geschreven nog voor ik werd geboren!). Het niet kunnen vinden in /home/larry/bin, maakt dat Bash vervolgens zoekt in /bin, en daar lukt het wel! /bin/ls komt voor en het is uitvoerbaar, dus Bash stopt met zoeken naar een programma met de naam ls en voert het uit. Er had net zo goed nog een ander programma met de naam ls in de directory /usr/bin voor kunnen komen, maar bash zou het nooit uitvoeren tenzij hier expliciet om wordt gevraagd door een absolute padnaam op te geven:
/home/larry# /usr/bin/ls |
De variabele PATH is ervoor dat we geen volledige padnamen voor elke opdracht in hoeven tikken. Wanneer je een opdracht intikt, zoekt Bash naar deze opdracht in de volgorde opgegeven in PATH, en voert deze uit als het de opdracht aantreft. Vind het de opdracht niet, dan krijg je een primitieve foutmelding:
/home/larry# clubly clubly: command not found |
In mijn PATH komt de directory "." niet voor. Als dit wel zo was zou het er ongeveer zo hebben uitgezien:
/home/larry# echo $PATH .:/home/larry/bin:/bin:/usr/bin:/usr/local/bin: /usr/bin/X11:/usr/TeX/bin /home/larry# |
Dit heeft te maken met een of ander debat in Unix kringen (waar je nu deel van uitmaakt, of je dat nu leuk vindt of niet). Het probeem bestaat hieruit dat het plaatsen van de huidige directory in je PATH een beveiligingslek kan veroorzaken. Stel dat je met cd naar een directory gaat waar iemand een "Trojan Horse" programma met de naam ls heeft achtergelaten, en je geeft de opdracht ls wat je al snel doet als je eenmaal in een andere directory bent aangekomen. Gezien de huidige directory "." als eerste in je PATH voorkomt, zou de shell deze versie van ls aantreffen en het uitvoeren. Wat voor onheil wellicht in dat programma is geplaatst, heb je dan uitgevoerd. De persoon die dit deed had hier geen rootprivileges voor nodig; er was alleen schrijfpermissies nodig op de directory waar de "onechte" ls werd gelokaliseerd. Het zou zijn homedirectory kunnen zijn geweest, als iemand zou weten dat je daar ooit rond zou neuzen.
Op je eigen systeem is het hoogstonwaarschijnlijk dat mensen een val voor elkaar zetten. Alle gebruikers zijn waarschijnlijk vrienden of collega's. Op een groot multi-user systeem (zoals op vele universiteitscomputers), zouden er echter voldoende onvriendelijke programmeurs kunnen zijn die je nog nooit hebt ontmoet. Of je nu wel of niet het risico wilt lopen "." in je pad op te nemen, is afhankelijk van je situatie; Ik was niet van plan om er op wat voor wijze dan ook autoritair in te zijn, ik wil gewoon dat je bewust bent van de risico's[2] Multi-user systemen zijn in werkelijkheid gemeenschappen, waar mensen elkaar op allerlei onvoorziene manieren iets aan kunnen doen.
De werkelijke wijze waarop ik mijn PATH instel, bestaat voornamelijk uit wat je tot dusverre hebt geleeerd over omgevingsvariabelen. Zo ziet mijn .bashrc eruit:
export PATH=${PATH}:.:${HOME}/bin:/bin:/usr/bin:/usr/local/bin:
/usr/bin/X11:/usr/TeX/bin
|
Hier maak ik gebruik van het feit dat de variabele HOME is ingesteld voordat Bash mijn .bashrc inleest, door de waarde ervan te gebruiken bij het instellen van mijn PATH. De accolades ("{...}") dienen als een extra niveau van quoting; ze bakenen in zekere mate af waartoe "$" zal evalueren, zodat de shell niet in verwarring raakt door de tekst die er onmiddellijk opvolgt, (in dit geval "/bin"). Hier is nog een voorbeeld van het effect van accolades:
/home/larry# echo ${HOME}foo
/home/larryfoo
/home/larry#
|
Zonder de accolades zou ik niets krijgen, aangezien er geen omgevingsvariabele met de naam HOMEfoo bestaat.
/home/larry#echo $HOMEfoo /home/larry# |
Laat me iets anders in dat pad verduidelijken: de betekenis van "${PATH}". Het neemt de waarde van een PATH variabele op in mijn nieuwe PATH die eerder werd ingesteld. Waar werd de oude variabele ingesteld? Het bestand /etc/profile dient als een soort globaal .bash_profile dat voor alle gebruikers gelijk is. Een dergelijk gecentraliseerd bestand maakt het makkelijker voor de systeembeheerder om een nieuwe directory toe te voegen aan ieders PATH of iets dergelijks, zonder ze allemaal individueel te hoeven wijzigen. Als je het oude directorypad in je nieuwe directorypad opneemt, dan raak je geen directory's kwijt die het systeem reeds voor je heeft ingesteld.
Je kunt ook zelf bepalen hoe je prompt eruit komt te zien. Dit wordt bewerkstelligd door het instellen van de waarde van de omgevingsvariabele PS1. Persoonlijk wil ik een prompt die me het directorypad van de huidige werkdirectory toont. Ik bewerkstellig dit als volgt in mijn .bashrc:
export PS1='$PWD# ' |
Zoals je kunt zien, worden er hier in feite twee variabelen gebruikt. Degene die wordt ingesteld is PS1, en het wordt ingesteld op de waarde van PWD, waaraan kan worden gedacht als de "Print Werk Directory" of "DirectoryPad van WerkDirectory". Maar de evaluatie van PWD vindt plaats binnen enkele aanhalingstekens. De enkele aanhalingstekens dienen om de expressie daartussenin te evalueren, welke zelf de variabele PWD evalueert. Als je de opdracht export PS1=$PWD gaf, dan zou je in je prompt constant het pad naar de huidige directory worden weergegeven op moment dat PS1 werd ingesteld, in plaats van dat het werd bijgewerkt zodra je van directory wijzigt. Dat schept wat verwarring, en echt belangrijk is het niet. Onthoud gewoon dat je de aanhalingstekens nodig hebt als je wilt dat de huidige directory wordt weergegeven in je prompt.
Misschien geef je de voorkeur aan export PS1='$PWD>', of zelfs de systeemnaam: export PS1=`hostname`'>'. Laat me dat laatste voorbeeld wat verder uitdiepen.
In het laatste voorbeeld werd gebruik gemaakt van een nieuw type quoting, de back quotes. Deze aanhalingstekens bieden nergens bescherming tegen. Je zult bemerken dat "hostname" nergens in de prompt verschijnt wanneer je dat uitvoert. Wat er in werkelijkheid gebeurt is dat de opdracht tussen de aanhalingstekens wordt geëvalueerd, en de uitvoer in plaats van de aanhalingstekens en de opdrachtnaam wordt geplaatst.
Probeer echo `ls` of wc `ls`. Als je wat meer ervaren bent in het gebruik van de shell, dan zal deze techniek steeds krachtiger worden.
Er valt heel wat meer te configureren aan .bashrc, maar daarvoor is hierin niet voldoende ruimte om het allemaal uit te leggen. Voor meer informatie kun je de manpage van bash lezen of vragen stellen aan ervaren gebruikers van Bash. Hier is een volledig .bashrc bestand voor je om te bestuderen; het is vrij standaard, alhoewel het zoekpad wat lang is.
# wat willekeurige opdrachten:
ulimit -c unlimited
export history_control=ignoredups
export PS1='$PWD>'
umask 022
# toepassingsspecifieke paden:
export MANPATH=/usr/local/man:/usr/man
export INFOPATH=/usr/local/info
export PGPPATH=${HOME}/.pgp
# maak het PATH aan:
homepath=${HOME}:~/bin
stdpath=/bin:/usr/bin:/usr/local/bin:/usr/ucb/:/etc:/usr/etc:/usr/games
pubpath=/usr/public/bin:/usr/gnusoft/bin:/usr/local/contribs/bin
softpath=/usr/bin/X11:/usr/local/bin/X11:/usr/TeX/bin
export PATH=.:${homepath}:${stdpath}:${pubpath}:${softpath}
# Technisch gesproken, waren de accolades niet nodig, omdat de dubbele
# punten geldige scheidingstekens zijn; niettemin is het een goede
# gewoonte gebruik te maken van accolades, en ze kunnen geen kwaad.
# aliassen
alias ls="ls -CF"
alias fg1="fg %1"
alias fg2="fg %2"
alias tba="talk sussman@tern.mcs.anl.gov"
alias tko="talk kold@cs.oberlin.edu"
alias tji="talk jimb@totoro.bio.indiana.edu"
alias mroe="more"
alias moer="more"
alias email="emacs -f vm"
alias pu=pushd
alias po=popd
alias b="~/.b"
alias ds=dirs
alias ro="rm *~; rm .*~"
alias rd="rmdir"
alias ll="ls -l"
alias la="ls -a"
alias rr="rm -r"
alias md="mkdir"
alias ed2="emacs -d floss:0 -fg \"grey95\" -bg \"grey50\""
function gco
{
gcc -o $1 $1.c -g
}
|
| [1] | Nu zie je waarom shellprogramma's zo belangrijk zijn. Stel dat je met de hand een gehele omgeving moest doorgeven elke keer dat je een programma aanriep! |
| [2] | Denk eraan dat je altijd programma's in de huidige directory uit kunt voeren door expliciet het pad op te geven, d.w.z.: "./foo". |