forked from lhunath/guide.bash.academy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path03.variables.html
More file actions
753 lines (647 loc) · 51.9 KB
/
03.variables.html
File metadata and controls
753 lines (647 loc) · 51.9 KB
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
---
id: variables
layout: chapter
title: Variables and Expansions
subtitle: "How do I store and work with data?"
status: drafting
description: "Bash parameters and variables; environment variables, special parameters and array parameters; expanding parameters, expansion operators, command substitution and process substitution; pathname expansion, tilde expansion and brace expansion."
published: true
---
<section>
<h1>How do I store and re-use data?</h1>
<p>We now know how to use bash for writing and managing simple commands. These commands give us access to many powerful operations on our system. We've learned how commands tell bash to execute programs by creating new processes for them. We've even learned to manipulate the basic input and output of these processes such that we can read from and write to arbitrary files.</p>
<p>Those of you that have been paying really close attention will even have spotted how we can pass arbitrary data into processes using constructs such as here-documents and here-strings.</p>
<p>The biggest limitation now is our inability to handle data flexibly. We can write it out to files and then read it in again, by employing many file redirections, and we can pass in static pre-defined data using here-documents and here-strings. But this leaves us longing for more.</p>
<p>High time to unlock the next level of wonders: bash parameters.</p>
<h2>What are bash parameters?</h2>
<p>Simply put, bash parameters are regions in memory where you can temporarily store some information for later use.</p>
<p>Not unlike files, we write to these parameters and read from them when we need to retrieve the information later. But since we're using the system's memory and not the disk to write this information to, access is much faster. Using parameters is also much easier and the syntax more powerful than redirecting input and output to and from files.</p>
<p>Bash provides a few different types of parameters: positional parameters, special parameters and shell variables. The latter are the most interesting type, the former two mainly give us access to certain information bash makes available to us. We'll introduce the practical aspects and usage of parameters through variables and then explain how positional and special parameters are different.</p>
<h2>Shell Variables</h2>
<p>A shell variable is essentially a bash parameter that has a name. You can use variables to store a value and later modify or read that value back for re-use.</p>
<p>Using variables is easy. You store information in them through variable assignment, and access that information at any later time using parameter expansion:</p>
<pre lang="bash">
$ <kbd>name=lhunath</kbd><em>Assign the value <code>lhunath</code> to the variable <code>name</code></em>
$ <kbd>echo "Hello, $name. How are you?"</kbd><em>Expand the value of <code>name</code> into the echo argument</em>
Hello, lhunath. How are you?
</pre>
<p>As you can see, the assignment creates a variable called <kbd>name</kbd> and puts a value in it. Expansion is done by prefixing the name with a <kbd title="dollar">$</kbd> symbol, which causes our value to get injected into the echo argument.</p>
<h3>Assignment</h3>
<p>Assignment uses the <kbd title="equals">=</kbd> operator. It is imperative that you understand there can be no syntactical space around the operator. While other languages may permit this, bash does not. Remember from the previous chapter that spaces in bash have a special meaning: they split commands into arguments. If we were to put spaces around the <code>=</code> operator, they would cause bash to split the command into a command name and arguments,
thinking you wanted to execute a program rather than assign a variable value:</p>
<pre lang="bash" class="bad">
$ <kbd>name = lhunath</kbd><em>Run the command <code>name</code> with arguments <code>=</code> and <code>lhunath</code>.</em>
-bash: name: command not found
</pre>
<p>To fix this code, we simply remove the space around the <code>=</code> operator that was causing the word splitting. If we wanted to assign a value to the variable which begins with a few literal space characters, we'll need to use quotes to signal bash that our space is literal and shouldn't serve to trigger word splitting:</p>
<pre lang="bash" class="good">
$ <kbd>name=lhunath</kbd>
$ <kbd>item=' 4. Milk'</kbd><em>Use quotes to make the spaces literal.</em>
</pre>
<h3>Expansion</h3>
<p>Assigning values to variables is neat but not really immediately useful. It's being able to re-use those values at any time that makes parameters so interesting. Re-using parameter values is done by expanding them. Expansion effectively takes the data out of your parameter and inlines it into the data of your command. As we saw briefly before, we expand parameters by prefixing their name with a <code>$</code> symbol. Whenever you see this symbol in bash, it's probably
because something is getting expanded. It could be a parameter, or the output of a command, or the result of an arithmetic operation. We'll learn more about the other expansions later on.</p>
<p>As we've already highlighted (but always bears repeating), your expansions should always be double-quoted to avoid bash's word-splitting unexpectedly tearing it to pieces. In addition, parameter expansion allows you to wrap curly braces (<kbd>{</kbd> and <kbd>}</kbd>) around your expansion. These braces are used to tell bash what the beginning and end of your parameter name is. They are usually optional, as bash can often figure the name out by itself. Though
sometimes they become a necessity:</p>
<pre lang="bash">
$ <kbd>name=Britta time=23.73</kbd><em>We want to expand <code>time</code> and add an <code>s</code> for seconds</em>
$ <kbd>echo "$name's current record is $times."</kbd><em>but bash mistakes the name for <code>times</code> which holds nothing</em>
Britta's current record is .
$ <kbd>echo "$name's current record is ${time}s."</kbd><em>Braces explicitly tell bash where the name ends</em>
Britta's current record is 23.73s.
</pre>
<p>Parameter expansions are great for inserting user or program data into our command instructions, but they also have an extra ace up their sleeve: expansion operators. While expanding a parameter, it is possible to apply an operator to the expanding value. This operator can modify the value in one of many useful ways:</p>
<pre lang="bash">
$ <kbd>name=Britta time=23.73</kbd>
$ <kbd>echo "$name's current record is ${time%.*} seconds and ${time#*.} hundredths."</kbd>
Britta's current record is 23 seconds and 73 hundredths.
$ <kbd>echo "PATH currently contains: ${PATH//:/, }"</kbd>
PATH currently contains: /Users/lhunath/.bin, /usr/local/bin, /usr/bin, /bin, /usr/libexec
</pre>
<p>The examples above use the <code>#</code>, <code>%</code> and <code>//</code> operators to perform various operations on the parameter's value before expanding the result. The parameters themselves aren't changed, only the value that is expanded into place is affected by the operator. In the first case, we used the <code>%</code> operator to remove the <code title="dot">.</code> and the number after it from <code>time</code>'s value before expanding it. That left us
with just the part in front of the <code>.</code>, which is the seconds. The second case did something similar, we used the <code>#</code> operator to remove a part from the start of the <code>time</code> value. Finally, we used the <code>//</code> operator, (which is really a special case of the <code>/</code> operator), to replace every <code title="colon">:</code> character in <code>PATH</code>'s value with <code>, </code>. The result is a list of directories that is
easier to read for people than the original colon-separated <code>PATH</code>.
<table>
<thead>
<tr>
<th colspan="3"><kbd>url='http://guide.bash.academy/variables.html'</kbd></th>
</tr>
</thead>
<tr>
<th>Operator</th>
<th>Example</th>
<th>Result</th>
</tr>
<tr>
<th>
<code class="syntax"><strong>${</strong><var>parameter</var><strong>#</strong><var>pattern</var><strong>}</strong></code>
</th>
<td><kbd>"${url#<mark>*/</mark>}"</kbd></td>
<td rowspan="2">
<pre><mark>http:/</mark>/guide.bash.academy/variables.html
↓
/guide.bash.academy/variables.html</pre>
</td>
</tr>
<tr>
<td colspan="2">Remove the <em>shortest</em> string that matches the <var>pattern</var> if it's at the start of the value.</td>
</tr>
<tr>
<th>
<code class="syntax"><strong>${</strong><var>parameter</var><strong>##</strong><var>pattern</var><strong>}</strong></code>
</th>
<td><kbd>"${url##<mark>*/</mark>}"</kbd></td>
<td rowspan="2">
<pre><mark>http://guide.bash.academy/</mark>variables.html
↓
variables.html</pre>
</td>
</tr>
<tr>
<td colspan="2">Remove the <em>longest</em> string that matches the <var>pattern</var> if it's at the start of the value.</td>
</tr>
<tr>
<th>
<code class="syntax"><strong>${</strong><var>parameter</var><strong>%</strong><var>pattern</var><strong>}</strong></code>
</th>
<td><kbd>"${url%<mark>/*</mark>}"</kbd></td>
<td>
<pre>http://guide.bash.academy<mark>/variables.html</mark>
↓
http://guide.bash.academy</pre>
</td>
</tr>
<tr>
<td colspan="2">Remove the <em>shortest</em> string that matches the <var>pattern</var> if it's at the end of the value.</td>
</tr>
<tr>
<th>
<code class="syntax"><strong>${</strong><var>parameter</var><strong>%%</strong><var>pattern</var><strong>}</strong></code>
</th>
<td><kbd>"${url%%<mark>/*</mark>}"</kbd></td>
<td>
<pre>http:<mark>//guide.bash.academy/variables.html</mark>
↓
http:</pre>
</td>
</tr>
<tr>
<td colspan="2">Remove the <em>longest</em> string that matches the <var>pattern</var> if it's at the end of the value.</td>
</tr>
<tr>
<th>
<code class="syntax"><strong>${</strong><var>parameter</var><strong>/</strong><var>pattern</var><strong>/</strong><var>replacement</var><strong>}</strong></code>
</th>
<td><kbd>"${url/<mark>.</mark>/<mark>-</mark>}"</kbd></td>
<td>
<pre>http://guide<mark>.</mark>bash.academy/variables.html
↓
http://guide-bash.academy/variables.html</pre>
</td>
</tr>
<tr>
<td colspan="2">Replace <em>the first</em> string that matches the <var>pattern</var> with the replacement.</td>
</tr>
<tr>
<th>
<code class="syntax"><strong>${</strong><var>parameter</var><strong>//</strong><var>pattern</var><strong>/</strong><var>replacement</var><strong>}</strong></code>
</th>
<td><kbd>"${url//<mark>.</mark>/<mark>-</mark>}"</kbd></td>
<td>
<pre>http://guide<mark>.</mark>bash<mark>.</mark>academy/variables<mark>.</mark>html
↓
http://guide-bash-academy/variables-html</pre>
</td>
</tr>
<tr>
<td colspan="2">Replace <em>each</em> string that matches the <var>pattern</var> with the replacement.</td>
</tr>
<tr>
<th>
<code class="syntax"><strong>${</strong><var>parameter</var><strong>/#</strong><var>pattern</var><strong>/</strong><var>replacement</var><strong>}</strong></code>
</th>
<td><kbd>"${url/#<mark>*:</mark>/<mark>https:</mark>}"</kbd></td>
<td>
<pre><mark>http:</mark>//guide.bash.academy/variables.html
↓
https://guide.bash.academy/variables.html</pre>
</td>
</tr>
<tr>
<td colspan="2">Replace the string that matches the <var>pattern</var> at the <em>beginning</em> of the value with the replacement.</td>
</tr>
<tr>
<th>
<code class="syntax"><strong>${</strong><var>parameter</var><strong>/%</strong><var>pattern</var><strong>/</strong><var>replacement</var><strong>}</strong></code>
</th>
<td><kbd>"${url/%<mark>.html</mark>/<mark>.jpg</mark>}"</kbd></td>
<td>
<pre>http://guide.bash.academy/variables<mark>.html</mark>
↓
http://guide.bash.academy/variables.jpg</pre>
</td>
</tr>
<tr>
<td colspan="2">Replace the string that matches the <var>pattern</var> at the <em>end</em> of the value with the replacement.</td>
</tr>
<tr>
<th>
<code class="syntax"><strong>${#</strong><var>parameter</var><strong>}</strong></code>
</th>
<td><kbd>"${#url}"</kbd></td>
<td>
<pre>http://guide.bash.academy/variables.html
↓
40</pre>
</td>
</tr>
<tr>
<td colspan="2">Expand the length of the value (in bytes).</td>
</tr>
<tr>
<th>
<code class="syntax"><strong>${</strong><var>parameter</var><strong>:</strong><var>start</var>[<strong>:</strong><var>length</var>]<strong>}</strong></code>
</th>
<td><kbd>"${url:<mark>7</mark>}"</kbd></td>
<td>
<pre>http://<mark>guide.bash.academy/variables.html</mark>
↓
guide.bash.academy/variables.html</pre>
</td>
</tr>
<tr>
<td colspan="2">Expand a part of the value, starting at <var>start</var>, <var>length</var> bytes long.</td>
</tr>
<tr>
<th>
<code class="syntax"><strong>${</strong><var>parameter</var>[<strong>^</strong>|<strong>^^</strong>|<strong>,</strong>|<strong>,,</strong>][<var>pattern</var>]<strong>}</strong></code>
</th>
<td><kbd>"${url^^<mark>[ht]</mark>}"</kbd></td>
<td>
<pre><mark>htt</mark>p://guide.bas<mark>h</mark>.academy/variables.<mark>ht</mark>ml
↓
HTTp://guide.basH.academy/variables.HTml</pre>
</td>
</tr>
<tr>
<td colspan="2">Expand the transformed value, either upper-casing or lower-casing the first or all characters that match the <var>pattern</var>. You can omit the pattern to match any character.</td>
</tr>
</table>
<p> </p>
<footer>
Shell variables are parameters that you can freely assign values to. Assignment is done using the syntax <code>var=value</code>. Parameters can be expanded to inline their data into a command's arguments. Parameter expansion is done by prefixing the variable name with a <code>$</code> symbol. Sometimes, you'll need to add <code>{</code> and <code>}</code> braces around the parameter name to explicitly tell bash where the name of your parameter begins and ends (eg.
<code>"${time}s"</code>).<br>
Parameter expansions should <strong>always be double-quoted</strong> for consistency and to prevent any potential white-space in them from causing word-splitting in addition to triggering unexpected pathname completion. While expanding parameters, you can apply a special parameter expansion operator to mutate the expanded value in some way.
</footer>
<h2 id="expansion_ex">Exercises!</h2>
<h4>EXPAN.1. Assign <kbd>hello</kbd> to the variable <var>greeting</var>.</h4>
<pre class="exercise"><samp><kbd>greeting=hello</kbd></samp></pre>
<h4>EXPAN.2. Show the contents of the variable <var>greeting</var>.</h4>
<pre class="exercise"><samp><kbd>echo "$greeting"</kbd>
hello</samp></pre>
<h4>EXPAN.3. Assign the string <kbd> world</kbd> to the end of the variable's current contents.</h4>
<pre class="exercise"><samp><kbd>greeting="$greeting world"</kbd></samp>
<samp><kbd>greeting+=" world"</kbd><em><code>+=</code> appends the string to the end of the current value.</em></samp></pre>
<h4>EXPAN.4. Show the last word in the variable <var>greeting</var>.</h4>
<pre class="exercise"><samp><kbd>echo "${greeting##* }"</kbd>
world</samp></pre>
<h4>EXPAN.5. Show the contents of the variable <var>greeting</var> with the first character upper-cased and an exclamation mark (<code>!</code>) at the end.</h4>
<pre class="exercise"><samp><kbd>echo "${greeting^}!"</kbd>
Hello world!</samp></pre>
<h4>EXPAN.6. Replace the first space character in the variable's contents with <var> big </var>.</h4>
<pre class="exercise"><samp><kbd>greeting=${greeting/ / big }</kbd></samp></pre>
<h4>EXPAN.7. Redirect the contents of the variable <var>greeting</var> into a file whose name is the value of the variable with the spaces replaced by underscores (<code>_</code>) and a <code>.txt</code> at the end.</h4>
<pre class="exercise"><samp><kbd>echo "$greeting" > "${greeting// /_}.txt"</kbd></samp></pre>
<h4>EXPAN.8. Show the contents of the variable <var>greeting</var> with the middle word fully upper-cased.</h4>
<pre class="exercise"><samp><kbd>middle=${greeting% *} middle=${middle#* }; echo "${greeting%% *} ${middle^^} ${greeting##* }"</kbd>
hello BIG world</samp></pre>
</section>
<section>
<h1>What is the environment and what is it used for?</h1>
<p>There are two separate spaces where variables are kept. These separate spaces are often confused, leading to many misunderstandings. You've already become familiar with the first: shell variables. The second space where variables are kept is the process environment. We'll introduce environment variables and explain how they differ from shell variables.</p>
<h2>Environment Variables</h2>
<p>Unlike shell variables, environment variables exist at the process level. That means they are not a feature of the bash shell, but rather a feature of any program process on your system. If we imagine a process as a piece of land you buy, the building we put on the land will be the code running in your process. You could put a <code>bash</code> house or a <code>grep</code> shack or a <code>firefox</code> tower on the land. Environment variables are variables
stored on your process' land itself, while shell variables are stored inside the bash house built on your land.<br />
You can store variables in the environment and you can store variables in the shell. The environment is something every process has, while the shell space is only available to bash processes. As a rule, <em>you should put your variables in the shell space</em> unless you explicitly require the behaviour of environment variables.</p>
<pre>
╭─── bash ─────────────────────────╮
│ ╭──────────────────╮ │
│ ENVIRONMENT │ SHELL │ │
│ │ shell_var1=value │ │
│ │ shell_var2=value │ │
│ ╰──────────────────╯ │
│ ENV_VAR1=value │
│ ENV_VAR2=value │
╰──────────────────────────────────╯
</pre>
<p>When you run a new program from the shell, bash will run this program in a new process. When it does, this new process will have its own environment. But unlike shell processes, ordinary processes do not have shell variables. They only have environment variables. More importantly, when a new process is created, its environment is populated by making a <strong>copy</strong> of the environment of the creating process:</p>
<pre>
╭─── bash ───────────────────────╮
│ ╭────────────────╮ │
│ ENVIRONMENT │ SHELL │ │
│ │ greeting=hello │ │
│ ╰────────────────╯ │
│ HOME=/home/lhunath │
│ PATH=/bin:/usr/bin │
╰─┬──────────────────────────────╯
╎ ╭─── ls ─────────────────────────╮
└╌╌┥ │
│ ENVIRONMENT │
│ │
│ HOME=/home/lhunath │
│ PATH=/bin:/usr/bin │
╰────────────────────────────────╯
</pre>
<p>It is a common misconception that the environment is a system-global pool of variables that all processes share. This illusion is often the result of seeing the same variables available in child processes. When you create a custom environment variable in the shell, any child processes you create afterwards will inherit this variable as a result of it being copied from your shell into the child's environment. However, since the environment is specific to each process,
changing or creating new variables in the child will in no way affect the parent:</p>
<pre>
╭─── bash ───────────────────────╮
│ ╭────────────────╮ │
│ ENVIRONMENT │ SHELL │ │
│ │ greeting=hello │ │
│ ╰────────────────╯ │
│ HOME=/home/lhunath │
│ PATH=/bin:/usr/bin │
│ NAME=<mark>Bob</mark> │
╰─┬──────────────────────────────╯
╎ ╭─── bash ───────────────────────╮
└╌╌┥ ╭────────────────╮ │
│ ENVIRONMENT │ SHELL │ │
│ ╰────────────────╯ │
│ HOME=/home/lhunath │
│ PATH=/bin:/usr/bin │
│ NAME=<mark>Bob</mark> │
╰────────────────────────────────╯
$ <kbd>NAME=John</kbd>
╭─── bash ───────────────────────╮
│ ╭────────────────╮ │
│ ENVIRONMENT │ SHELL │ │
│ │ greeting=hello │ │
│ ╰────────────────╯ │
│ HOME=/home/lhunath │
│ PATH=/bin:/usr/bin │
│ NAME=<mark>Bob</mark> │
╰─┬──────────────────────────────╯
╎ ╭─── bash ───────────────────────╮
└╌╌┥ ╭────────────────╮ │
│ ENVIRONMENT │ SHELL │ │
│ ╰────────────────╯ │
│ HOME=/home/lhunath │
│ PATH=/bin:/usr/bin │
│ NAME=<mark>John</mark> │
╰────────────────────────────────╯
</pre>
<p>This distinction also makes it clear why one would opt to put certain variables in the environment. While most of your variables will be ordinary shell variables, you may opt to "export" some of your shell variables into the shell's process environment. In doing so, you're effectively exporting your variable's data to each child process you create, and those child processes will in turn export their environment variables to their children. Your system uses environment variables for all sorts of things, mainly to provide state information and default configurations for certain processes.</p>
<p>For instance, the <code>login</code> program, which is traditionally used to log a user into the system, exports information about your user into the environment (<var>USER</var> containing your user name, <var>HOME</var> containing your home directory, <var>PATH</var> containing a standard command search path, etc.). All processes that run as a result of you logging in can now learn what user they're running for by looking at the environment.</p>
<p>You can export your own variables into the environment. This is often done to configure the behavior of any programs you run. For instance, you can export <var>LANG</var> and assign it a value that tells programs what language and character set they should use. Envrionment variables are generally only useful to those programs that know about and support them explicitly. Some variables have a very narrow usage, for instance
<var>LSCOLORS</var> can be used by some <code>ls</code> programs to colorize their output of files on your system.</p>
<pre>
╭─── bash ───────────────────────╮
│ ╭────────────────╮ │
│ ENVIRONMENT │ SHELL │ │
│ │ greeting=hello │ │
│ ╰────────────────╯ │
│ HOME=/home/lhunath │
│ PATH=/bin:/usr/bin │
│ LANG=en_CA │
│ PAGER=less │
│ LESS=-i -R │
╰─┬──────────────────────────────╯
╎ ╭─── rm ─────────────────────────╮<em><code>rm</code> uses just <var>LANG</var> if present to determine</em>
├╌╌┥ │<em>the language of its error messages.</em>
╎ │ ENVIRONMENT │
╎ │ │
╎ │ HOME=/home/lhunath │
╎ │ PATH=/bin:/usr/bin │
╎ │ <mark>LANG=en_CA</mark> │
╎ │ PAGER=less │
╎ │ LESS=-i -R │
╎ ╰────────────────────────────────╯
╎ ╭─── man ────────────────────────╮<em>In addition to <var>LANG</var>, <code>man</code> uses <var>PAGER</var> to determine</em>
└╌╌┥ │<em>what program to use for paginating long manuals.</em>
│ ENVIRONMENT │
│ │
│ HOME=/home/lhunath │
│ PATH=/bin:/usr/bin │
│ <mark>LANG=en_CA</mark> │
│ <mark>PAGER=less</mark> │
│ LESS=-i -R │
╰─┬──────────────────────────────╯
╎ ╭─── less ───────────────────────╮<em><code>less</code> makes use of the <var>LESS</var> variable to supply</em>
└╌╌┥ │<em>an initial configuration for itself.</em>
│ ENVIRONMENT │
│ │
│ HOME=/home/lhunath │
│ PATH=/bin:/usr/bin │
│ <mark>LANG=en_CA</mark> │
│ PAGER=less │
│ <mark>LESS=-i -R</mark> │
╰────────────────────────────────╯
</pre>
<h2>Shell Initialization</h2>
<p>When you start an interactive bash session, bash will prepare itself for usage by reading a few initialization commands from different files on your system. You can use these files to tell bash how to behave. One in particular is intended to give you the opportunity to export variables into the environment. The file is called <code>.bash_profile</code> and it lives in your home directory. There's a good chance that you don't have this file yet; if this is the
case, you can just create the file and bash will find it the next time it goes looking for it.</p>
<p>At the very end of your <code>~/.bash_profile</code>, you should have the command <code>source ~/.bashrc</code>. That's because when <code>.bash_profile</code> exists, bash behaves a little curious in that it stops looking for its standard shell initialization file <code>~/.bashrc</code>. The <code>source</code> command remedies this oddity.</p>
<p>Note that if there is no <code>~/.bash_profile</code> file, bash will try to read from <code>~/.profile</code> instead, if it exists. The latter is a generic shell profile configuration file, which is also read by other shells. You can opt to put your environment configuration there instead, but if you do, you need to be aware that you should limit yourself to POSIX sh
syntax and not use any bash-specific shell syntax in the file. POSIX sh syntax is similar to bash but it is beyond the scope of this guide.</p>
<pre>
login<em>The <code>login</code> program signs the user in</em>
│
╰─ <strong>-bash</strong><em>The <code>login</code> command starts the user's login shell</em>
│
╰─ screen<em>The user runs the <code>screen</code> program from his login shell</em>
│
╰─ weechat<em>The <code>screen</code> program creates multiple windows</em>
│ <em>and allows the user to switch between them.</em>
╰─ bash <em>The first runs an IRC client, two others run a</em>
│ <em>non-login bash shell.</em>
╰─ bash
</pre>
<p>This process tree depicts a user who uses bash as his login shell and multiplexes his terminal to create several separate "screens", allowing him to interact with multiple concurrently running programs. After logging in, the system (the <code>login</code> program) determines the user's login shell. It might do this, for example, by looking at <code>/etc/passwd</code>. In this case, the user's login shell is set to bash. <code>login</code> proceeds by running bash and
setting its name to <code>-bash</code>. It is standard procedure for the <code>login</code> program to prefix the name of the login shell with a <code>-</code> (dash), indicating to the shell that it should behave as a login shell.</p>
<p>Once the user has a running bash login shell, he runs the <code>screen</code> program. While screen is running, it takes over the user's entire terminal and emulates multiple terminals within it, allowing the user to switch between them. In each emulated terminal, screen runs a new program. In this case, the user has screen configured to start one emulated terminal that runs an IRC client, and two that run interactive (but non-login) bash shells. Here's what that would
look like in practice:</p>
<script type="text/javascript" src="https://asciinema.org/a/13948.js" id="asciicast-13948" async></script>
<p>Let's take a look at how the initialization happens in this scenario, and where the environment variables come from:</p>
<pre>
login
│ <ins>TERM=dumb</ins>
│ <ins>USER=lhunath</ins>
│ <ins>HOME=/home/lhunath</ins>
│ <ins>PATH=/usr/bin:/bin</ins>
│
╰─ -bash
│ TERM=dumb
│ USER=lhunath
│ HOME=/home/lhunath
│ <del>PATH=/usr/bin:/bin</del>
│ <ins>PWD=/home/lhunath</ins>
│ <ins>SHLVL=1</ins>
│╭──────────────╮ ╭────────────────────────╮╭──────────────────╮
┝┥ login shell? ┝─yes─┥ source ~/.bash_profile ┝┥ source ~/.bashrc │
│╰──────────────╯ ╰────────────────────────╯╰──────────────────╯
│ <ins>PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/libexec</ins>
│ <ins>EDITOR=vim</ins>
│ <ins>LANG=en_CA.UTF-8</ins>
│ <ins>LESS=-i -M -R -W -S</ins>
│ <ins>GREP_COLOR=31</ins>
│
╰─ screen
│ <del>TERM=dumb</del>
│ <ins>TERM=screen-bce</ins>
│ USER=lhunath
│ HOME=/home/lhunath
│ PATH=/usr/bin:/bin
│ PWD=/home/lhunath
│ SHLVL=1
│ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/libexec
│ EDITOR=vim
│ LANG=en_CA.UTF-8
│ LESS=-i -M -R -W -S
│ GREP_COLOR=31
│ <ins>WINDOW=0</ins>
│
╰─ weechat
│
╰─ bash
│ │╭──────────────╮
│ ╰┥ login shell? ┝
│ ╰──────┰───────╯
│ no
│ ╭──────┸───────╮ ╭──────────────────╮
│ │ interactive? ┝─yes─┥ source ~/.bashrc │
│ ╰──────────────╯ ╰──────────────────╯
╰─ bash
│╭──────────────╮
╰┥ login shell? ┝
╰──────┰───────╯
no
╭──────┸───────╮ ╭──────────────────╮
│ interactive? ┝─yes─┥ source ~/.bashrc │
╰──────────────╯ ╰──────────────────╯
</pre>
<p>As you can see, different levels export their own variables into the environment. Each child process inherits the variables from its parent's environment. In turn, it can overwrite some of these values or add new variables.</p>
<p>Notice how the first (login) bash sources both <code>~/.bash_profile</code> and <code>~/.bashrc</code> while the bottom two source only <code>~/.bashrc</code>. That's because only the first bash process is started as a "login shell" (by means of having a <code>-</code> in front of its name). The bottom two bash processes are ordinary interactive shells. The reason they have no need for sourcing <code>~/.bash_profile</code> is now becoming more obvious: the responsibility of
<code>~/.bash_profile</code> is to set up bash's environment, and the bottom two shells are already inheriting the environment from their login shell ancestor.</p>
</section>
<section>
<h1>What else can I use parameters for?</h1>
<p>As we mentioned earlier in the chapter, there are positional parameters, special parameters and variables. Variables are essentially parameters with a name. We're going to have a closer look at the different kinds of parameters and how they allow you to get specific information from the shell or change certain behaviours of the shell.</p>
<h2>Positional Parameters</h2>
<p>Where variables are parameters with a name, positional parameters are parameters with a number (more specifically, a positive integer number). We expand these parameters using the normal parameter expansion syntax: <code>$1</code>, <code>$3</code>. It's important to note, though, that bash requires you to employ curly braces around positional parameters of more than one digit: <code>${10}</code>, <code>${22}</code> (in practice, you will rarely if ever need to explicitly refer to positional parameters this high up).</p>
<p>Positional parameters expand to values that were sent into the process as arguments when it was created by the parent. For instance, when you start a <code>grep</code> process using this command:</p>
<pre lang="bash">
$ <kbd>grep Name registrations.txt</kbd>
</pre>
<p>You're effectively running the <code>grep</code> command with the arguments <code>Name</code> and <code>registrations.txt</code>. If <code>grep</code> were a bash script, the first argument would be available in the script by expanding <code>$1</code> and the second argument by expanding <code>$2</code>. Positional parameters higher than <code>2</code> will be unset.</p>
<p>It's good to know that there is also a zero'th positional parameter. This positional parameter expands to the <em>name</em> of the process. The name of the process is chosen by the program that creates it, so the zero'th argument can really contain anything and is entirely up to your script's parent. Most shells will use the absolute path of the file that they ran to start the process as the name of the process, or the command that the user executed to start the process. Be aware that this is by no means a requirement and you cannot make any reliable assumptions on the contents of the zero'th argument: it is best avoided for all intents and purposes.</p>
<p>What's nice and extremely convenient: most of what we've learned thus far about variable parameters applies to positional parameters as well: we can expand them and we can apply parameter expansion operators on these expansions to mutate the resulting values:</p>
<pre lang="bash">
<kbd>#!/usr/bin/env bash</kbd>
<kbd>echo "The Name Script"</kbd>
<kbd>echo "usage: names 'My Full Name'"; echo</kbd>
<kbd></kbd>
<kbd>first=${1%% *} last=${1##* } middle=${1#$first} middle=${middle%$last}</kbd>
<kbd>echo "Your first name is: $first"</kbd>
<kbd>echo "Your last name is: $last"</kbd>
<kbd>echo "Your middle names are: $middle"</kbd>
</pre>
<p>If you save this script in a file called <code>names</code> and run it according to the usage description, by passing a single argument to it, you'll see the script analyse your name and inform you which part of your name constitute the first, last and middle names. We're using the variables <var>first</var>, <var>last</var> and <var>middle</var> to store these pieces of information for later, when we expand the variables in the <code>echo</code> statements. Notice how the
computation of the <var>middle</var> name requires both the knowledge of the full name (available from the first positional parameter) and the first name (which was previously computed and stored in the variable <var>first</var>).</p>
<pre lang="bash">
$ <kbd>chmod +x names</kbd>
$ <kbd>./names 'Maarten Billemont'</kbd>
The Name Script
usage: names 'My Full Name'
Your first name is: Maarten
Your last name is: Billemont
Your middle names are:
$ <kbd>./names 'James Tiberius "Jim" Kirk'</kbd>
The Name Script
usage: names 'My Full Name'
Your first name is: James
Your last name is: Kirk
Your middle names are: Tiberius "Jim"
</pre>
<p>It is important to understand that, unlike most variables, positional parameters are read-only parameters. On reflection, it likely makes sense to you that one cannot change the arguments to your script from within your script. As such, this is a syntax error:</p>
<pre lang="bash" class="bad">
$ <kbd>1='New First Argument'</kbd>
-bash: 1=New First Argument: command not found
</pre>
<p>While the error message is slightly confounding, it indicates that bash doesn't even recognize this statement as an attempt to assign a value to a variable (since the parameter <code>1</code> is not a variable) and instead thinks you have given it the name of a command you want to run.</p>
<p>There is, however, a built-in command we can use to change the values of the set of positional parameters. While this is a common practice in ancient shells that lack bash's more advanced features, you will rarely if ever have a need for this in bash. To modify the current set of positional parameters, use the <code>set</code> command and specify the new positional parameters as arguments after the <code>--</code> argument:</p>
<pre lang="bash">
$ <kbd>set -- 'New First Argument' Second Third 'Fourth Argument'</kbd>
$ <kbd>echo "1: $1, 2: $2, 4: $4"</kbd>
1: New First Argument, 2: Second, 4: Fourth Argument
</pre>
<p>In addition to changing the set of positional parameters, there is also the <code>shift</code> built-in that can be used for "pushing" our set of positional parameters around. When we shift positional parameters, we essentially push them all toward the beginning, causing the first few positional parameters to get bumped off to make way for the others:</p>
<pre lang="bash">
<mark>New First Argument</mark> <mark>Second</mark> <mark>Third</mark> <mark>Fourth Argument</mark>
$ <kbd>shift 2</kbd><em>Push the positional parameters back 2.</em>
<mark>Third</mark> <mark>Fourth Argument</mark> <----<em>The first two disappeared and the third is now in first spot with the fourth in second place.</em>
</pre>
<p>Finally, when starting a new bash shell using the <code>bash</code> command, there is a way to pass in positional parameters. This is a very useful way of passing a list of arguments to an inline bash script. You will use this method later when you combine inline bash code with other utilities, but for now this is a great way of experimenting with positional parameters without having to create a separate script to invoke and pass arguments to (such as we did with the
<code>names</code> example above). Here's how to run an inline bash command and pass in a list of arguments to populate the positional parameters:</p>
<pre lang="bash">
$ <kbd>bash -c 'echo "1: $1, 2: $2, 4: $4"' -- 'New First Argument' Second Third 'Fourth Argument'</kbd>
1: New First Argument, 2: Second, 4: Fourth Argument
</pre>
<p>We run the <code>bash</code> command, passing the <code>-c</code> option followed by an argument that contains some bash shell code. This will tell bash that instead of starting a new interactive bash shell, you want to just have the shell run the provided bash code and finish. After the shell code, we specify the arguments to use for populating the positional parameters. The first argument in our example is <code>--</code>, and while this argument is technically used to populate
the zero'th positional parameter, it is a good idea to always use <code>--</code> for the sake of compatibility and to make clear the separation between bash's arguments and the arguments to your shell code. After this argument, each argument populates the standard positional parameters as you would expect.</p>
<p>Take note of the fact that all arguments, and in particular the argument containing the bash code, are <strong><code>'single-quoted'</code></strong>. Whenever code is put in a string (such as in the case of passing it in as an argument), the code <em>should</em> be single-quoted. Do not use <code>"double quotes"</code> to wrap code strings. This is important because single quotes are much more reliable at making the wrapped data literal than
double quotes. If we used double-quotes in the example above, the shell that we're typing the <code>bash</code> command into would expand the <code>$1</code>, <code>$2</code> and <code>$4</code> expansions instead, resulting in a broken argument to the <code>-c</code> option.
<p>To illustrate this point, compare our good example from above:</p>
<pre lang="bash" class="good">
$ <kbd>bash -vc <mark>'echo "1: $1, 2: $2, 4: $4"'</mark> -- \</kbd><em>We pass the -v argument to bash to show us the code it is going to run before the result.</em>
<kbd>'New First Argument' Second Third 'Fourth Argument'</kbd><em>We can use \ at the end of a line to resume on a new line.</em>
echo "1: $1, 2: $2, 4: $4"<em>Here is the code it is going to run.</em>
1: New First Argument, 2: Second, 4: Fourth Argument<em>And this is the result.</em>
</pre>
<p>to what would happen if we used double quotes around the <code>-c</code> argument instead of single quotes:</p>
<pre lang="bash" class="bad">
$ <kbd>bash -vc <mark>"echo "1:</mark> <mark>$1,</mark> <mark>2:</mark> <mark>$2,</mark> <mark>4:</mark> <mark>$4""</mark> -- \</kbd><em>The outer double-quotes conflict with the inner double-quotes, leading to ambiguity.</em>
<kbd>'New First Argument' Second Third 'Fourth Argument'</kbd>
echo 1:<em>As a result, the argument to -c is no longer the entire bash code but only the first word of it.</em>
1:
$ <kbd>bash -vc <mark>"echo \"1: $1, 2: $2, 4: $4\""</mark> -- \</kbd><em>Even if we fix the quoting ambiguity, the $1, $2 and $4 are now evaluated by the shell we're typing this command into,</em>
<kbd>'New First Argument' Second Third 'Fourth Argument'</kbd><em>not the shell we pass the arguments to.</em>
echo "1: , 2: , 4: "<em>Since $1, $2 and $4 are likely empty in your interactive shell, they will expand empty and disappear from the -c argument.</em>
1: , 2: , 4:
</pre>
<p>We could go as far as to fix all of the issues inside the double quotes by backslash-escaping all of the special characters, including the double quotes and dollar signs. This would fix the issue, but it makes the shell code look extremely convoluted and hard to read. Maintaining shell code that has been escaped in a special way like this is a nightmare and begs for accidental mistakes that are hard to spot:</p>
<pre lang="bash" class="good">
$ <kbd>bash -vc "echo \"1: \$1, 2: \$2, 4: \$4\"" -- \</kbd>
<kbd>'New First Argument' Second Third 'Fourth Argument'</kbd>
echo "1: $1, 2: $2, 4: $4"
1: New First Argument, 2: Second, 4: Fourth Argument
</pre>
<h2>Special Parameters</h2>
<p>Understanding positional parameters makes understanding special parameters much easier: they are very similar. Special parameters are parameters whose name is a single symbolic character, they are used to request certain state information from the bash shell. Here are the different kinds of special parameters and the information they hold:</p>
<table>
<tr>
<th>Parameter</th>
<th>Example</th>
<th>Description</th>
</tr>
<tr>
<th><code class="syntax"><strong>"$</strong><var>*</var><strong>"</strong></code></th>
<td><kbd>echo "Arguments: $<mark>*</mark>"</kbd></td>
<td>
Expands a <strong><em>single</em> string</strong>, joining all positional parameters into one, separated by the first character in <var>IFS</var> (by default, a space).<br>
<strong>Note:</strong> You should never use this parameter unless you explicitly intend to join all the parameters. You almost always want to use <code>@</code> instead.
</td>
</tr>
<tr>
<th><code class="syntax"><strong>"$</strong><var>@</var><strong>"</strong></code></th>
<td><kbd>rm "$<mark>@</mark>"</kbd></td>
<td>
Expands the positional parameters as a list of separate arguments.
</td>
</tr>
<tr>
<th><code class="syntax"><strong>"$</strong><var>#</var><strong>"</strong></code></th>
<td><kbd>echo "Count: $<mark>#</mark>"</kbd></td>
<td>
Expands into a number indicating the amount of positional parameters that are available.
</td>
</tr>
<tr>
<th><code class="syntax"><strong>"$</strong><var>?</var><strong>"</strong></code></th>
<td><kbd>(( $? == 0 )) || echo "Error: $<mark>?</mark>"</kbd></td>
<td>
Expands the exit code of the last (synchronous) command that just finished.<br>
An exit code of 0 indicates the command succeeded, any other number indicates why the command failed.
</td>
</tr>
<tr>
<th><code class="syntax"><strong>"$</strong><var>-</var><strong>"</strong></code></th>
<td><kbd>[[ $<mark>-</mark> = *i* ]]</kbd></td>
<td>
Expands to the set of option flags that are currently active in the shell.<br>
Option flags configure the behaviour of the shell, the example tests for the presence of the <code>i</code> flag, indicating the shell is interactive (has a prompt) and is not running a script.
</td>
</tr>
<tr>
<th><code class="syntax"><strong>"$</strong><var>$</var><strong>"</strong></code></th>
<td><kbd>echo "$<mark>$</mark>" > /var/run/myscript.pid</kbd></td>
<td>
Expands a number that's the unique process identifier for the shell process (that's parsing the code).
</td>
</tr>
<tr>
<th><code class="syntax"><strong>"$</strong><var>!</var><strong>"</strong></code></th>
<td><kbd>kill "$<mark>!</mark>"</kbd></td>
<td>
Expands a number that's the unique process identifier of the last process that was started in the background (asynchronously).<br>
The example signals the background process that it's time to terminate.
</td>
</tr>
<tr>
<th><code class="syntax"><strong>"$</strong><var>_</var><strong>"</strong></code></th>
<td><kbd>mkdir -p ~/workspace/projects/myscripts && cd "$<mark>_</mark>"</kbd></td>
<td>
Expands to the last argument of the previous command.
</td>
</tr>
</table>
<p>Just like positional parameters, special parameters are read-only: you can only use them to expand information, not store information.</p>
<h2>Shell Internal Variables</h2>
<p>You already know what shell variables are. Were you aware that the bash shell also creates a few variables for you? These variables are used for various tasks and are handy for looking up certain state information from the shell or changing certain shell behaviours.</p>
<p>Internal shell variables are shell variables with ALL-UPPERCASE names. The same holds true for nearly all environment variables. It is important to make sure that when we start creating our own shell variables, we don't accidentally use the name of a shell variable we didn't know about, this mistake will result in all kinds of dangerous and unexpected behaviour. Thankfully, shell variable names are case-sensitive, so the general rule is that <strong>you should make all of your own
shell variables lower-case</strong> to avoid ever accidentally overriding a shell-internal variable of the same name. If you create an environment variable, give it an ALL-UPPERCASE name.</p>
<h2>Arrays</h2>
<h2 id="parameter_ex">Exercises!</h2>
<h4>PARAM.1. Start a new bash shell that outputs its <em>first</em> argument and pass <kbd>Hello World!</kbd> in as an argument to it.</h4>
<pre class="exercise"><samp><kbd>bash -c 'echo "$1"' -- 'Hello World!'</kbd></samp></pre>
<h4>PARAM.2. Start a bash shell that outputs the number of arguments passed in and pass in the arguments <kbd>1</kbd>, <kbd>2</kbd> and <kbd>The Third</kbd>.</h4>
<pre class="exercise"><samp><kbd>bash -c 'echo "$#"' -- 1 2 'The Third'</kbd></samp></pre>
<h4>PARAM.3. Start a bash shell that shifts a positional parameter away and then outputs the first. Pass in the arguments <kbd>1</kbd>, <kbd>2</kbd> and <kbd>The Third</kbd>.</h4>
<pre class="exercise"><samp><kbd>bash -c 'shift; echo "$1"' -- 1 2 'The Third'</kbd></samp></pre>
<h4>PARAM.4. Start a bash shell that outputs the last argument passed in and pass in the arguments <kbd>1</kbd>, <kbd>2</kbd> and <kbd>The Third</kbd>.</h4>
<pre class="exercise"><samp><kbd>bash -c 'echo "${@: -1}"' -- 1 2 'The Third'</kbd></samp></pre>
</section>
<aside class="nav">
<h1><a href="02.commands.html">Back track: Commands And Arguments</a></h1>
<h2><a href="04.conditionals.html">Next up: Tests And Conditionals</a></h2>
</aside>