1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
|
<!doctype linuxdoc system>
<linuxdoc>
<!--
Een introductie bashshell programmeren
-->
<!-- Titel informatie -->
<article>
<titlepag>
<title>BASH Programmering - Introductie HOW-TO</title>
<author><name>door Mike G <tt>mikkey at dynamo.com.ar</tt><newline>
Vertaald door: Ellen Bokhorst, bokkie at nl.linux.org</name></author>
<date>Do jul 27 09:36:18 ART 2000</date>
<abstract>
Dit artikel is bedoeld om je te helpen een begin te maken met
het programmeren van basis tot middelmatige shell-scripts.
Het is niet bedoeld als een document voor gevorderden (zie de titel).
Ik ben GEEN expert noch een goeroe shellprogrammeur.
Ik besloot dit te schrijven omdat ik er veel van zal leren en het wellicht
van nut kan zijn voor andere mensen. Feedback wordt zeer gewaardeerd vooral
in de vorm van een patch :).
</abstract>
</titlepag>
<!-- Inhoudsopgave -->
<toc>
<!-- Requisites -->
<sect>
<heading>Introductie</heading>
<sect1>
<heading>Ophalen van de laatste versie</heading>
<p>
<htmlurl url="http://www.linuxdoc.org/HOWTO/Bash-Prog-Intro-HOWTO.html"
name="http://www.linuxdoc.org/HOWTO/Bash-Prog-Intro-HOWTO.html">
</p>
</sect1>
<sect1>
<heading>Benodigdheden</heading>
<p> Bekendheid met GNU/Linux opdrachtregels, en bekendheid met
basisprogrammeerconcepten is prettig. Alhoewel dit geen introductie
is in programmeren, wordt er van veel basisconcepten een uitleg
gegeven (of op z'n minst wordt dit geprobeerd).
</p>
</sect1>
<sect1>
<heading>Gebruik van dit document</heading>
<p>Dit document probeert te helpen bij de volgende situtaties
<itemize>
<item>Je hebt een idee over programmeren en je wilt beginnen
een aantal shellscripts te coderen.</item>
<item>Je hebt een vaag idee over shellprogrammering en wilt
een soort van referentie.</item>
<item>Je wilt een aantal shellscripts zien met wat opmerkingen
om te beginnen je eigen shellscripts te gaan schrijven.</item>
<item>Je gaat over van DOS/Windows (of je deed dit al) en wil
"batch" processen maken.</item>
<item>Je bent een echte nerd en leest iedere beschikbare how-to
</item>
</itemize></p>
</sect1>
</sect>
<!-- Zeer eenvoudige Scripts -->
<sect><heading>
Zeer eenvoudige Scripts</heading>
<p>In deze HOW-TO wordt getracht je een aantal hints te geven over
shellscriptprogrammering welke sterk is gebaseerd op voorbeelden.</p>
<p>In deze sectie zijn een aantal kleine scripts te vinden die je
hopelijk op weg helpen een aantal technieken te doorgronden.</p>
<sect1><heading>
Traditioneel hello world script</heading>
<p><!-- __TO-DO__
someone suggested using this instead of the line numbering
but it doesn't work on verion 1.0.9
<pre></pre>
--></p>
<p>
<tscreen><verb>
#!/bin/bash
echo Hello World
</verb></tscreen></p>
<p>Dit script bestaat slechts uit twee regels.
In de eerste regel wordt het systeem duidelijk gemaakt welk programma
moet worden gebruikt om het bestand uit te voeren.</p>
<p>De tweede regel bestaat uit alleen die actie waarmee 'Hello World!
op de terminal wordt afgedrukt.</p>
<p>Als je iets krijgt als <it>./hello.sh: Command not found.</it>
is waarschijnlijk de eerste regel `#!/bin/bash' niet goed, roep
`whereis bash' of zie `bash zoeken' om te bezien hoe je deze regel
zou moeten schrijven.</p>
</sect1>
<sect1>
<heading>Een zeer eenvoudig backupscript</heading>
<p>
<tscreen><verb>
#!/bin/bash
tar -cZf /var/my-backup.tgz /home/me/
</verb></tscreen></p>
<p>In plaats dat er nu een bericht op de terminal wordt weergegeven,
maken we in dit script een tar-ball van de homedirectory van een
gebruiker. Het is niet de bedoeling dat dit wordt gebruikt. Later in
dit document wordt een handiger backupscript gepresenteerd.</p>
</sect1>
</sect>
<!-- Omleiding -->
<sect><heading>Alles over omleiding</heading>
<sect1><heading>Theorie en snelle naslag</heading>
<p>Er zijn 3 file descriptors, stdin, stdout en stderr (std=standaard).
</p>
<p>In principe kun je:
<enum>
<item> stdout naar een bestand omleiden</item>
<item> stderr naar een bestand omleiden</item>
<item> stdout naar stderr omleiden</item>
<item> stderr naar stdout omleiden</item>
<item> stderr en stdout naar een bestand omleiden</item>
<item> stderr en stdout naar stdout omleiden</item>
<item> stderr en stdout naar stderr omleiden</item>
</enum>
1 is een representatie van stdout en 2 van stderr.</p>
<p> Een opmerking hierover: met de opdracht less kun je zowel
stdout (welke in de buffer blijft) en stderr, welke op het scherm
wordt weergegeven, bekijken, maar is verwijderd als je door de buffer
probeert te 'bladeren'.
</p>
</sect1>
<sect1><heading>Voorbeeld: stdout naar bestand</heading>
<p>Hiermee wordt de uitvoer van een programma naar een bestand
weggeschreven.
<tscreen><verb>
ls -l > ls-l.txt
</verb></tscreen>
Hier zal een bestand, genaamd 'ls-l.txt' worden aangemaakt en hierin
zal worden opgevangen wat je krijgt als je de opdracht 'ls -l' typt
en uitvoert.</p>
</sect1>
<sect1><heading>Voorbeeld: stderr naar bestand</heading>
<p> Dit zorgt dat de uitvoer van stderr van een programma naar
een bestand wordt weggeschreven.
<tscreen><verb>
grep da * 2> grep-errors.txt
</verb></tscreen>
Hier zal een bestand, genaamd 'grep-errors.txt' worden aangemaakt en
hier zal dan het stderr gedeelte van de uitvoer in staan van de
opdracht 'grep da *'.</p>
</sect1>
<sect1><heading>Voorbeeld: stdout naar stderr</heading>
<p>Dit zorgt dat de sterr uitvoer van een programma naar
dezelfde filedescriptor als stdout wordt weggeschreven.
<tscreen><verb>
grep da * 1>&2
</verb></tscreen>
Hier wordt het stdout gedeelte van de opdracht naar stderr gezonden,
je merkt dat mogelijk op verschillende manieren.</p>
</sect1>
<sect1><heading>Voorbeeld: stderr naar stdout</heading>
<p> Dit zorgt dat de stderr uitvoer van een programma naar
dezelfde filedescriptor wordt weggeschreven als stdout.
<tscreen><verb>
grep * 2>&1
</verb></tscreen>
Hier wordt het stderr gedeelte van de opdracht naar stdout gestuurd,
als je middels een pipe-symbool de uitvoer naar less stuurt, zul je
zien dat de regels die normaal gesproken 'verdwijnen' (als ze naar
stderr worden geschreven) nu behouden blijven (omdat ze zich op
stdout bevinden).</p>
</sect1>
<sect1><heading>Voorbeeld: stderr en stdout naar bestand</heading>
<p> Hiermee zal alle uitvoer van een programma in een bestand worden
geplaatst. Soms is dit geschikt voor cron entries, als je een opdracht
in absolute stilte wilt laten uitvoeren.
<tscreen><verb>
rm -f $(find / -name core) &> /dev/null
</verb></tscreen>
Dit (denkend aan een cron entry) zal ieder bestand genaam 'core'
in iedere directory verwijderen. Merk op dat je er nogal zeker
van moet zijn wat een opdracht doet als je de uitvoer ervan gaat
verwijderen.</p>
</sect1>
<!--
redirect stderr en stdout naar stdout
redirect stderr en stdout naar stderr
-->
<!--
<sect1>
Voorbeeld:
<p> Hiermee zal
<tscreen><verb>
opdracht
</verb></tscreen>
Uitleg
</sect1>
-->
</sect>
<!-- pipes -->
<sect><heading>Pipes</heading>
<p>In deze sectie wordt op een zeer eenvoudige en praktische wijze uitgelegd
hoe gebruik te maken van het pipe-symbool en waarvoor je het zou kunnen
gebruiken.
</p>
<sect1><heading>
Wat het zijn en waarvoor je het zou kunnen gebruiken</heading>
<p> Pipe-symbolen laten je gebruik maken van (zeer eenvoudig uitgelegd) de
uitvoer van een programma als de invoer van een ander programma.</p>
</sect1>
<sect1><heading>Voorbeeld: eenvoudige pipe met sed</heading>
<p> Dit is een zeer eenvoudige manier om gebruik te maken van een
pipe-symbool.
<tscreen><verb>
ls -l | sed -e "s/[aeio]/u/g"
</verb></tscreen>
Wat er hier gebeurt is het volgende: eerst wordt de opdracht 'ls -l'
uitgevoerd, en in plaats dat de uitvoer ervan wordt afgedrukt, wordt
het naar het programma sed gestuurd (piped), wat op zijn beurt afdrukt wat
het moet afdrukken.</p>
</sect1>
<sect1><heading>Voorbeeld: een alternatief voor ls -l *.txt</heading>
<p> Waarschijnlijk is dit een moeilijkere manier om een ls -l *.txt uit
te voeren, maar het dient hier ter illustratie, niet voor het oplossen van
een opsommingsdilemma.
<tscreen><verb>
ls -l | grep "\.txt$"
</verb></tscreen>
Hier wordt de uitvoer van het programma ls -l naar het grep programma
gezonden, welke op zijn beurt de regels afdrukt die overeenkomen met de
reguliere expressie "\.txt$".</p>
</sect1>
<!--
<sect1>
Voorbeeld:
<p> Dit zal
<tscreen><verb>
opdracht
</verb></tscreen>
Uitleg
</sect1>
-->
</sect>
<!-- Variabelen -->
<sect><heading>
Variabelen</heading>
<!-- Droge Theorie -->
<p> Je kunt net als in iedere andere programmeertaal gebruik maken
van variabelen. Er zijn geen gegevenstypen.
Onder bash kan een variabele bestaan uit een nummer, een teken, of een
reeks tekens.</p>
<p>Je hoeft een variabele niet te declareren. Door er slechts een
waarde aan toe te kennen zal het worden aangemaakt.</p>
<!-- Voorbeelden -->
<sect1><heading>
Voorbeeld: Hello World! door gebruik te maken van variabelen</heading>
<p>
<tscreen><verb>
#!/bin/bash
STR="Hello World!"
echo $STR
</verb></tscreen></p>
<p>In regel 2 wordt een variabele aangemaakt met de naam STR
en hieraan wordt de string "Hello World!" toegekend.
De WAARDE van deze variabele wordt vervolgens opgehaald door er
een '$' voor te plaatsen.
Let er alsjeblieft op (probeer het uit!) dat als je het '$' teken
niet gebruikt, de uitvoer van het programma iets anders zal zijn,
en het waarschijnlijk niet datgene zal zijn wat je wilt dat 't is.
</p>
</sect1>
<sect1><heading>
Voorbeeld: Een zeer eenvoudig backupscript (iets beter)</heading>
<p>
<tscreen><verb>
#!/bin/bash
OF=/var/my-backup-$(date +%Y%m%d).tgz
tar -cZf $OF /home/me/
</verb></tscreen></p>
<p>In dit script wordt iets anders geïntroduceerd. Ten
eerste zou je nu bekend moeten zijn met de aanmaak van de
variabele en de toekenning in regel 2. Let op de expressie
`$(date +%Y%m%d)'.
Als je het script uit laat voeren, zal je bemerken dat het
de opdracht binnen de haakjes uitvoert, en de uitvoer ervan
afvangt.</p>
<p>Merk op dat de naam van het bestand, de uitvoer, iedere dag
anders zal zijn, vanwege de opmaakoptie van de opdracht date
(+%Y%m%d). Je kunt dit wijzigen door een ander formaat op te geven.
</p>
<p>Nog wat meer voorbeelden:</p>
<p> echo ls</p>
<p> echo $(ls)</p>
</sect1>
<sect1><heading>Lokale variabelen</heading>
<p> Lokale variabelen kunnen worden aangemaakt door gebruik te maken van het
keyword <it>local</it>.
<tscreen><verb>
#!/bin/bash
HELLO=Hello
function hello {
local HELLO=World
echo $HELLO
}
echo $HELLO
hello
echo $HELLO
</verb></tscreen>
Dit voorbeeld zou genoeg moeten zijn om aan te tonen hoe je
een lokale variabele gebruikt.
</p>
</sect1>
</sect>
<!-- Voorwaardelijke opdrachten -->
<sect>
<heading>Voorwaardelijke opdrachten</heading>
<p>Voorwaardelijke opdrachten laten je een beslissing nemen of een
actie wel of niet zal worden uitgevoerd, de beslissing wordt genomen door
de waarde te bepalen van een expressie.</p>
<!-- Droge Theorie -->
<sect1><heading>
Droge Theorie</heading>
<p>Voorwaardelijke opdrachten bestaan er in veel vormen.
De basisvorm is:
<bf>if</bf> <it/expressie</it> <bf>then</bf> <it/statement</it>
hierbij wordt 'statement' alleen uitgevoerd wanneer de 'expressie'
de waarde true oplevert.</p>
<p>`2<1' is een expressie die de waarde false oplevert,
terwijl `2>1'
de waarde true oplevert.</p>
<p> Er zijn nog andere vormen voorwaardelijke opdrachten, zoals:
<bf/if/ <it/expressie/
<bf/then/ <it/statement1/ <bf/else/ <it/statement2/.
Hier wordt 'statement1' uitgevoerd als de 'expressie' true
oplevert, anders wordt 'statement2' uitgevoerd.</p>
<p>Nog een andere vorm van een voorwaardelijke opdracht is:
<bf/if/ <it/expressie1/
<bf/then/ <it/statement1/
<bf/else if/ <it/expressie2/ <bf/then/ <it/statement2/
<bf/else/ <it/statement3/
In deze vorm is slechts de
"ELSE IF 'expressie2' THEN 'statement2'" toegevoegd wat
maakt dat statement2 wordt uitgevoerd als expressie2 de waarde true
oplevert. De rest is wat je er zelf van maakt.
(zie vorige vormen).</p>
<p> Iets over syntax:</p>
<p> De basis van een 'if' constructie onder bash is:</p>
<p> if [expressie];</p>
<p> then</p>
<p> code als 'expressie' is true.</p>
<p> fi</p>
</sect1>
<!-- Voorbeelden -->
<sect1><heading>
Voorbeeld: Basis voorwaardelijke opdracht if .. then</heading>
<p>
<tscreen><verb>
#!/bin/bash
if [ "foo" = "foo" ]; then
echo waarde van expressie leverde true op
fi
</verb></tscreen>
</p>
<p>De code die zal worden uitgevoerd als de expressie tussen de
blokhaken true oplevert, is te vinden achter het 'then' woord en
voor het 'fi' woord waarmee het einde van de voorwaardelijk
uit te voeren code wordt aangegeven.</p>
</sect1>
<sect1><heading>
Voorbeeld: Voorbeeld basis voorwaardelijke opdracht if .. then ... else
</heading>
<p>
<tscreen><verb>
#!/bin/bash
if [ "foo" = "foo" ]; then
echo expressie levert de waarde true op
else
echo expressie levert de waarde false op
fi
</verb></tscreen></p>
</sect1>
<sect1><heading>
Voorbeeld: Voorwaardelijke opdrachten met variabelen</heading>
<p>
<tscreen><verb>
#!/bin/bash
T1="foo"
T2="bar"
if [ "$T1" = "$T2" ]; then
echo expressie levert de waarde true op
else
echo expressie levert de waarde false op
fi
</verb></tscreen></p>
</sect1>
</sect>
<!--
<sect1>
Voorbeeld: testen of een bestand bestaat
<p> nog één met dank aan mike
<tscreen><verb>
#!/bin/bash
FILE=~/.basrc
if [ -f $FILE ]; then
echo bestand $FILE bestaat
else
echo bestand niet gevonden
fi
if [ 'test -f $FILE']
</verb></tscreen>
</sect1>
-->
<!-- Loops : for, while en until -->
<sect>
<heading>Loops for, while en until</heading>
<!-- Droge Theorie -->
<p>In deze sectie tref je for, while en until loops aan.</p>
<p> De <bf/for/ loop werkt iets anders dan in andere programmeertalen.
Eigenlijk laat het je een serie 'woorden' binnen een string herhalen.</p>
<p>De <bf/while/ voert een stuk code uit als de controlerende
expressie true oplevert, en stopt alleen wanneer het false is
(of als er binnen de
uitgevoerde code een expliciete break werd gevonden).</p>
<p>De <bf/until/ loop is bijna gelijk aan de while loop behalve dat
de code wordt uitgevoerd terwijl de controlerende expressie de waarde
false oplevert.</p>
<p>Als je het vermoeden hebt dat while en until erg op elkaar lijken, heb je gelijk.
<!-- Voorbeelden --></p>
<sect1>
<heading>Voorbeeld met for</heading>
<p>
<tscreen><verb>
#!/bin/bash
for i in $( ls ); do
echo item: $i
done
</verb></tscreen>
</p>
<p>In de tweede regel, declareren we i als variabele voor de
verschillende waarden in $( ls ).</p>
<p>De derde regel zou zonodig langer kunnen zijn, of er zouden meer
regels voor kunnen komen voor de done (4).</p>
<p> `done' (4) geeft aan dat de code die de waarde van $i gebruikte
beëindigd is en $i een nieuwe waarde aan kan nemen.</p>
<p>Dit script heeft weinig nut, en een nuttiger wijze om gebruik te
maken van de for loop zou zijn het in het voorgaande voorbeeld
te gebruiken waarbij slechts bepaalde bestanden overeenkomen.</p>
</sect1>
<sect1><heading>Met C vergelijkende for</heading>
<p> fiesh raadde het toevoegen van deze vorm van een loop aan.
Het is een for loop
meer vergelijkbaar met die van C/perl...
<tscreen><verb>
#!/bin/bash
for i in `seq 1 10`;
do
echo $i
done
</verb></tscreen>
</p>
</sect1>
<!-- while -->
<sect1>
<heading>Voorbeeld met while</heading>
<p> <tscreen><verb>
#!/bin/bash
COUNTER=0
while [ $COUNTER -lt 10 ]; do
echo De teller is $COUNTER
let COUNTER=COUNTER+1
done
</verb></tscreen>
</p>
<p> Dit script 'emuleert' de welbekende
(C, Pascal, perl, enz) 'for' structuur</p>
</sect1>
<!-- until -->
<sect1>
<heading>Until voorbeeld</heading>
<p> <tscreen><verb>
#!/bin/bash
COUNTER=20
until [ $COUNTER -lt 10 ]; do
echo COUNTER $COUNTER
let COUNTER-=1
done
</verb></tscreen></p>
</sect1>
</sect>
<!-- Functions -->
<sect>
<heading>Functies</heading>
<p>Zoals in bijna iedere programmeertaal, kun je functies gebruiken om
stukken code op een wat logischer wijze te groeperen of te oefenen in
de goddelijke kunst van recursie.</p>
<p>Het declareren van een functie is slecht een kwestie van het
schrijven van function mijn_func { mijn_code }.</p>
<p>Het aanroepen van een functie is net als het aanroepen van ieder ander
programma, je tikt gewoon de naam ervan in.</p>
<sect1>
<heading>Voorbeeld van een functie</heading>
<p> <tscreen><verb>
#!/bin/bash
function quit {
exit
}
function hello {
echo Hello!
}
hello
quit
echo foo
</verb></tscreen></p>
<p>In de regels 2-4 staat de 'quit' functie.
In de regels 5-7 staat de 'hello' functie.
Als je er niet geheel zeker van bent wat dit script doet, probeer
het dan uit!</p>
<p>Merk op dat functies niet in een specifieke volgorde hoeven te
staan.</p>
<p>Bij het uitvoeren van het script, zal je bemerken dat als eerste
de functie 'hello' wordt aangeroepen en ten tweede de 'quit'
functie en de functie komt nooit bij regel 10.</p>
</sect1>
<sect1><heading>Voorbeeld van een functie met parameters</heading>
<p> <tscreen><verb>
#!/bin/bash
function quit {
exit
}
function e {
echo $1
}
e Hello
e World
quit
echo foo
</verb></tscreen>
Dit script is bijna identiek aan het voorgaande script. Het belangrijkste
verschil is de functie 'e'. Deze functie drukt het eerste argument dat het
ontvangt af. Argumenten binnen functies worden op dezelfde wijze behandeld
als argumenten die aan het script worden gegeven.
</p>
</sect1>
</sect>
<!-- User interfaces -->
<sect>
<heading>Gebruikersinterfaces</heading>
<!-- Eenvoudige menu's met select -->
<sect1>
<heading>Het gebruik van select om eenvoudige menu's te maken</heading>
<p>
<tscreen><verb>
#!/bin/bash
OPTIONS="Hello Quit"
select opt in $OPTIONS; do
if [ "$opt" = "Quit" ]; then
echo klaar
exit
elif [ "$opt" = "Hello" ]; then
echo Hello World
else
clear
echo onjuiste keuze
fi
done
</verb></tscreen> </p>
<p>Als je dit script uitvoert, zal je zien dat dit de droom is
van een programmeur voor op tekst gebaseerde menu's.
Je zal waarschijnlijk bemerken dat het erg lijkt op de 'for'
instructie, in plaats van dat ieder 'woord' in $OPTIONS wordt
doorlopen, geeft het de gebruiker een prompt.
</p>
</sect1>
<!-- Gebruik maken van de opdrachtregel -->
<sect1><heading>
Gebruik maken van de opdrachtregel</heading>
<p>
<tscreen><verb>
#!/bin/bash
if [ -z "$1" ]; then
echo usage: $0 directory
exit
fi
SRCD=$1
TGTD="/var/backups/"
OF=home-$(date +%Y%m%d).tgz
tar -cZf $TGTD$OF $SRCD
</verb></tscreen>
</p>
<p>Het zou je duidelijk moeten zijn wat dit script doet.
De expressie in de eerste voorwaardelijke opdracht test of het
programma een argument ($1) meekreeg en stopt als het dit niet deed,
waarbij een kort bericht over het gebruik aan de gebruiker wordt
getoond. De rest van het script zou vanaf hier duidelijk moeten zijn.
</p>
</sect1>
</sect>
<!-- Misc -->
<sect>
<heading>Diversen</heading>
<!-- Inlezen van gebruikersinvoer -->
<sect1>
<heading>Inlezen van gebruikersinvoer met read</heading>
<p>In veel situaties wil je de gebruiker wellicht vragen om wat invoer, en
er zijn verscheidene manieren om dit te bereiken:
Dit is één van die manieren:
<tscreen><verb>
#!/bin/bash
echo Vul alsjeblieft je naam in
read NAME
echo "Hi $NAME!"
</verb></tscreen>
Een variant daarop zal het volgende voorbeeld verduidelijken waarmee
je meerdere waarden met read kan verkrijgen.
<tscreen><verb>
#!/bin/bash
echo Vul alsjeblieft je voornaam en achternaam in
read FN LN
echo "Hi! $LN, $FN !"
</verb></tscreen>
</p>
</sect1>
<!-- Arithmetic evaluation -->
<sect1><heading>
Rekenkundige waarde bepalen</heading>
<p>Probeer het volgende achter de opdrachtregel (of in een shell):</p>
<p>echo 1 + 1</p>
<p>Als je verwachtte '2' te zien te krijgen, zal je wel teleurgesteld
zijn. Wat als je wilt dat BASH van een aantal getallen de waarde
bepaalt?
Dit is de oplossing:</p>
<p> echo $((1+1))</p>
<p>Hiermee zal een 'logischer' uitvoer worden geproduceerd.
Dit is voor het bepalen van de waarde van een rekenkundige expressie.
Je kunt dit ook als volgt bereiken:</p>
<p> echo $[1+1]</p>
<p>Als je breuken of meer rekenkunde nodig hebt of dit gewoon wilt,
kun je bc gebruiken om de waarde te bepalen van rekenkundige
expressies.</p>
<p>als ik op de opdrachtregel "echo $[3/4]" opgaf, zou het 0
retourneren omdat bash alleen integers gebruikt bij beantwoording.
Als je "echo 3/4|bc -l" opgaf, zou het op juiste wijze 0.75
retourneren.</p>
</sect1>
<sect1><heading>Bash zoeken</heading>
<p> UIt een bericht van mike (zie Met dank aan) </p>
<p> je gebruikt altijd #!/bin/bash .. je zou wellicht een voorbeeld</p>
<p> kunnen geven van waar bash is te vinden.</p>
<p> de voorkeur is 'locate bash', maar niet op alle computers is</p>
<p> locate geënstalleerd.</p>
<p> `find ./ -name bash' vanaf de rootdir zal gewoonlijk wel werken.</p>
<p> Aanbevolen te doorzoeken locaties:</p>
<p> ls -l /bin/bash</p>
<p> ls -l /sbin/bash</p>
<p> ls -l /usr/local/bin/bash</p>
<p> ls -l /usr/bin/bash</p>
<p> ls -l /usr/sbin/bash</p>
<p> ls -l /usr/local/sbin/bash</p>
<p> (kan zo geen andere directory's bedenken... ik heb het voorheen</p>
<p> meestal wel op een ander systeem op één van deze</p>
<p> plaatsen kunnen vinden.</p>
<p> Je zou ook nog 'which bash' kunnen proberen.</p>
</sect1>
<!-- Capturing a commands output -->
<sect1><heading>De return waarde van een programma verkrijgen</heading>
<p>Onder Bash, wordt de return waarde van een programma in een speciale
variabele, genaamd $?, opgeslagen.</p>
<p>Hiermee wordt geïllustreerd hoe de return waarde van een programma
kan worden afgevangen, waarbij ik ervan uit ga dat de directory <it>dada</it>
niet voorkomt.
(Ook dit was een suggestie van mike)
<tscreen><verb>
#!/bin/bash
cd /dada &> /dev/null
echo rv: $?
cd $(pwd) &> /dev/null
echo rv: $?
</verb> </tscreen>
</p>
</sect1>
<sect1><heading>Afvangen van de uitvoer van een opdracht</heading>
<p>Dit kleine script laat alle tabellen van alle databases zien (ervan
uitgaande dat je MySQL hebt geïnstalleerd). Overweeg tevens het
wijzigen van de opdracht 'mysql' waarbij een geldige gebruikersnaam en
wachtwoord wordt gebruikt.
<tscreen><verb>
#!/bin/bash
DBS=`mysql -uroot -e"show databases"`
for b in $DBS ;
do
mysql -uroot -e"show tables from $b"
done
</verb></tscreen>
</p>
</sect1>
<!-- Meerdere bronbestanden -->
<sect1><heading>Meerdere bronbestanden</heading>
<p>Je kunt met de opdracht source meerdere bestanden gebruiken.</p>
<p> __TO-DO__</p>
</sect1>
</sect>
<!-- Tables -->
<sect>
<heading>Tabellen</heading>
<!-- String operators -->
<sect1>
<heading>Stringvergelijkings operatoren</heading>
<p> (1) s1 = s2</p>
<p> (2) s1 != s2</p>
<p> (3) s1 < s2</p>
<p> (4) s1 > s2</p>
<p> (5) -n s1 </p>
<p> (6) -z s1 </p>
<p> (1) s1 komt overeen met s2</p>
<p> (2) s1 komt niet overeen met s2</p>
<p> (3) __TO-DO__</p>
<p> (4) __TO-DO__</p>
<p> (5) s1 is niet gelijk aan null
(bevat één of meer tekens)</p>
<p> (6) s1 is null </p>
</sect1>
<sect1><heading>Stringvergelijking voorbeelden</heading>
<p> Vergelijken van twee strings.
<tscreen><verb>
#!/bin/bash
S1='string'
S2='String'
if [ $S1=$S2 ];
then
echo "S1('$S1') is niet gelijk aan S2('$S2')"
fi
if [ $S1=$S1 ];
then
echo "S1('$S1') is gelijk aan S1('$S1')"
fi
</verb></tscreen>
</p>
<p> Ik haal hier een opmerking aan vanuit een mail ingezonden door
Andreas Beck, verwijzend naar het gebruik van
<it/if [ $1 = $2 ]/.
</p>
<p>Dit is niet zo'n goed idee, omdat als $S1 of $S2 leeg is, je een
parse error krijgt.
x$1=x$2 of "$1"="$2" is beter.
</p>
</sect1>
<!-- Arithmetic operators -->
<sect1><heading>Rekenkundige operators</heading>
<p> +</p>
<p> -</p>
<p> *</p>
<p> /</p>
<p> % (rest)</p>
</sect1>
<!-- Arithmetic relational operators -->
<sect1><heading>Rekenkundige relationele operators</heading>
<p> -lt (<) </p>
<p> -gt (>)</p>
<p> -le (<=)</p>
<p> -ge (>=)</p>
<p> -eq (==)</p>
<p> -ne (!=)</p>
<p> C programmeurs zouden de operator eenvoudigweg indelen naar
de overeenkomstige haakjes.</p>
</sect1>
<!-- Usefull commands -->
<sect1>
<heading>Handige opdrachten</heading>
<p>Deze sectie werd door Kees herschreven (zie met dank aan...)</p>
<p>Een paar van deze opdrachten zijn bijna volledige programmeertalen.
Van deze opdrachten wordt alleen de basis uitgelegd. Voor een meer
gedetailleerde beschrijving, kun je de man pages van de opdrachten raadplegen.
<bf/sed/ (stream editor)</p>
<p> Sed is een niet interactieve editor. In plaats dat een bestand wordt
aangepast door de cursor op het scherm te verplaatsen, maak je gebruik van
een script met de sed-instructies en geef je deze als argument samen met
het te wijzigen bestand op aan sed. Je kunt sed ook als een filter beschrijven.
Laten we eens wat voorbeelden bekijken:
<tscreen><verb>
$sed 's/te_vervangen/vervangen_door/g' /tmp/dummy
</verb></tscreen>
</p>
<p> Sed vervangt de string 'te_vervangen' door de string 'vervangen_door' en
leest vanuit het /tmp/dummy bestand.
Het resultaat zal naar stdout (normaal gesproken de console) worden
gezonden, maar je kunt aan het einde van deze regel ook '> capture' toevoegen
waardoor sed de uitvoer naar het bestand 'capture' zal sturen.
<tscreen><verb>
$sed 12, 18d /tmp/dummy
</verb></tscreen>
</p>
<p> Sed laat alle regels zien behalve de regels 12 tot 18. Het oorspronkelijke
bestand wordt door deze opdracht niet aangepast.
<bf/awk/ (manipulatie van gegevensbestanden, ophalen en verwerken van tekst)
</p>
<p>Er bestaan veel implementaties van de programmeertaal AWK
(de bekendste interpreters zijn GNU's
gawk en 'new awk' mawk.) Het principe is eenvoudig: AWK scant op een patroon,
en voor ieder overeenkomend patroon zal een actie worden uitgevoerd.
</p>
<p> Wederom heb ik een dummy-bestand aangemaakt met de volgende regels:
</p>
<p> <it/"test123/</p>
<p> <it/test/</p>
<p> <it/tteesstt"/</p>
<p>
<tscreen><verb>
$awk '/test/ {print}' /tmp/dummy
</verb></tscreen></p>
<p> test123</p>
<p> test</p>
<p> Het patroon waar AWK naar zoekt, is 'test' en de actie die het uitvoert
wanneer het een regel in het bestand /tmp/dummy met de string 'test' vindt, is
`print'.
<tscreen><verb>
$awk '/test/ {i=i+1} END {print i}' /tmp/dummy
</verb></tscreen>
3
</p>
<p>Wanneer je naar meer patronen zoekt, kun je de tekst tussen de
aanhalingstekens beter vervangen door '-f file.awk' zodat je alle patronen
en acties in het bestand 'file.awk' kunt plaatsen.
<bf/grep/ (druk regels af overeenkomend met een zoekpatroon)
</p>
<p>We hebben in de vorige hoofdstuk reeds heel wat grep opdrachten gezien,
die de regels laten zien die met een patroon overeenkomen. Maar grep kan meer.
<tscreen><verb>
$grep "zoek naar dit" /var/log/messages -c
</verb></tscreen>
</p>
<p> 12</p>
<p>De string "zoek naar dit" is 12 keer in het bestand /var/log/messages
gevonden.
</p>
<p> [ok, dit voorbeeld was nep, het bestand /var/log/messages was aangepast :-)]
<bf/wc/ (telt regels, woorden en bytes)
</p>
<p>In het volgende voorbeeld, zien we dat de uitvoer niet hetgeen is wat we
ervan verwachtte. Het dummy bestand, zoals in dit voorbeeld gebruikt, bevat
de volgende tekst:
<it/"bash introduction/
<it/ howto test file"/
<tscreen><verb>
$wc --words --lines --bytes /tmp/dummy
</verb></tscreen>
</p>
<p> 2 5 34 /tmp/dummy</p>
<p>De volgorde van de parameters doet er voor wc niet toe. Wc drukt ze altijd
in de standaardvolgorde af, dus zoals je kunt zien:
<lines><words><bytes><filename>.
<bf>sort</bf> (sorteer regels van tekstbestanden)</p>
<p> Ditmaal bevat het dummy bestand de volgende tekst:</p>
<p> <it/"b/</p>
<p> <it/c/</p>
<p> <it/a"/
<tscreen><verb>
$sort /tmp/dummy
</verb></tscreen></p>
<p> Zo ziet de uitvoer er ongeveer uit:</p>
<p> <it>a</it></p>
<p> <it>b</it></p>
<p> <it>c</it></p>
<p> Opdrachten zouden niet zo eenvoudig moeten zijn :-)
<bf>bc</bf> (een calculator programmeertaal)</p>
<p>Met Bc kunnen berekeningen vanaf de opdrachtregel worden gemaakt
(invoer vanuit een bestand, niet via omleiding of een pipe
maar wel vanuit een gebruikersinterface.)
Het volgende demonstreert een aantal opdrachten. Merk op dat ik gebruik
maak van de parameter -q om een welkomstbericht te voorkomen.
<tscreen><verb>
$bc -q
</verb></tscreen>
</p>
<p> <it>1 == 5</it></p>
<p> <it>0</it></p>
<p> <it>0.05 == 0.05</it></p>
<p> <it>1</it></p>
<p> <it>5 != 5</it></p>
<p> <it>0</it></p>
<p> <it>2 ^ 8</it></p>
<p> <it>256</it></p>
<p> <it>sqrt(9)</it></p>
<p> <it>3</it></p>
<p> <it>while (i != 9) {</it></p>
<p> <it>i = i + 1;</it></p>
<p> <it>print i</it></p>
<p> <it>}</it></p>
<p> <it/123456789/</p>
<p> <it/quit/
<bf/tput/ (initialiseer een terminal of ondervraag een terminfo database)
</p>
<p> Een kleine demonstratie van de mogelijkheden van tput:
<tscreen><verb>
$tput cup 10 4
</verb></tscreen></p>
<p> De prompt verschijnt op (y10,x4).
<tscreen><verb>
$tput reset
</verb></tscreen></p>
<p> Maak het scherm schoon en de prompt verschijnt op (y1,x1).
Merk op dat (y0,x0) de linkerbovenhoek is.
<tscreen><verb>
$tput cols
</verb></tscreen>
<it/80/</p>
<p> Toont het aantal mogelijke tekens in richting x.</p>
<p> Het is zeer aan te bevelen (op z'n minst) met deze programma's bekend
te zijn. Er zijn heel veel van deze kleine programma's die je echte
magie op de opdrachtregel laten doen.</p>
<p> [een aantal voorbeelden zijn overgenomen uit de man pages of FAQ's]
</p>
</sect1>
</sect>
<!-- Meer Scripts -->
<sect>
<heading>Meer Scripts</heading>
<sect1><heading>
Een opdracht toepassen voor alle bestanden in een directory.
</heading>
</sect1>
<sect1><heading>
Voorbeeld: Een zeer eenvoudig backupscript (iets beter)
</heading>
<p>
<tscreen><verb>
#!/bin/bash
SRCD="/home/"
TGTD="/var/backups/"
OF=home-$(date +%Y%m%d).tgz
tar -cZf $TGTD$OF $SRCD
</verb></tscreen>
</p>
</sect1>
<sect1><heading>
Bestandshernoemer</heading>
<p>
<tscreen><verb>
#!/bin/sh
# renna: hernoem meerdere bestanden op basis van verscheidene
# regels geschreven door felix hudson jan - 2000
# controleer dit bestand eerst op de diverse 'modes'
# als de eerste ($1) voorwaarde overeenkomt dan voeren we dat
# deel van het programma uit en stoppen
# controleer op de prefix voorwaarde
if [ $1 = p ]; then
# nu ontdoen we ons van de mode ($1) variabele en prefix ($2)
prefix=$2 ; shift ; shift
# een snelle controle of er bestanden werden opgegeven
# als dit niet zo is dan kunnen we maar beter niets doen dan een
# een aantal niet bestaande bestanden te hernoemen!!
if [$1 = ]; then
echo "geen bestanden opgegeven"
exit 0
fi
# deze for loop verwerkt alle bestanden die we aan het programma
# opgaven
# het hernoemt per opgegeven naam
for file in $*
do
mv ${file} $prefix$file
done
# we verlaten nu het programma
exit 0
fi
# controle op het hernoemen van een suffix
# de rest van dit gedeelte is vrijwel gelijk aan de vorige sectie
# kijk alsjeblieft in die notities
if [ $1 = s ]; then
suffix=$2 ; shift ; shift
if [$1 = ]; then
echo "geen bestanden opgegeven"
exit 0
fi
for file in $*
do
mv ${file} $file$suffix
done
exit 0
fi
# controleer op de replacement hernoeming
if [ $1 = r ]; then
shift
# ik nam deze op uit voorzorg dat er geen bestanden beschadigd
# raken als de gebruiker niets opgeeft
# slechts een veiligheidsmaatregel
if [ $# -lt 3 ] ; then
echo "usage: renna r [expression] [replacement] files... "
exit 0
fi
# verwijder andere informatie
OLD=$1 ; NEW=$2 ; shift ; shift
# deze for loop verwerkt alle bestanden die we aan het programma
# opgaven, het hernoemt één bestand tegelijkertijd
# door gebruik te maken van het programma 'sed'
# dit is een eenvoudig opdrachtregelprogramma dat standaardinvoer
# verwerkt en een expressie vervangt door een opgegeven string
# hier geven we het de bestandsnaam door (als standaardinvoer) en
# vervangen de nodige tekst
for file in $*
do
new=`echo ${file} | sed s/${OLD}/${NEW}/g`
mv ${file} $new
done
exit 0
fi
# als we hier zijn aangekomen dat wil dat zeggen dat niets
# zinnigs aan het programma werd doorgegeven, dus vertellen
# we de gebruiker hoe het te gebruiken
echo "Gebruik;"
echo " renna p [prefix] files.."
echo " renna s [suffix] files.."
echo " renna r [expression] [replacement] files.."
exit 0
# done!
</verb></tscreen>
</p>
</sect1>
<sect1><heading>
Bestandshernoemer (eenvoudig)</heading>
<p>
<tscreen><verb>
#!/bin/bash
# renames.sh
# basis bestandshernoemer
criteria=$1
re_match=$2
replace=$3
for i in $( ls *$criteria* );
do
src=$i
tgt=$(echo $i | sed -e "s/$re_match/$replace/")
mv $src $tgt
done
</verb></tscreen>
</p>
</sect1>
</sect>
<sect>
<heading>Wanneer er iets niet goed gaat (debuggen)</heading>
<sect1><heading>
Manieren om BASH aan te roepen</heading>
<p>Aardig om te doen is het volgende op de eerste regel toe te voegen
<tscreen><verb>
#!/bin/bash -x
</verb></tscreen></p>
<p>Hierdoor zal wat interessante uitvoer worden geproduceerd</p>
</sect1>
</sect>
<sect>
<heading>Over het document</heading>
<p>Geef me gerust je aanbevelingen/correcties, of wat je dan ook
interessant vindt om in dit document terug te vinden. Ik zal het
zo spoedig mogelijk bijwerken.</p>
<sect1>
<heading>(geen) garantie</heading>
<p>Dit document wordt zonder enige garantie geleverd.</p>
</sect1>
<sect1><heading>Vertalingen</heading>
<p> Italiaans: door William Ghelfi (wizzy at tiscalinet.it)
<htmlurl name="is hier" url="http://web.tiscalinet.it/penguin_rules">
</p>
<p> Frans: door Laurent Martelli
<htmlurl name="ontbreekt" url="http://"></p>
<p>
Koreaans: Minseok Park
<htmlurl url="http://kldp.org" name="http://kldp.org"></p>
<p>
Koreaans: Chun Hye Jin
<htmlurl url="" name="unknown"></p>
<p> Spaans: onbekend
<htmlurl url="http://www.insflug.org" name="http://www.insflug.org"></p>
<p> Ik denk dat er meer vertalingen zijn, maar heb daar geen informatie
over, mail het alsjeblieft naar me als je hier informatie over hebt, zodat
ik deze sectie kan bijwerken.</p>
</sect1>
<sect1>
<heading>Met dank aan</heading>
<p>
<itemize>
<item>Mensen die dit document naar andere talen vertaalde (vorige
sectie).</item>
<item> Nathan Hurst voor het opsturen van heel wat correcties.</item>
<item> Jon Abbott voor het opsturen van opmerkingen over het
berekenen van de waarde van rekenkundige expressies.</item>
<item> Felix Hudson voor het schrijven van het <it>renna</it>
script</item>
<item>Kees van den Broek (voor het opsturen van de vele correcties,
het herschrijven van de sectie handige opdrachten)</item>
<item>Mike (pink) deed een aantal suggesties betreft het lokaliseren
van bash en het testen van bestanden</item>
<item>Fiesh deed een aardige suggestie over de sectie loops</item>
<item>Lion raadde aan melding te maken van een gebruikelijke
foutmelding (./hello.sh: Command not found).</item>
<item>Andreas Beck voor verscheidene correcties en opmerkingen</item>
</itemize></p>
</sect1>
<sect1><heading>Historie</heading>
<p> Nieuwe vertalingen opgenomen en kleine correcties aangebracht.</p>
<p> De sectie handige opdrachten herschreven door Kees.</p>
<p> Meer correcties en suggesties opgenomen.</p>
<p> Voorbeelden toegevoegd over het vergelijken van strings.</p>
<p> v0.8 de versie gedropt, ik denk dat de datum voldoende is.</p>
<p> v0.7 Meer correcties en een aantal oude TO-DO secties geschreven.</p>
<p> v0.6 Kleine correcties.</p>
<p> v0.5 De sectie over omleiding toegevoegd.</p>
<p> v0.4 verdween van zijn lokatie vanwegen mijn ex-baas en dit
document vond een nieuwe plaats op de juiste url: www.linuxdoc.org.</p>
<p> voorgaand: Ik weet het niet meer en maakte geen gebruik van rcs of
cvs :(</p>
</sect1>
<sect1>
<heading>Meer bronnen</heading>
<p> Introductie bash (onder BE)
<htmlurl url="http://org.laol.net/lamug/beforever/bashtut.htm"
name="http://org.laol.net/lamug/beforever/bashtut.htm"></p>
<p> Bourne Shell Programming
http://207.213.123.70/book/ </p>
</sect1>
</sect>
</article>
</linuxdoc>
|