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"
|
95
|
5 local Site_translator = require "luan:ai/Site_translator.luan"
|
91
|
6 local get_lang = Site_translator.get_lang or error()
|
|
7 local text_writer = Site_translator.text_writer or error()
|
40
|
8 local Shared = require "site:/lib/Shared.luan"
|
|
9 local head = Shared.head or error()
|
|
10 local header = Shared.header or error()
|
|
11
|
|
12
|
|
13 local content = {
|
|
14 intro = {
|
45
|
15 title = [[Introduction]]
|
40
|
16 content = function()
|
|
17 %>
|
101
|
18 <p>I really don't want to write this tutorial, but all the existing Bash 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 junk. And they don't explain core concepts. So I have no choice but to write this for my <a href="learn.html#bash">Learn Reactionary Programming</a> Bash lesson.</p>
|
40
|
19
|
49
|
20 <p><a href="bash.html">Bash</a> is a <a href="https://en.wikipedia.org/wiki/Unix_shell">shell</a>, one of many, but the one I prefer. 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
|
21 <%
|
|
22 end
|
|
23 }
|
45
|
24 access = {
|
|
25 title = [[Running Bash]]
|
40
|
26 content = function()
|
|
27 %>
|
45
|
28 <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
|
29 <%
|
|
30 end
|
|
31 }
|
45
|
32 start = {
|
|
33 title = [[Getting Started]]
|
41
|
34 content = function()
|
|
35 %>
|
45
|
36 <p>When I start Bash on my Mac I see:</p>
|
|
37
|
|
38 <code block>
|
|
39 Last login: Thu Jan 4 23:25:35 on ttys004
|
|
40
|
|
41 The default interactive shell is now zsh.
|
|
42 To update your account to use zsh, please run `chsh -s /bin/zsh`.
|
|
43 For more details, please visit https://support.apple.com/kb/HT208050.
|
|
44 ~ $
|
44
|
45
|
45
|
46 </code>
|
|
47
|
|
48 <p>On Windows - MSYS2 I just see:</p>
|
44
|
49
|
45
|
50 <code block>
|
|
51 ~ $
|
|
52
|
|
53 </code>
|
|
54
|
|
55 <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>
|
|
56
|
|
57 <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
|
58
|
45
|
59 <code block>
|
|
60 ~ $ qqq
|
|
61 -bash: qqq: command not found
|
|
62 ~ $
|
|
63 </code>
|
|
64
|
|
65 <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>
|
|
66
|
|
67 <code block>
|
|
68 ~ $ echo hi
|
|
69 hi
|
|
70 ~ $ echo how are you
|
|
71 how are you
|
|
72 ~ $ echo bye
|
|
73 bye
|
|
74 </code>
|
|
75
|
49
|
76 <p>The <code>echo</code> command just echoes 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>
|
45
|
77
|
|
78 <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>
|
|
79
|
|
80 <code block>
|
|
81 ~ $ echo how are you
|
|
82 how are you
|
|
83 </code>
|
|
84
|
|
85 <p><code>echo</code> just returns the arguments separated by one space.</p>
|
49
|
86
|
|
87 <code block>
|
|
88 ~ $ echo one; echo two
|
|
89 one
|
|
90 two
|
|
91 </code>
|
|
92
|
|
93 <p>You can put multiple commands on one line separated by a <code>;</code>.</p>
|
41
|
94 <%
|
|
95 end
|
|
96 }
|
45
|
97 man = {
|
|
98 title = [[The "man" Command]]
|
41
|
99 content = function()
|
|
100 %>
|
45
|
101 <p>Enter:</p>
|
|
102 <code block>
|
|
103 ~ $ man echo
|
|
104 </code>
|
|
105
|
49
|
106 <p>You should get something like:</p>
|
45
|
107
|
|
108 <code block>
|
|
109
|
|
110 ECHO(1) BSD General Commands Manual ECHO(1)
|
|
111
|
|
112 NAME
|
|
113 echo -- write arguments to the standard output
|
|
114
|
|
115 SYNOPSIS
|
|
116 echo [-n] [string ...]
|
|
117
|
|
118 DESCRIPTION
|
|
119 The echo utility writes any specified operands, separated by single blank
|
|
120 (` ') characters and followed by a newline (`\n') character, to the stan-
|
|
121 dard output.
|
|
122
|
|
123 The following option is available:
|
|
124
|
|
125 -n Do not print the trailing newline character. This may also be
|
|
126 achieved by appending `\c' to the end of the string, as is done by
|
|
127 iBCS2 compatible systems. Note that this option as well as the
|
|
128 effect of `\c' are implementation-defined in IEEE Std 1003.1-2001
|
|
129 (``POSIX.1'') as amended by Cor. 1-2002. Applications aiming for
|
|
130 maximum portability are strongly encouraged to use printf(1) to
|
|
131 suppress the newline character.
|
|
132 :
|
|
133 </code>
|
|
134
|
|
135 <p>But if you are on Windows, you may not have <code>man</code> installed. In that case, do:</p>
|
|
136
|
|
137 <code block>
|
|
138 ~ $ pacman -S man-db
|
|
139 </code>
|
|
140
|
|
141 <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>
|
|
142
|
|
143 <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>
|
|
144
|
|
145 <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
|
146 <%
|
|
147 end
|
|
148 }
|
46
|
149 dirs = {
|
|
150 title = [[Directories]]
|
|
151 content = function()
|
|
152 %>
|
|
153 <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>
|
|
154
|
|
155 <p>On Mac:</p>
|
|
156
|
|
157 <code block>
|
|
158 ~ $ pwd
|
|
159 /Users/fschmidt
|
|
160 ~ $ open .
|
|
161 ~ $
|
|
162 </code>
|
|
163
|
|
164 <p>On Windows:</p>
|
|
165
|
|
166 <code block>
|
|
167 ~ $ pwd
|
|
168 /home/fschmidt
|
|
169 ~ $ explorer .
|
|
170 ~ $
|
|
171 </code>
|
|
172
|
49
|
173 <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. <code>open .</code> should open the Mac Finder for the current directory, and <code>explorer .</code> should open the Windows File Explorer for the current directory.<p>
|
46
|
174
|
|
175 <p>Continuing on my Mac:</p>
|
|
176
|
|
177 <code block>
|
|
178 ~ $ mkdir learn
|
|
179 ~ $ cd learn
|
|
180 ~/learn $ pwd
|
|
181 /Users/fschmidt/learn
|
|
182 </code>
|
|
183
|
|
184 <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>
|
|
185
|
|
186 <code block>
|
|
187 ~/learn $ pwd
|
|
188 /Users/fschmidt/learn
|
|
189 ~/learn $ ls
|
|
190 ~/learn $ touch file1
|
|
191 ~/learn $ ls
|
|
192 file1
|
|
193 ~/learn $ touch file2
|
|
194 ~/learn $ touch file3
|
|
195 ~/learn $ ls
|
|
196 file1 file2 file3
|
|
197 ~/learn $ mkdir dir1
|
|
198 ~/learn $ ls
|
|
199 dir1 file1 file2 file3
|
|
200 ~/learn $ ls -F
|
|
201 dir1/ file1 file2 file3
|
|
202 ~/learn $ ls -a
|
|
203 . .. dir1 file1 file2 file3
|
|
204 ~/learn $ ls -a -F
|
|
205 ./ ../ dir1/ file1 file2 file3
|
|
206 ~/learn $ ls -aF
|
|
207 ./ ../ dir1/ file1 file2 file3
|
|
208 </code>
|
|
209
|
|
210 <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>
|
|
211
|
|
212 <code block>
|
|
213 ~/learn $ ls file1
|
|
214 file1
|
|
215 ~/learn $ ls qqq
|
|
216 ls: qqq: No such file or directory
|
|
217 ~/learn $ ls file1 qqq file2
|
|
218 ls: qqq: No such file or directory
|
|
219 file1 file2
|
|
220 ~/learn $ ls dir1
|
|
221 ~/learn $ touch dir1/d1file
|
|
222 ~/learn $ ls dir1
|
|
223 d1file
|
|
224 ~/learn $ ls -d dir1
|
|
225 dir1
|
|
226 ~/learn $ ls file1 file2 dir1
|
|
227 file1 file2
|
|
228
|
|
229 dir1:
|
|
230 d1file
|
|
231 ~/learn $ ls -d file1 file2 dir1
|
|
232 dir1 file1 file2
|
|
233 ~/learn $ ls -dF file1 file2 dir1
|
|
234 dir1/ file1 file2
|
|
235 </code>
|
|
236
|
|
237 <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>
|
|
238
|
|
239 <code block>
|
|
240 ~/learn $ ls
|
|
241 dir1 file1 file2 file3
|
|
242 ~/learn $ ls .
|
|
243 dir1 file1 file2 file3
|
|
244 ~/learn $ ls -d .
|
|
245 .
|
|
246 ~/learn $ ls -dF .
|
|
247 ./
|
|
248 ~/learn $ ls ./file1
|
|
249 ./file1
|
|
250 ~/learn $ ls dir1
|
|
251 d1file
|
|
252 ~/learn $ ls ./dir1
|
|
253 d1file
|
|
254 ~/learn $ pwd
|
|
255 /Users/fschmidt/learn
|
|
256 ~/learn $ cd .
|
|
257 ~/learn $ pwd
|
|
258 /Users/fschmidt/learn
|
|
259 </code>
|
|
260
|
|
261 <p><code>.</code> is the current directory.</p>
|
|
262
|
|
263 <code block>
|
|
264 ~/learn $ pwd
|
|
265 /Users/fschmidt/learn
|
|
266 ~/learn $ cd dir1
|
|
267 ~/learn/dir1 $ pwd
|
|
268 /Users/fschmidt/learn/dir1
|
|
269 ~/learn/dir1 $ ls .
|
|
270 d1file
|
|
271 ~/learn/dir1 $ ls ..
|
|
272 dir1 file1 file2 file3
|
|
273 ~/learn/dir1 $ cd ..
|
|
274 ~/learn $ pwd
|
|
275 /Users/fschmidt/learn
|
|
276 ~/learn $ cd dir1
|
|
277 ~/learn/dir1 $ pwd
|
|
278 /Users/fschmidt/learn/dir1
|
|
279 ~/learn/dir1 $ cd ../..
|
|
280 ~ $ pwd
|
|
281 /Users/fschmidt
|
|
282 ~ $ cd learn
|
|
283 ~/learn $ pwd
|
|
284 /Users/fschmidt/learn
|
|
285 </code>
|
|
286
|
|
287 <p><code>..</code> is the parent directory.</p>
|
|
288
|
|
289 <code block>
|
|
290 ~/learn $ echo *
|
|
291 dir1 file1 file2 file3
|
|
292 ~/learn $ echo d*
|
|
293 dir1
|
|
294 ~/learn $ echo f*
|
|
295 file1 file2 file3
|
|
296 ~/learn $ echo *1
|
|
297 dir1 file1
|
|
298 ~/learn $ echo dir1/*
|
|
299 dir1/d1file
|
|
300 ~/learn $ echo */*
|
|
301 dir1/d1file
|
|
302 ~/learn $ echo qqq*
|
|
303 qqq*
|
|
304 </code>
|
|
305
|
|
306 <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>
|
|
307
|
|
308 <code block>
|
|
309 ~/learn $ ls *
|
|
310 file1 file2 file3
|
|
311
|
|
312 dir1:
|
|
313 d1file
|
|
314 ~/learn $ ls -dF *
|
|
315 dir1/ file1 file2 file3
|
|
316 ~/learn $ ls -dF d*
|
|
317 dir1/
|
|
318 ~/learn $ ls -dF f*
|
|
319 file1 file2 file3
|
|
320 ~/learn $ ls -dF *1
|
|
321 dir1/ file1
|
|
322 ~/learn $ ls dir1/*
|
|
323 dir1/d1file
|
|
324 ~/learn $ ls */*
|
|
325 dir1/d1file
|
|
326 ~/learn $ ls -dF qqq*
|
|
327 ls: qqq*: No such file or directory
|
|
328 </code>
|
|
329
|
|
330 <p>Should be self-explanatory.</p>
|
|
331
|
|
332 <code block>
|
|
333 ~/learn $ pwd
|
|
334 /Users/fschmidt/learn
|
|
335 ~/learn $ cd ~
|
|
336 ~ $ pwd
|
|
337 /Users/fschmidt
|
|
338 ~ $ cd learn/dir1
|
|
339 ~/learn/dir1 $ pwd
|
|
340 /Users/fschmidt/learn/dir1
|
|
341 ~/learn/dir1 $ cd
|
|
342 ~ $ pwd
|
|
343 /Users/fschmidt
|
49
|
344 ~ $ cd ~/learn
|
46
|
345 ~/learn $ pwd
|
|
346 /Users/fschmidt/learn
|
|
347 ~/learn $ echo ~
|
|
348 /Users/fschmidt
|
|
349 ~/learn $ echo .
|
|
350 .
|
|
351 ~/learn $ echo ..
|
|
352 ..
|
|
353 </code>
|
|
354
|
|
355 <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>
|
|
356
|
|
357 <code block>
|
|
358 ~/learn $ ls -ltF
|
|
359 total 0
|
|
360 drwxr-xr-x 3 fschmidt staff 96 Jan 5 02:33 dir1/
|
|
361 -rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file3
|
|
362 -rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file2
|
|
363 -rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file1
|
|
364 </code>
|
|
365
|
|
366 <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>
|
|
367
|
|
368 <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>
|
|
369
|
|
370 <code block>
|
|
371 ~/learn $ echo file
|
|
372 file1 file2 file3
|
|
373 ~/learn $ echo file
|
|
374 </code>
|
|
375
|
|
376 <p>In general, you can press tab anytime while entering a file name and see what happens. Autocompletion saves a lot of typing.</p>
|
|
377 <%
|
|
378 end
|
|
379 }
|
|
380 files = {
|
|
381 title = [[Working with Files]]
|
|
382 content = function()
|
|
383 %>
|
|
384 <code block>
|
|
385 ~/learn $ ls -F
|
|
386 dir1/ file1 file2 file3
|
|
387 ~/learn $ cp file1 copied
|
|
388 ~/learn $ ls -F
|
|
389 copied dir1/ file1 file2 file3
|
|
390 ~/learn $ mv copied moved
|
|
391 ~/learn $ ls -F
|
|
392 dir1/ file1 file2 file3 moved
|
|
393 ~/learn $ rm moved
|
|
394 ~/learn $ ls -F
|
|
395 dir1/ file1 file2 file3
|
|
396 </code>
|
|
397
|
|
398 <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>
|
|
399
|
|
400 <code block>
|
|
401 ~/learn $ ls -F
|
|
402 dir1/ file1 file2 file3
|
|
403 ~/learn $ mkdir dir2
|
|
404 ~/learn $ touch dir2/d2file
|
|
405 ~/learn $ ls -F
|
|
406 dir1/ dir2/ file1 file2 file3
|
|
407 ~/learn $ ls dir2
|
|
408 d2file
|
|
409 ~/learn $ rm dir2
|
|
410 rm: dir2: is a directory
|
|
411 ~/learn $ rm -d dir2
|
|
412 rm: dir2: Directory not empty
|
|
413 ~/learn $ rm dir2/d2file
|
|
414 ~/learn $ rm -d dir2
|
|
415 ~/learn $ ls -F
|
|
416 dir1/ file1 file2 file3
|
|
417 </code>
|
|
418
|
|
419 <code block>
|
|
420 ~/learn $ ls -F
|
|
421 dir1/ file1 file2 file3
|
|
422 ~/learn $ mkdir dir2
|
|
423 ~/learn $ touch dir2/d2file
|
|
424 ~/learn $ ls -F
|
|
425 dir1/ dir2/ file1 file2 file3
|
47
|
426 ~/learn $ rm -r dir2
|
46
|
427 ~/learn $ ls -F
|
|
428 dir1/ file1 file2 file3
|
|
429 </code>
|
|
430
|
|
431 <code block>
|
|
432 ~/learn $ ls -F
|
|
433 dir1/ file1 file2 file3
|
|
434 ~/learn $ cp dir1 dir2
|
|
435 cp: dir1 is a directory (not copied).
|
47
|
436 ~/learn $ cp -r dir1 dir2
|
46
|
437 ~/learn $ ls -F
|
|
438 dir1/ dir2/ file1 file2 file3
|
|
439 ~/learn $ ls dir2
|
|
440 d1file
|
|
441 ~/learn $ cp f* dir2
|
|
442 ~/learn $ ls dir2
|
|
443 d1file file1 file2 file3
|
47
|
444 ~/learn $ rm -r dir2
|
46
|
445 ~/learn $ ls -F
|
|
446 dir1/ file1 file2 file3
|
|
447 </code>
|
|
448
|
|
449 <code block>
|
|
450 ~/learn $ ls -F
|
|
451 dir1/ file1 file2 file3
|
|
452 ~/learn $ mkdir dir2
|
47
|
453 ~/learn $ cp -r dir1 dir2
|
46
|
454 ~/learn $ ls -F dir2
|
|
455 dir1/
|
|
456 ~/learn $ ls -F dir2/dir1
|
|
457 d1file
|
47
|
458 ~/learn $ rm -r dir2
|
46
|
459 ~/learn $ ls -F
|
|
460 dir1/ file1 file2 file3
|
|
461 </code>
|
|
462
|
|
463 <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>
|
|
464 <%
|
|
465 end
|
|
466 }
|
|
467 quote = {
|
|
468 title = [[Quoting]]
|
|
469 content = function()
|
|
470 %>
|
|
471
|
|
472 <code block>
|
|
473 ~/learn $ echo a b
|
|
474 a b
|
|
475 ~/learn $ echo "a b"
|
|
476 a b
|
|
477 ~/learn $ echo 'a b'
|
|
478 a b
|
|
479 ~/learn $ echo "a b" c
|
|
480 a b c
|
|
481 </code>
|
|
482
|
|
483 <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>
|
|
484
|
|
485 <code block>
|
|
486 ~/learn $ echo a\ \ \ b
|
|
487 a b
|
|
488 </code>
|
|
489
|
|
490 <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>
|
|
491
|
111
|
492 <code block>
|
|
493 ~/learn $ echo file*
|
|
494 file1 file2 file3
|
|
495 ~/learn $ echo "file*"
|
|
496 file*
|
|
497 ~/learn $ echo 'file*'
|
|
498 file*
|
|
499 </code>
|
|
500
|
|
501 <p>Quotes prevent wildcard expansion.</p>
|
|
502
|
46
|
503 <%
|
|
504 end
|
|
505 }
|
|
506 vars = {
|
|
507 title = [[Variables]]
|
|
508 content = function()
|
|
509 %>
|
|
510
|
|
511 <code block>
|
|
512 ~/learn $ echo $X
|
|
513
|
|
514 ~/learn $ X="some text"
|
|
515 ~/learn $ echo $X
|
|
516 some text
|
|
517 ~/learn $ echo "X is: $X"
|
|
518 X is: some text
|
|
519 ~/learn $ echo 'X is: $X'
|
|
520 X is: $X
|
|
521 ~/learn $ X="$X and more"
|
|
522 ~/learn $ echo $X
|
|
523 some text and more
|
|
524 </code>
|
|
525
|
|
526 <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>
|
47
|
527
|
|
528 <p>There are special variables called environment variables that are used by Bash.</p>
|
|
529
|
|
530 <code block>
|
|
531 ~/learn $ echo $PATH
|
|
532 /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/fschmidt/Dropbox/bin:/Users/fschmidt/hg/luan/scripts:/usr/local/opt/postgresql@9.5/bin
|
|
533 ~/learn $ which ls
|
|
534 /bin/ls
|
|
535 ~/learn $ cd /bin
|
|
536 /bin $ pwd
|
|
537 /bin
|
|
538 /bin $ ls
|
|
539 [ dd launchctl pwd test
|
|
540 bash df link rm unlink
|
|
541 cat echo ln rmdir wait4path
|
|
542 chmod ed ls sh zsh
|
|
543 cp expr mkdir sleep
|
|
544 csh hostname mv stty
|
|
545 dash kill pax sync
|
|
546 date ksh ps tcsh
|
|
547 /bin $ ls -F
|
|
548 [* dd* launchctl* pwd* test*
|
|
549 bash* df* link* rm* unlink*
|
|
550 cat* echo* ln* rmdir* wait4path*
|
|
551 chmod* ed* ls* sh* zsh*
|
|
552 cp* expr* mkdir* sleep*
|
|
553 csh* hostname* mv* stty*
|
|
554 dash* kill* pax* sync*
|
|
555 date* ksh* ps* tcsh*
|
|
556 /bin $ cd ~/learn
|
|
557 ~/learn $
|
|
558 </code>
|
|
559
|
53
|
560 <p><code>PATH</code> is an environment variable containing a list of directories separated by <code>:</code> that are searched for commands by Bash. The <code>which</code> command shows the full path to a command. <code>ls -F</code> appends a <code>*</code> to executable files.</p>
|
47
|
561
|
|
562 <code block>
|
|
563 ~/learn $ subl file1
|
|
564 -bash: subl: command not found
|
|
565 ~/learn $ "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" file1
|
|
566 ~/learn $ PATH="$PATH:/Applications/Sublime Text.app/Contents/SharedSupport/bin"
|
|
567 ~/learn $ echo $PATH
|
|
568 /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/fschmidt/Dropbox/bin:/Users/fschmidt/hg/luan/scripts:/usr/local/opt/postgresql@9.5/bin:/Applications/Sublime Text.app/Contents/SharedSupport/bin
|
|
569 ~/learn $ subl file1
|
|
570 ~/learn $
|
|
571 </code>
|
|
572
|
91
|
573 <p>Here I edit the file <code>file1</code> with <a href="learn.html#editor">Sublime Text</a>, first by using the full path, and then by adding the directory to <code>PATH</code> so that Bash can find <code>subl</code>.</p>
|
47
|
574
|
|
575 <p>I have Microsoft Word on Windows. From the Windows Command Prompt (not Bash):</p>
|
|
576
|
|
577 <code block>
|
|
578 C:\Users\fschmidt>winword
|
|
579
|
|
580 C:\Users\fschmidt>where winword
|
|
581 C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE
|
|
582 </code>
|
|
583
|
|
584 <p><code>winword</code> runs Microsoft Word. The Command Prompt <code>where</code> command is like the Bash <code>which</code> command. So now on MSYS2:</p>
|
|
585
|
|
586 <code block>
|
|
587 ~ $ winword
|
|
588 bash: winword: command not found
|
|
589 ~ $ echo $PATH
|
|
590 /usr/local/bin:/usr/bin:/bin:/opt/bin:/c/Windows/System32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0/:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/c/Program Files/TortoiseHg:/c/Program Files/Java/jdk1.8.0_202/bin
|
|
591 ~ $ PATH="$PATH:/c/Program Files/Microsoft Office/root/Office16"
|
|
592 ~ $ echo $PATH
|
|
593 /usr/local/bin:/usr/bin:/bin:/opt/bin:/c/Windows/System32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0/:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/c/Program Files/TortoiseHg:/c/Program Files/Java/jdk1.8.0_202/bin:/c/Program Files/Microsoft Office/root/Office16
|
|
594 ~ $ winword
|
|
595 ~ $
|
|
596 </code>
|
|
597
|
|
598 <p>Returning to the Mac, there is another way to run applications found in Finder's "Applications" simply as applications instead of as commands.</p>
|
|
599
|
|
600 <code block>
|
|
601 ~/learn $ open -a 'Sublime Text' file1
|
|
602 </code>
|
|
603
|
|
604 <p>Another useful environment variable is <code>PS1</code> which controls the command prompt. I already have this set up, but if I didn't:</p>
|
|
605
|
|
606 <code block>
|
|
607 Franklins-MacBook-Pro:learn fschmidt$ echo $PS1
|
|
608 \h:\W \u\$
|
|
609 Franklins-MacBook-Pro:learn fschmidt$ PS1="\w $ "
|
|
610 ~/learn $ echo $PS1
|
|
611 \w $
|
|
612 ~/learn $
|
|
613 </code>
|
|
614
|
|
615 <p>Google "bash PS1" for more info.</p>
|
|
616
|
46
|
617 <%
|
|
618 end
|
|
619 }
|
|
620 bash_profile = {
|
|
621 title = [[.bash_profile]]
|
|
622 content = function()
|
|
623 %>
|
47
|
624 <code block>
|
|
625 ~/learn $ cd
|
|
626 ~ $ ls .bash_profile
|
|
627 .bash_profile
|
|
628 </code>
|
|
629
|
|
630 <p>If <code>.bash_profile</code> isn't found then do <code>touch .bash_profile</code> to create it. This file contains Bash commands that are run when Bash starts. If you already have this file, it is likely to contain comments that start with <code>#</code>. Comments are ignored like this:</p>
|
|
631
|
|
632 <code block>
|
|
633 ~ $ # comment line, does nothing
|
|
634 ~ $ echo whatever # end of line comment
|
|
635 whatever
|
|
636 ~ $
|
|
637 </code>
|
|
638
|
|
639 <p>To edit <code>.bash_profile</code> on a Mac, you can do:</p>
|
|
640
|
|
641 <code block>
|
|
642 ~ $ open -a 'Sublime Text' .bash_profile
|
|
643 </code>
|
|
644
|
|
645 <p>To edit <code>.bash_profile</code> on Windows, you can do:</p>
|
|
646
|
|
647 <code block>
|
|
648 ~ $ notepad .bash_profile
|
|
649 </code>
|
|
650
|
|
651 <p>Now try adding this line to <code>.bash_profile</code>:</p>
|
|
652
|
|
653 <code block>
|
|
654 echo hello there
|
|
655 </code>
|
|
656
|
|
657 <p>Now when you open a new Bash terminal, you should see "hello there". <code>.bash_profile</code> runs when Bash is started by opening a new Bash terminal.</p>
|
|
658
|
|
659 <p>I set <code>PS1</code> and <code>PATH</code> in <code>.bash_profile</code> to have the command prompt I want, and access to the commands that I want. I suggest that you make the <a href="https://www.sublimetext.com/docs/command_line.html">Sublime Text command</a> <code>subl</code> available in <code>PATH</code>.</p>
|
|
660
|
|
661 <%
|
|
662 end
|
|
663 }
|
48
|
664 find = {
|
|
665 title = [[The "find" Command]]
|
|
666 content = function()
|
|
667 %>
|
|
668 <code block>
|
|
669 ~/learn $ find .
|
|
670 .
|
|
671 ./file3
|
|
672 ./file2
|
|
673 ./file1
|
|
674 ./dir1
|
|
675 ./dir1/d1file
|
|
676 ~/learn $ find . -name 'file*'
|
|
677 ./file3
|
|
678 ./file2
|
|
679 ./file1
|
|
680 ~/learn $ find . -name '*file'
|
|
681 ./dir1/d1file
|
|
682 ~/learn $ find . -name 'd*'
|
|
683 ./dir1
|
|
684 ./dir1/d1file
|
|
685 ~/learn $ find . -name '*1' -or -name '*2'
|
|
686 ./file2
|
|
687 ./file1
|
|
688 ./dir1
|
|
689 </code>
|
|
690
|
|
691 <p><code>find</code> recursively searches for files in a directory tree. Note that in this case the <code>*</code> wildcard matching is not being done by Bash, it is being done by <code>find</code>. <code>find</code> has many options for searching for files and acting on them, see <code>man find</code>.</p>
|
|
692 <%
|
|
693 end
|
|
694 }
|
|
695 io = {
|
|
696 title = [[Input and Output]]
|
|
697 content = function()
|
|
698 %>
|
|
699 <code block>
|
|
700 ~/learn $ echo 'this is a test' >test.txt
|
|
701 ~/learn $ ls -F
|
|
702 dir1/ file1 file2 file3 test.txt
|
|
703 ~/learn $ cat test.txt
|
|
704 this is a test
|
|
705 ~/learn $ echo 'this is another test' >test.txt
|
|
706 ~/learn $ cat test.txt
|
|
707 this is another test
|
|
708 ~/learn $ echo 'another line' >>test.txt
|
|
709 ~/learn $ cat test.txt
|
|
710 this is another test
|
|
711 another line
|
49
|
712 ~/learn $ cat <test.txt
|
|
713 this is another test
|
|
714 another line
|
|
715 ~/learn $ cat <<End >test.txt
|
|
716 > I am typing this
|
|
717 > and this
|
|
718 > End
|
|
719 ~/learn $ cat test.txt
|
|
720 I am typing this
|
|
721 and this
|
|
722 ~/learn $ (echo one; echo two) >test.txt
|
|
723 ~/learn $ cat test.txt
|
|
724 one
|
|
725 two
|
|
726 </code>
|
|
727
|
|
728 <p>All programs have standard input, standard output, and standard error. Programs write normal output to standard output and error messages to standard error. By default, standard input comes from the terminal, and standard output and standard error go to the terminal, but this can be changed. <code>>file</code> sends standard output to <code>file</code>. <code>>>file</code> appends standard output to <code>file</code>. <code><file</code> reads standard input from <code>file</code>. <code><<whatever</code> reads standard input from the text that follows until a line with just <code>whatever</code>. Commands can be combined between <code>(</code> and <code>)</code>. Be sure to <code>man cat</code> to understand how <code>cat</code> works.</p>
|
|
729
|
|
730 <code block>
|
48
|
731 ~/learn $ ls >ls.txt
|
|
732 ~/learn $ cat ls.txt
|
|
733 dir1
|
|
734 file1
|
|
735 file2
|
|
736 file3
|
|
737 ls.txt
|
|
738 test.txt
|
|
739 ~/learn $ ls -d f* q* >ls.txt
|
|
740 ls: q*: No such file or directory
|
|
741 ~/learn $ cat ls.txt
|
|
742 file1
|
|
743 file2
|
|
744 file3
|
|
745 ~/learn $ ls -d f* q* 2>ls.txt
|
|
746 file1 file2 file3
|
|
747 ~/learn $ cat ls.txt
|
|
748 ls: q*: No such file or directory
|
|
749 ~/learn $ ls -d f* q* | tee ls.txt
|
|
750 ls: q*: No such file or directory
|
|
751 file1
|
|
752 file2
|
|
753 file3
|
|
754 ~/learn $ cat ls.txt
|
|
755 file1
|
|
756 file2
|
|
757 file3
|
|
758 ~/learn $ ls -d f* q* 2>&1 | tee ls.txt
|
|
759 ls: q*: No such file or directory
|
|
760 file1
|
|
761 file2
|
|
762 file3
|
|
763 ~/learn $ cat ls.txt
|
|
764 ls: q*: No such file or directory
|
|
765 file1
|
|
766 file2
|
|
767 file3
|
|
768 </code>
|
|
769
|
49
|
770 <p><code>2>file</code> sends standard error to <code>file</code>. <code>|</code> sends standard output of the previous command to standard input of the following command. <code>2>&1</code> sends standard error to standard output. <code>tee file</code> reads standard input and then writes it to both standard output and to <code>file</code>.</p>
|
48
|
771
|
|
772 <code block>
|
|
773 ~/learn $ find . -type f | wc -l
|
|
774 6
|
|
775 </code>
|
|
776
|
|
777 <p>There are 6 files in <code>learn</code>. Use <code>man</code> to figure out how this works.</p>
|
|
778 <%
|
|
779 end
|
|
780 }
|
49
|
781 ctrl = {
|
|
782 title = [[Control Keys]]
|
|
783 content = function()
|
|
784 %>
|
|
785 <code block>
|
|
786 ~/learn $ sleep 3
|
|
787 ~/learn $ sleep 30
|
|
788 ^C
|
|
789 ~/learn $
|
|
790 </code>
|
|
791
|
|
792 <p><code>sleep 3</code> sleeps for 3 seconds, meaning it does nothing for 3 seconds. I waited 3 seconds for this command to finish. Then I ran <code>sleep 30</code> which would sleep for 30 seconds, but I lost my patience and pressed control+c which interrupts the program and breaks out of it. You can try control+c if you ever get stuck waiting for a command to finish.</p>
|
|
793
|
|
794 <code block>
|
|
795 ~/learn $ wc
|
|
796 I am typing this
|
|
797 and this
|
|
798 now I will end my input with control+d
|
|
799 3 14 65
|
|
800 ~/learn $ wc
|
|
801 this time I will use control+c to break out
|
|
802 ^C
|
|
803 ~/learn $
|
|
804 </code>
|
|
805
|
|
806 <p>Control+d means end of input.</p>
|
|
807 <%
|
|
808 end
|
|
809 }
|
48
|
810 subst = {
|
|
811 title = [[Command Substitution]]
|
|
812 content = function()
|
|
813 %>
|
|
814 <code block>
|
|
815 ~/learn $ echo I am in $(pwd)
|
|
816 I am in /Users/fschmidt/learn
|
|
817 ~/learn $ echo this directory contains: $(ls)
|
|
818 this directory contains: dir1 file1 file2 file3 ls.txt test.txt
|
|
819 ~/learn $ echo this directory contains $(ls | wc -l) files
|
|
820 this directory contains 6 files
|
|
821 </code>
|
|
822
|
|
823 <p><code>cmd $(commands)</code> will use the output of <code>commands</code> as argument text for <code>cmd</code>.</p>
|
|
824
|
|
825 <code block>
|
|
826 ~/learn $ cat $(find . -type f) | wc -c
|
|
827 86
|
|
828 </code>
|
|
829
|
|
830 <p>The files in <code>learn</code> contain a total of 86 bytes. Use <code>man</code> to figure out how this works.</p>
|
|
831 <%
|
|
832 end
|
|
833 }
|
49
|
834 ampersand = {
|
|
835 title = [[Ampersand]]
|
|
836 content = function()
|
|
837 %>
|
|
838 <code block>
|
|
839 ~/learn $ (sleep 5; echo done) &
|
|
840 [1] 10080
|
|
841 ~/learn $ echo waiting
|
|
842 waiting
|
|
843 ~/learn $ done
|
|
844
|
|
845 [1]+ Done ( sleep 5; echo done )
|
|
846 ~/learn $
|
|
847 </code>
|
|
848
|
|
849 <p>Normally Bash waits for a command to complete before showing the command prompt and allowing input. But ending a command line with <code>&</code> tells bash not to wait, but instead to run the command in a separate process. Above in <code>~/learn $ echo waiting</code>, I typed in <code>echo waiting</code>. But in <code>~/learn $ done</code>, I did not type <code>done</code>. Instead this was produced by <code>echo done</code> after 5 seconds. <code>[1] 10080</code> tells me that a process was started and <code>[1]+ Done ( sleep 5; echo done )</code> tells me that the process finished.</p>
|
|
850
|
|
851 <p>This is useful where you do not want to wait for a command to finish. Consider this on Windows:</p>
|
|
852
|
|
853 <code block>
|
|
854 ~ $ notepad
|
|
855 </code>
|
|
856
|
|
857 <p>Here you will not get a command prompt again until you quit Notepad because Bash is waiting for this command to finish. So instead do:
|
|
858
|
|
859 <code block>
|
|
860 ~ $ notepad &
|
|
861 [1] 2010
|
|
862 ~ $
|
|
863 </code>
|
|
864
|
|
865 <p>Now Notepad will run and you can continue using Bash.</p>
|
|
866 <%
|
|
867 end
|
|
868 }
|
|
869 scripts = {
|
|
870 title = [[Shell Scripts]]
|
41
|
871 content = function()
|
|
872 %>
|
49
|
873 <p>Make a file called <code>test.sh</code> containing the following:</p>
|
|
874
|
|
875 <code block>
|
|
876 echo this is a shell script
|
|
877 </code>
|
|
878
|
|
879 <p>Now from Bash:</p>
|
|
880
|
|
881 <code block>
|
|
882 ~/learn $ cat test.sh
|
|
883 echo this is a shell script
|
|
884 ~/learn $ ./test.sh
|
|
885 -bash: ./test.sh: Permission denied
|
|
886 ~/learn $ ls -F test.sh
|
|
887 test.sh
|
|
888 ~/learn $ chmod +x test.sh
|
|
889 ~/learn $ ls -F test.sh
|
|
890 test.sh*
|
|
891 ~/learn $ ./test.sh
|
|
892 this is a shell script
|
|
893 ~/learn $
|
|
894 </code>
|
|
895
|
|
896 <p><code>chmod +x file</code> makes <code>file</code> into an executable that can be run. Now I will edit <code>test.sh</code></p>
|
|
897
|
|
898 <code block>
|
|
899 ~/learn $ # edit test.sh
|
|
900 ~/learn $ cat test.sh
|
|
901 nonsense
|
|
902 echo this is a shell script
|
|
903 ~/learn $ ./test.sh
|
|
904 ./test.sh: line 1: nonsense: command not found
|
|
905 this is a shell script
|
|
906 ~/learn $ # edit test.sh
|
|
907 ~/learn $ cat test.sh
|
|
908 set -e
|
|
909 nonsense
|
|
910 echo this is a shell script
|
|
911 ~/learn $ ./test.sh
|
|
912 ./test.sh: line 2: nonsense: command not found
|
|
913 ~/learn $
|
|
914 </code>
|
|
915
|
|
916 <p>By default, scripts continue running after an error. In longer scripts, we want the script to exit after an error. <code>set -e</code> does this, see <code>help set</code>.</p>
|
|
917
|
|
918 <code block>
|
|
919 ~/learn $ X=some
|
|
920 ~/learn $ echo $X
|
|
921 some
|
|
922 ~/learn $ echo $Xthing
|
|
923
|
|
924 ~/learn $ echo ${X}thing
|
|
925 something
|
|
926 ~/learn $ # edit test.sh
|
|
927 ~/learn $ cat test.sh
|
|
928 echo "\$* = $*"
|
|
929 echo "\$# = $#"
|
|
930 echo "\$0 = $0"
|
|
931 echo "\$1 = $1"
|
|
932 echo "\$2 = $2"
|
|
933 echo "\$3 = $3"
|
|
934 echo "\$4 = $4"
|
|
935 echo "\$14 = $14"
|
|
936 echo "\${14} = ${14}"
|
|
937 echo "\$@ = $@"
|
|
938 ./count.sh "$*"
|
|
939 ./count.sh "$@"
|
|
940 ~/learn $ ./test.sh a b "c d"
|
|
941 $* = a b c d
|
|
942 $# = 3
|
|
943 $0 = ./test.sh
|
|
944 $1 = a
|
|
945 $2 = b
|
|
946 $3 = c d
|
|
947 $4 =
|
|
948 $14 = a4
|
|
949 ${14} =
|
|
950 $@ = a b c d
|
|
951 1
|
|
952 3
|
|
953 ~/learn $ cat count.sh
|
|
954 echo $#
|
|
955 ~/learn $
|
|
956 </code>
|
|
957
|
|
958 <p>Bash scripts have special defined variables. The difference between <code>$*</code> and <code>$@</code> is subtle, and you will usually just use <code>$*</code>. <code>$*</code> returns all arguments as one string while <code>$@</code> returns the arguments separately, but this distinction rarely makes any difference.</p>
|
|
959 <%
|
|
960 end
|
|
961 }
|
|
962 vars_and_scripts = {
|
|
963 title = [[Variables and Scripts]]
|
|
964 content = function()
|
|
965 %>
|
|
966 <code block>
|
|
967 ~/learn $ X=value
|
|
968 ~/learn $ echo $X
|
|
969 value
|
|
970 ~/learn $ # edit test.sh
|
|
971 ~/learn $ cat test.sh
|
|
972 echo "\$X = $X"
|
|
973 ~/learn $ ./test.sh
|
|
974 $X =
|
|
975 ~/learn $ export X
|
|
976 ~/learn $ ./test.sh
|
|
977 $X = value
|
|
978 </code>
|
|
979
|
|
980 <p>Variables are defined in the current shell. Shell scripts are run in their own shell. So by default, they don't see variables defined in the terminal/parent shell. <code>export var</code> makes <code>var</code> available in descendant processes, meaning available in shell scripts. It is a good idea to do <code>export PATH</code> in <code>.bash_profile</code> so that your PATH is available to your scripts.</p>
|
|
981
|
|
982 <code block>
|
|
983 ~/learn $ X=terminal
|
|
984 ~/learn $ echo $X
|
|
985 terminal
|
|
986 ~/learn $ # edit test.sh
|
|
987 ~/learn $ cat test.sh
|
|
988 X=script
|
|
989 export X
|
|
990 ~/learn $ ./test.sh
|
|
991 ~/learn $ echo $X
|
|
992 terminal
|
|
993 ~/learn $ . test.sh
|
|
994 ~/learn $ echo $X
|
|
995 script
|
|
996 </code>
|
|
997
|
|
998 <p>You can export a variable from parent to children but not from children to parent. <code>. script</code> includes the text in the file <code>script</code> in the current shell. In this case, it is not run in a separate shell. This is the only way to have a script set variables in your terminal shell.</p>
|
|
999
|
|
1000 <code block>
|
|
1001 ~/learn $ pwd
|
|
1002 /Users/fschmidt/learn
|
|
1003 ~/learn $ # edit test.sh
|
|
1004 ~/learn $ cat test.sh
|
|
1005 cd ~
|
|
1006 ~/learn $ ./test.sh
|
|
1007 ~/learn $ pwd
|
|
1008 /Users/fschmidt/learn
|
|
1009 ~/learn $ . test.sh
|
|
1010 ~ $ pwd
|
|
1011 /Users/fschmidt
|
|
1012 ~ $ cd learn
|
|
1013 ~/learn $
|
|
1014 </code>
|
|
1015
|
|
1016 <p>This illustrates the difference between <code>./script</code> and <code>. script</code>.</p>
|
|
1017
|
|
1018 <%
|
|
1019 end
|
|
1020 }
|
|
1021 your_scripts = {
|
|
1022 title = [[Your Scripts]]
|
|
1023 content = function()
|
|
1024 %>
|
|
1025 <code block>
|
|
1026 ~/learn $ echo $PATH
|
|
1027 /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/fschmidt/Dropbox/bin:/Users/fschmidt/hg/luan/scripts:/usr/local/opt/postgresql@9.5/bin:/Applications/Sublime Text.app/Contents/SharedSupport/bin
|
|
1028 ~/learn $ echo ~/Dropbox/bin
|
|
1029 /Users/fschmidt/Dropbox/bin
|
|
1030 ~/learn $ ls -F ~/Dropbox/bin/e
|
|
1031 /Users/fschmidt/Dropbox/bin/e*
|
|
1032 ~/learn $ cat ~/Dropbox/bin/e
|
|
1033 open -a 'Sublime Text' $*
|
|
1034 ~/learn $ e test.sh
|
|
1035 ~/learn $
|
|
1036 </code>
|
|
1037
|
53
|
1038 <p>When you write useful scripts, put them in a directory and add that directory to your PATH. I use <code>~/Dropbox/bin</code> and I have a script named <code>e</code> in that directory for editing files. So <code>e test.sh</code> lets me edit <code>test.sh</code> from the command line.</p>
|
76
|
1039
|
|
1040 <p>Note that Bash will only look in your PATH for scripts unless you give an explicit path to the script.</p>
|
|
1041
|
|
1042 <code block>
|
|
1043 ~/learn $ # edit test.sh
|
|
1044 ~/learn $ cat test.sh
|
|
1045 echo this is a shell script
|
|
1046 ~/learn $ test.sh
|
|
1047 -bash: test.sh: command not found
|
|
1048 ~/learn $ ./test.sh
|
|
1049 this is a shell script
|
|
1050 ~/learn $
|
|
1051 </code>
|
|
1052
|
|
1053 <p>Calling <code>test.sh</code> by itself fails because it isn't in the PATH. But <code>./test.sh</code> works because it is an explicit path.</p>
|
49
|
1054 <%
|
|
1055 end
|
|
1056 }
|
|
1057 advanced = {
|
|
1058 title = [[Advanced Scripting]]
|
|
1059 content = function()
|
|
1060 %>
|
|
1061 <p>Here is a more advanced script called <code>undocx.sh</code> that unpacks a Word DOCX file.</p>
|
|
1062
|
|
1063 <code block>
|
|
1064 #!/bin/bash
|
|
1065
|
|
1066 set -e
|
|
1067
|
|
1068 if [ $# -ne 1 ]; then
|
|
1069 echo "usage: $0 filename"
|
|
1070 exit 1
|
|
1071 fi
|
|
1072
|
|
1073 FILE="$1"
|
|
1074 NEWDIR=$(basename $FILE .docx)
|
|
1075
|
|
1076 mkdir $NEWDIR
|
|
1077 unzip $FILE -d $NEWDIR
|
|
1078
|
|
1079 export XMLLINT_INDENT=$'\t'
|
|
1080 for file in $(find $NEWDIR -name "*.xml" -o -name "*.rels"); do
|
|
1081 mv "$file" temp.xml
|
|
1082 xmllint --format temp.xml >"$file"
|
|
1083 done
|
|
1084 rm temp.xml
|
|
1085 </code>
|
|
1086
|
|
1087 <p>Bash is a full programming language containing all the usual features. Some commands in my script are well explained by <code>man</code>, but some are not. In particular, the documentation for <code>if</code> and <code>for</code> are poor. In cases like this, I suggest asking ChatGPT like this:
|
|
1088
|
|
1089 <code block>
|
|
1090 Please explain the Bash "if" statement.
|
|
1091 </code>
|
|
1092
|
|
1093 <code block>
|
|
1094 Please explain the Bash "for" statement.
|
|
1095 </code>
|
|
1096
|
|
1097 <p>ChatGPT knows Bash well. I trust ChatGPT to explain details but not to explain core concepts. You can also try Google, but ChatGPT is better than modern programmers.</p>
|
|
1098 <%
|
|
1099 end
|
|
1100 }
|
|
1101 conclusion = {
|
|
1102 title = [[Conclusion]]
|
|
1103 content = function()
|
|
1104 %>
|
|
1105 <p>At least 90% of your usage of Bash will be simple commands that you enter in the terminal. Try to use Bash as much as possible instead of using the GUI so that you get practice using it. Unless you become system administrator, you won't use advanced scripting much. But with a solid understanding of the core basics, you should be able to figure out how to read or write advanced scripts when needed.</p>
|
41
|
1106 <%
|
|
1107 end
|
|
1108 }
|
40
|
1109 }
|
|
1110
|
|
1111
|
|
1112 local function show_toc(content)
|
|
1113 %>
|
|
1114 <ul>
|
|
1115 <%
|
|
1116 for id, info in pairs(content) do
|
|
1117 %>
|
|
1118 <li><a id="c_<%=id%>" href="#<%=id%>"><%=info.title%></a></li>
|
|
1119 <%
|
|
1120 end
|
|
1121 %>
|
|
1122 </ul>
|
|
1123 <%
|
|
1124 end
|
|
1125
|
|
1126 local function show_content(content,h)
|
|
1127 for id, info in pairs(content) do
|
|
1128 %>
|
|
1129 <div heading>
|
|
1130 <h<%=h%>><a id="<%=id%>" href="#<%=id%>"><%=info.title%></a></h<%=h%>>
|
|
1131 <a href="#c_<%=id%>">contents</a>
|
|
1132 </div>
|
|
1133 <%
|
|
1134 info.content()
|
|
1135 end
|
|
1136 end
|
|
1137
|
|
1138 return function()
|
91
|
1139 Io.stdout = text_writer()
|
40
|
1140 %>
|
|
1141 <!doctype html>
|
91
|
1142 <html lang="<%=get_lang()%>">
|
40
|
1143 <head>
|
|
1144 <% head() %>
|
45
|
1145 <title>Reactionary Bash Tutorial</title>
|
40
|
1146 </head>
|
|
1147 <body>
|
|
1148 <% header() %>
|
|
1149 <div content>
|
48
|
1150 <h1><a href="learn_bash.html">Reactionary Bash Tutorial</a></h1>
|
40
|
1151 <hr>
|
|
1152 <h2>Contents</h2>
|
|
1153 <div toc>
|
|
1154 <% show_toc(content) %>
|
|
1155 </div>
|
|
1156 <hr>
|
|
1157 <% show_content(content,2) %>
|
|
1158 </div>
|
|
1159 </body>
|
|
1160 </html>
|
|
1161 <%
|
|
1162 end
|