40
|
1 local Luan = require "luan:Luan.luan"
|
|
2 local error = Luan.error
|
|
3 local pairs = Luan.pairs or error()
|
|
4 local Io = require "luan:Io.luan"
|
|
5 local Http = require "luan:http/Http.luan"
|
|
6 local Shared = require "site:/lib/Shared.luan"
|
|
7 local head = Shared.head or error()
|
|
8 local header = Shared.header or error()
|
|
9
|
|
10
|
|
11 local content = {
|
|
12 intro = {
|
45
|
13 title = [[Introduction]]
|
40
|
14 content = function()
|
|
15 %>
|
46
|
16 <p>I really don't want to write this tutorial, but all the existing <a href="bash.html">Bash</a> tutorials are so horrible that I have no choice. I looked at books, websites, and YouTube - all horrible. They don't start with the basics. They include all kinds of useless crap. And they don't explain core concepts. So I have no choice but to write this damn thing for my <a href="http://localhost:8080/learn.html#bash">Learn Reactionary Programming</a> Bash lesson.</p>
|
40
|
17
|
45
|
18 <p>I will focus on Mac and Windows. I don't have Linux, and I hate Linux, so I won't discuss it. Most of Bash is the same on Mac and Windows, but where they differ, I will discuss both.</p>
|
40
|
19 <%
|
|
20 end
|
|
21 }
|
45
|
22 access = {
|
|
23 title = [[Running Bash]]
|
40
|
24 content = function()
|
|
25 %>
|
45
|
26 <p>How you access Bash depends on your operating system. If you are on a Mac then you access Bash through the Mac Terminal which is found in "Applications > Utilities > Terminal.app". Be sure to <a href="https://www.howtogeek.com/444596/how-to-change-the-default-shell-to-bash-in-macos-catalina/">set the default shell to Bash</a>. If you are on Windows then install <a href="https://www.msys2.org/">MSYS2</a>. The default terminal isn't so good, so I suggest <a href="https://www.msys2.org/docs/terminals/#windows-terminal">using the Windows Terminal</a>.</p>
|
40
|
27 <%
|
|
28 end
|
|
29 }
|
45
|
30 start = {
|
|
31 title = [[Getting Started]]
|
41
|
32 content = function()
|
|
33 %>
|
45
|
34 <p>When I start Bash on my Mac I see:</p>
|
|
35
|
|
36 <code block>
|
|
37 Last login: Thu Jan 4 23:25:35 on ttys004
|
|
38
|
|
39 The default interactive shell is now zsh.
|
|
40 To update your account to use zsh, please run `chsh -s /bin/zsh`.
|
|
41 For more details, please visit https://support.apple.com/kb/HT208050.
|
|
42 ~ $
|
44
|
43
|
45
|
44 </code>
|
|
45
|
|
46 <p>On Windows - MSYS2 I just see:</p>
|
44
|
47
|
45
|
48 <code block>
|
|
49 ~ $
|
|
50
|
|
51 </code>
|
|
52
|
|
53 <p>The line with the <b><code>$</code></b> is the command prompt. The cursor is at the end of it, and if I type, my text will go there. You may have different text before the <b><code>$</code></b> which is okay, but the line should end with <b><code>$</code></b>. If it doesn't, something is wrong.</p>
|
|
54
|
|
55 <p>Now type "qqq". When I say type "whatever", you should type return/enter at the end. Only when you type return/enter will Bash process what you typed. Now you should see:</p>
|
44
|
56
|
45
|
57 <code block>
|
|
58 ~ $ qqq
|
|
59 -bash: qqq: command not found
|
|
60 ~ $
|
|
61 </code>
|
|
62
|
|
63 <p>Bash doesn't know what "qqq" means and says so. Now try the following... Note that you type what is after the <b><code>$</code></b> and Bash should respond as shown.</p>
|
|
64
|
|
65 <code block>
|
|
66 ~ $ echo hi
|
|
67 hi
|
|
68 ~ $ echo how are you
|
|
69 how are you
|
|
70 ~ $ echo bye
|
|
71 bye
|
|
72 </code>
|
|
73
|
|
74 <p>The <code>echo</code> command just echos what comes after. Now press the up-arrow on your keyboard. This should put the previous command where your cursor is. Up-arrow again brings the command before that. Try down-arrow and left-arrow and right-arrow. You can use this to navigate through your command history. The delete key also works for editing lines. And of course you can type. When you press return/enter then Bash will get your edited command and process it.</p>
|
|
75
|
|
76 <p>When you enter <code>echo how are you</code>, <code>echo</code> is the command. This command has 3 arguments: <code>how</code>, <code>are</code>, and <code>you</code>. Commands and arguments are separated with spaces. It doesn't matter how many spaces, so:</p>
|
|
77
|
|
78 <code block>
|
|
79 ~ $ echo how are you
|
|
80 how are you
|
|
81 </code>
|
|
82
|
|
83 <p><code>echo</code> just returns the arguments separated by one space.</p>
|
41
|
84 <%
|
|
85 end
|
|
86 }
|
45
|
87 man = {
|
|
88 title = [[The "man" Command]]
|
41
|
89 content = function()
|
|
90 %>
|
45
|
91 <p>Enter:</p>
|
|
92 <code block>
|
|
93 ~ $ man echo
|
|
94 </code>
|
|
95
|
|
96 <p>You should get:</p>
|
|
97
|
|
98 <code block>
|
|
99
|
|
100 ECHO(1) BSD General Commands Manual ECHO(1)
|
|
101
|
|
102 NAME
|
|
103 echo -- write arguments to the standard output
|
|
104
|
|
105 SYNOPSIS
|
|
106 echo [-n] [string ...]
|
|
107
|
|
108 DESCRIPTION
|
|
109 The echo utility writes any specified operands, separated by single blank
|
|
110 (` ') characters and followed by a newline (`\n') character, to the stan-
|
|
111 dard output.
|
|
112
|
|
113 The following option is available:
|
|
114
|
|
115 -n Do not print the trailing newline character. This may also be
|
|
116 achieved by appending `\c' to the end of the string, as is done by
|
|
117 iBCS2 compatible systems. Note that this option as well as the
|
|
118 effect of `\c' are implementation-defined in IEEE Std 1003.1-2001
|
|
119 (``POSIX.1'') as amended by Cor. 1-2002. Applications aiming for
|
|
120 maximum portability are strongly encouraged to use printf(1) to
|
|
121 suppress the newline character.
|
|
122 :
|
|
123 </code>
|
|
124
|
|
125 <p>But if you are on Windows, you may not have <code>man</code> installed. In that case, do:</p>
|
|
126
|
|
127 <code block>
|
|
128 ~ $ pacman -S man-db
|
|
129 </code>
|
|
130
|
|
131 <p>to install <code>man</code> as described <a href="https://packages.msys2.org/package/man-db">here</a> and then try <code>man echo</code> again.</p>
|
|
132
|
|
133 <p>The <code>man</code> command shows documentation of commands. Unfortunately it has a silly user interface based on memorizing keys, so I will just tell you the few keys you need. Down-arrow and up-arrow move down and up by one line. The space key moves down by one page. And most importantly, typing "q" quits and takes you back to Bash. You just have to memorize this.</p>
|
|
134
|
|
135 <p>Now try entering <code>man man</code>. You don't need all the stuff shown, but you can see what a complicated man page looks like. You can use <code>man</code> to get the documentation of other commands as I discuss them.</p>
|
41
|
136 <%
|
|
137 end
|
|
138 }
|
46
|
139 dirs = {
|
|
140 title = [[Directories]]
|
|
141 content = function()
|
|
142 %>
|
|
143 <p>You should be familiar with Mac Finder or Windows File Explorer, and you should know from this that directories (also called "folders") are organized into a tree.</p>
|
|
144
|
|
145 <p>On Mac:</p>
|
|
146
|
|
147 <code block>
|
|
148 ~ $ pwd
|
|
149 /Users/fschmidt
|
|
150 ~ $ open .
|
|
151 ~ $
|
|
152 </code>
|
|
153
|
|
154 <p>On Windows:</p>
|
|
155
|
|
156 <code block>
|
|
157 ~ $ pwd
|
|
158 /home/fschmidt
|
|
159 ~ $ explorer .
|
|
160 ~ $
|
|
161 </code>
|
|
162
|
|
163 <p>When using Bash, you are always in some directory, called the current directory or the working directory. <code>pwd</code> shows you the full path to this directory. Do <code>man pwd</code> for details.<p>
|
|
164
|
|
165 <p>Continuing on my Mac:</p>
|
|
166
|
|
167 <code block>
|
|
168 ~ $ mkdir learn
|
|
169 ~ $ cd learn
|
|
170 ~/learn $ pwd
|
|
171 /Users/fschmidt/learn
|
|
172 </code>
|
|
173
|
|
174 <p><code>mkdir</code> makes a directory in the current directory. You should be able to see the created directory in Mac Finder or Windows File Explorer. <code>cd</code> stands for "change directory". This changes the current directory. <code>cd</code> is a built-in command (built into Bash), and <code>man</code> isn't useful with built-in commands, so instead of <code>man cd</code>, try <code>help cd</code>. Continuing...</p>
|
|
175
|
|
176 <code block>
|
|
177 ~/learn $ pwd
|
|
178 /Users/fschmidt/learn
|
|
179 ~/learn $ ls
|
|
180 ~/learn $ touch file1
|
|
181 ~/learn $ ls
|
|
182 file1
|
|
183 ~/learn $ touch file2
|
|
184 ~/learn $ touch file3
|
|
185 ~/learn $ ls
|
|
186 file1 file2 file3
|
|
187 ~/learn $ mkdir dir1
|
|
188 ~/learn $ ls
|
|
189 dir1 file1 file2 file3
|
|
190 ~/learn $ ls -F
|
|
191 dir1/ file1 file2 file3
|
|
192 ~/learn $ ls -a
|
|
193 . .. dir1 file1 file2 file3
|
|
194 ~/learn $ ls -a -F
|
|
195 ./ ../ dir1/ file1 file2 file3
|
|
196 ~/learn $ ls -aF
|
|
197 ./ ../ dir1/ file1 file2 file3
|
|
198 </code>
|
|
199
|
|
200 <p><code>ls</code> lists files and <code>touch</code> creates an empty file. Arguments that start with "-" are options. Do <code>man ls</code> to see what the options I used do. <code>-F</code> appends a "/" to directories, and <code>-a</code> shows files starting with "." which are usually hidden. Options can be combined.</p>
|
|
201
|
|
202 <code block>
|
|
203 ~/learn $ ls file1
|
|
204 file1
|
|
205 ~/learn $ ls qqq
|
|
206 ls: qqq: No such file or directory
|
|
207 ~/learn $ ls file1 qqq file2
|
|
208 ls: qqq: No such file or directory
|
|
209 file1 file2
|
|
210 ~/learn $ ls dir1
|
|
211 ~/learn $ touch dir1/d1file
|
|
212 ~/learn $ ls dir1
|
|
213 d1file
|
|
214 ~/learn $ ls -d dir1
|
|
215 dir1
|
|
216 ~/learn $ ls file1 file2 dir1
|
|
217 file1 file2
|
|
218
|
|
219 dir1:
|
|
220 d1file
|
|
221 ~/learn $ ls -d file1 file2 dir1
|
|
222 dir1 file1 file2
|
|
223 ~/learn $ ls -dF file1 file2 dir1
|
|
224 dir1/ file1 file2
|
|
225 </code>
|
|
226
|
|
227 <p>Without file arguments, <code>ls</code> lists files in the current directory. With file arguments, it lists those files if they exist. If the file is a directory, it will list what is in the directory unless the <code>-d</code> option is used.</p>
|
|
228
|
|
229 <code block>
|
|
230 ~/learn $ ls
|
|
231 dir1 file1 file2 file3
|
|
232 ~/learn $ ls .
|
|
233 dir1 file1 file2 file3
|
|
234 ~/learn $ ls -d .
|
|
235 .
|
|
236 ~/learn $ ls -dF .
|
|
237 ./
|
|
238 ~/learn $ ls ./file1
|
|
239 ./file1
|
|
240 ~/learn $ ls dir1
|
|
241 d1file
|
|
242 ~/learn $ ls ./dir1
|
|
243 d1file
|
|
244 ~/learn $ pwd
|
|
245 /Users/fschmidt/learn
|
|
246 ~/learn $ cd .
|
|
247 ~/learn $ pwd
|
|
248 /Users/fschmidt/learn
|
|
249 </code>
|
|
250
|
|
251 <p><code>.</code> is the current directory.</p>
|
|
252
|
|
253 <code block>
|
|
254 ~/learn $ pwd
|
|
255 /Users/fschmidt/learn
|
|
256 ~/learn $ cd dir1
|
|
257 ~/learn/dir1 $ pwd
|
|
258 /Users/fschmidt/learn/dir1
|
|
259 ~/learn/dir1 $ ls .
|
|
260 d1file
|
|
261 ~/learn/dir1 $ ls ..
|
|
262 dir1 file1 file2 file3
|
|
263 ~/learn/dir1 $ cd ..
|
|
264 ~/learn $ pwd
|
|
265 /Users/fschmidt/learn
|
|
266 ~/learn $ cd dir1
|
|
267 ~/learn/dir1 $ pwd
|
|
268 /Users/fschmidt/learn/dir1
|
|
269 ~/learn/dir1 $ cd ../..
|
|
270 ~ $ pwd
|
|
271 /Users/fschmidt
|
|
272 ~ $ cd learn
|
|
273 ~/learn $ pwd
|
|
274 /Users/fschmidt/learn
|
|
275 </code>
|
|
276
|
|
277 <p><code>..</code> is the parent directory.</p>
|
|
278
|
|
279 <code block>
|
|
280 ~/learn $ echo *
|
|
281 dir1 file1 file2 file3
|
|
282 ~/learn $ echo d*
|
|
283 dir1
|
|
284 ~/learn $ echo f*
|
|
285 file1 file2 file3
|
|
286 ~/learn $ echo *1
|
|
287 dir1 file1
|
|
288 ~/learn $ echo dir1/*
|
|
289 dir1/d1file
|
|
290 ~/learn $ echo */*
|
|
291 dir1/d1file
|
|
292 ~/learn $ echo qqq*
|
|
293 qqq*
|
|
294 </code>
|
|
295
|
|
296 <p><code>*</code> does wildcard matching of files. It is important to understand that Bash does the wildcard matching and then passes the resulting arguments to the command. <code>echo</code> never sees the "*" unless there is no match.</p>
|
|
297
|
|
298 <code block>
|
|
299 ~/learn $ ls *
|
|
300 file1 file2 file3
|
|
301
|
|
302 dir1:
|
|
303 d1file
|
|
304 ~/learn $ ls -dF *
|
|
305 dir1/ file1 file2 file3
|
|
306 ~/learn $ ls -dF d*
|
|
307 dir1/
|
|
308 ~/learn $ ls -dF f*
|
|
309 file1 file2 file3
|
|
310 ~/learn $ ls -dF *1
|
|
311 dir1/ file1
|
|
312 ~/learn $ ls dir1/*
|
|
313 dir1/d1file
|
|
314 ~/learn $ ls */*
|
|
315 dir1/d1file
|
|
316 ~/learn $ ls -dF qqq*
|
|
317 ls: qqq*: No such file or directory
|
|
318 </code>
|
|
319
|
|
320 <p>Should be self-explanatory.</p>
|
|
321
|
|
322 <code block>
|
|
323 ~/learn $ pwd
|
|
324 /Users/fschmidt/learn
|
|
325 ~/learn $ cd ~
|
|
326 ~ $ pwd
|
|
327 /Users/fschmidt
|
|
328 ~ $ cd learn/dir1
|
|
329 ~/learn/dir1 $ pwd
|
|
330 /Users/fschmidt/learn/dir1
|
|
331 ~/learn/dir1 $ cd
|
|
332 ~ $ pwd
|
|
333 /Users/fschmidt
|
|
334 ~ $ cd ~/learn
|
|
335 ~/learn $ pwd
|
|
336 /Users/fschmidt/learn
|
|
337 ~/learn $ echo ~
|
|
338 /Users/fschmidt
|
|
339 ~/learn $ echo .
|
|
340 .
|
|
341 ~/learn $ echo ..
|
|
342 ..
|
|
343 </code>
|
|
344
|
|
345 <p><code>~</code> means your home directory. <code>cd</code> without arguments is the same as <code>cd ~</code>. <code>~</code> is expanded into your home directory by Bash.</p>
|
|
346
|
|
347 <code block>
|
|
348 ~/learn $ ls -ltF
|
|
349 total 0
|
|
350 drwxr-xr-x 3 fschmidt staff 96 Jan 5 02:33 dir1/
|
|
351 -rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file3
|
|
352 -rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file2
|
|
353 -rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file1
|
|
354 </code>
|
|
355
|
|
356 <p><code>-l</code> gives you this ugly techy format. You get the date that the file was last modified. Before the date is the file size. <code>-t</code> sorts by date descending.</p>
|
|
357
|
|
358 <p>Lastly I will describe autocompletion. I type <code>echo d</code> without enter/return but instead then press the tab key. It autocompletes to <code>echo dir1/</code>. I press tab again and it autocompletes to <code>echo dir1/d1file</code>. Pressing tab while entering a file or directory makes Bash try to autocomplete using matching file names. If I enter <code>echo f</code> and press tab, I get <code>echo file</code>. It doesn't know which to choose next. Another tab just beeps. And another tab shows me the options like this:</p>
|
|
359
|
|
360 <code block>
|
|
361 ~/learn $ echo file
|
|
362 file1 file2 file3
|
|
363 ~/learn $ echo file
|
|
364 </code>
|
|
365
|
|
366 <p>In general, you can press tab anytime while entering a file name and see what happens. Autocompletion saves a lot of typing.</p>
|
|
367 <%
|
|
368 end
|
|
369 }
|
|
370 files = {
|
|
371 title = [[Working with Files]]
|
|
372 content = function()
|
|
373 %>
|
|
374 <p></p>
|
|
375 <code block>
|
|
376 ~/learn $ ls -F
|
|
377 dir1/ file1 file2 file3
|
|
378 ~/learn $ cp file1 copied
|
|
379 ~/learn $ ls -F
|
|
380 copied dir1/ file1 file2 file3
|
|
381 ~/learn $ mv copied moved
|
|
382 ~/learn $ ls -F
|
|
383 dir1/ file1 file2 file3 moved
|
|
384 ~/learn $ rm moved
|
|
385 ~/learn $ ls -F
|
|
386 dir1/ file1 file2 file3
|
|
387 </code>
|
|
388
|
|
389 <p><code>cp</code> copies files or directories. <code>mv</code> moves files or directories. <code>rm</code> removes files or directories. See the <code>man</code> pages of these commands for details.</p>
|
|
390
|
|
391 <code block>
|
|
392 ~/learn $ ls -F
|
|
393 dir1/ file1 file2 file3
|
|
394 ~/learn $ mkdir dir2
|
|
395 ~/learn $ touch dir2/d2file
|
|
396 ~/learn $ ls -F
|
|
397 dir1/ dir2/ file1 file2 file3
|
|
398 ~/learn $ ls dir2
|
|
399 d2file
|
|
400 ~/learn $ rm dir2
|
|
401 rm: dir2: is a directory
|
|
402 ~/learn $ rm -d dir2
|
|
403 rm: dir2: Directory not empty
|
|
404 ~/learn $ rm dir2/d2file
|
|
405 ~/learn $ rm -d dir2
|
|
406 ~/learn $ ls -F
|
|
407 dir1/ file1 file2 file3
|
|
408 </code>
|
|
409
|
|
410 <p></p>
|
|
411
|
|
412 <code block>
|
|
413 ~/learn $ ls -F
|
|
414 dir1/ file1 file2 file3
|
|
415 ~/learn $ mkdir dir2
|
|
416 ~/learn $ touch dir2/d2file
|
|
417 ~/learn $ ls -F
|
|
418 dir1/ dir2/ file1 file2 file3
|
|
419 ~/learn $ rm -R dir2
|
|
420 ~/learn $ ls -F
|
|
421 dir1/ file1 file2 file3
|
|
422 </code>
|
|
423
|
|
424 <p></p>
|
|
425
|
|
426 <code block>
|
|
427 ~/learn $ ls -F
|
|
428 dir1/ file1 file2 file3
|
|
429 ~/learn $ cp dir1 dir2
|
|
430 cp: dir1 is a directory (not copied).
|
|
431 ~/learn $ cp -R dir1 dir2
|
|
432 ~/learn $ ls -F
|
|
433 dir1/ dir2/ file1 file2 file3
|
|
434 ~/learn $ ls dir2
|
|
435 d1file
|
|
436 ~/learn $ cp f* dir2
|
|
437 ~/learn $ ls dir2
|
|
438 d1file file1 file2 file3
|
|
439 ~/learn $ rm -R dir2
|
|
440 ~/learn $ ls -F
|
|
441 dir1/ file1 file2 file3
|
|
442 </code>
|
|
443
|
|
444 <p></p>
|
|
445
|
|
446 <code block>
|
|
447 ~/learn $ ls -F
|
|
448 dir1/ file1 file2 file3
|
|
449 ~/learn $ mkdir dir2
|
|
450 ~/learn $ cp -R dir1 dir2
|
|
451 ~/learn $ ls -F dir2
|
|
452 dir1/
|
|
453 ~/learn $ ls -F dir2/dir1
|
|
454 d1file
|
|
455 ~/learn $ rm -R dir2
|
|
456 ~/learn $ ls -F
|
|
457 dir1/ file1 file2 file3
|
|
458 </code>
|
|
459
|
|
460 <p>I could explain all this, but I won't. You should learn to understand commands and their options using <code>man</code> and by playing with them. Don't continue until you completely understand the above.</p>
|
|
461 <%
|
|
462 end
|
|
463 }
|
|
464 quote = {
|
|
465 title = [[Quoting]]
|
|
466 content = function()
|
|
467 %>
|
|
468 <p></p>
|
|
469
|
|
470 <code block>
|
|
471 ~/learn $ echo a b
|
|
472 a b
|
|
473 ~/learn $ echo "a b"
|
|
474 a b
|
|
475 ~/learn $ echo 'a b'
|
|
476 a b
|
|
477 ~/learn $ echo "a b" c
|
|
478 a b c
|
|
479 </code>
|
|
480
|
|
481 <p>Bash treats text in quotes as one argument. So in <code>echo a b</code>, <code>echo</code> has two arguments: "a" and "b". In <code>echo "a b"</code>, <code>echo</code> has one argument: "<span pre>a b</span>". In <code>echo 'a b'</code>, <code>echo</code> has one argument: "<span pre>a b</span>". In <code>echo "a b" c</code>, <code>echo</code> has two arguments: "<span pre>a b</span>" and "c".</p>
|
|
482
|
|
483 <code block>
|
|
484 ~/learn $ echo a\ \ \ b
|
|
485 a b
|
|
486 </code>
|
|
487
|
|
488 <p>Outside of quotes, <code>\ </code> is not treated as a separator, but rather is treated as a space character that is part of the argument.</p>
|
|
489
|
|
490 <%
|
|
491 end
|
|
492 }
|
|
493 vars = {
|
|
494 title = [[Variables]]
|
|
495 content = function()
|
|
496 %>
|
|
497 <p></p>
|
|
498
|
|
499 <code block>
|
|
500 ~/learn $ echo $X
|
|
501
|
|
502 ~/learn $ X="some text"
|
|
503 ~/learn $ echo $X
|
|
504 some text
|
|
505 ~/learn $ echo "X is: $X"
|
|
506 X is: some text
|
|
507 ~/learn $ echo 'X is: $X'
|
|
508 X is: $X
|
|
509 ~/learn $ X="$X and more"
|
|
510 ~/learn $ echo $X
|
|
511 some text and more
|
|
512 </code>
|
|
513
|
|
514 <p>Here <code>X</code> is a variable. You get its value with <code>$X</code>. This also works inside double-quotes but not inside single-quotes.</p>
|
|
515 <%
|
|
516 end
|
|
517 }
|
|
518 bash_profile = {
|
|
519 title = [[.bash_profile]]
|
|
520 content = function()
|
|
521 %>
|
|
522 <p>later</p>
|
|
523 <%
|
|
524 end
|
|
525 }
|
45
|
526 later = {
|
|
527 title = [[placeholder]]
|
41
|
528 content = function()
|
|
529 %>
|
|
530 <p>later</p>
|
|
531 <%
|
|
532 end
|
|
533 }
|
40
|
534 }
|
|
535
|
|
536
|
|
537 local function show_toc(content)
|
|
538 %>
|
|
539 <ul>
|
|
540 <%
|
|
541 for id, info in pairs(content) do
|
|
542 %>
|
|
543 <li><a id="c_<%=id%>" href="#<%=id%>"><%=info.title%></a></li>
|
|
544 <%
|
|
545 end
|
|
546 %>
|
|
547 </ul>
|
|
548 <%
|
|
549 end
|
|
550
|
|
551 local function show_content(content,h)
|
|
552 for id, info in pairs(content) do
|
|
553 %>
|
|
554 <div heading>
|
|
555 <h<%=h%>><a id="<%=id%>" href="#<%=id%>"><%=info.title%></a></h<%=h%>>
|
|
556 <a href="#c_<%=id%>">contents</a>
|
|
557 </div>
|
|
558 <%
|
|
559 info.content()
|
|
560 end
|
|
561 end
|
|
562
|
|
563 return function()
|
|
564 Io.stdout = Http.response.text_writer()
|
|
565 %>
|
|
566 <!doctype html>
|
|
567 <html>
|
|
568 <head>
|
|
569 <% head() %>
|
45
|
570 <title>Reactionary Bash Tutorial</title>
|
40
|
571 </head>
|
|
572 <body>
|
|
573 <% header() %>
|
|
574 <div content>
|
45
|
575 <h1><a href="learn.html">Reactionary Bash Tutorial</a></h1>
|
40
|
576 <hr>
|
|
577 <h2>Contents</h2>
|
|
578 <div toc>
|
|
579 <% show_toc(content) %>
|
|
580 </div>
|
|
581 <hr>
|
|
582 <% show_content(content,2) %>
|
|
583 </div>
|
|
584 </body>
|
|
585 </html>
|
|
586 <%
|
|
587 end
|