-
Notifications
You must be signed in to change notification settings - Fork 258
/
expansions.html
1358 lines (1156 loc) · 98.5 KB
/
expansions.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
id: variables
layout: chapter
chapter: 3
title: Variables and Expansions
subtitle: How do I store and work with data?
status: alpha
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>What is expansion?</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 understand command arguments and how to pass information to our command in order to make them do the things we need done.</p>
<p>And with all of this knowledge, we begin to taste the power of what can be done with the shell. It's almost like we're communicating directly with our system in a brand new language where commands are tasks and arguments are the specific instructions on how those tasks should be performed.</p>
<p>One of the main limitations at this point is the fact that passing information to commands as arguments so explicitly is very limiting. To have to spell out every single file name on which to operate and every bit of data that should be shown on the screen or handled by a program means that we can only write programs when we have a perfect picture of what exactly needs to be done. What we need is a way of making our commands more dynamic, turning them into a sort of template for how
to perform actions that we can re-use time and time again.</p>
<p>Say we want to delete the files in our <code>Downloads</code> directory. With the knowledge we've gathered so far, we can look at what files are there and delete them:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>cd ~/Downloads</kbd>
<span class="prompt">$ </span><kbd>ls</kbd>
05 Between Angels and Insects.ogg
07 Wake Up.ogg
<span class="prompt">$ </span><kbd>rm -v '05 Between Angels and Insects.ogg' '07 Wake Up.ogg'</kbd>
removed '05 Between Angels and Insects.ogg'
removed '07 Wake Up.ogg'
<span class="prompt">$ </span><kbd>ls</kbd>
<span class="prompt">$ </span>
</pre>
<p>Brilliant. Our <code>ls</code> command gives no more output, that means our directory is empty.<br>
But wouldn't it be nice if we didn't have to be quite so explicit about everything? After all, our <em>intention</em> was to empty our <code>Downloads</code> directory. And to do so, we had to manually go there, find out which files were present, and issue an <code>rm</code> command enumerating each filename. Let's improve on this workflow and make our code a little more <em>dynamic</em>. What we want to achieve is a sort of template for doing a job that we can use again and
again. A job description that describes a way of executing our intent regardless of the specific situation we are currently in.</p>
<p>To do this, we need to remove all of the situational specifics from our code. In the example above, the situational specifics are the exact names of the files which we are trying to delete. Emptying our downloads won't mean deleting these two files every time we do it. What we want to do, is to delete <em>every file in the <code>Downloads</code> directory</em>, regardless of what their actual names are. The example above only worked because we had an intermediary step where
we, a human, had to look at the names of the files in the directory and re-write them as arguments into an <code>rm</code> command. How can we automate this process?</p>
<h2>Pathname Expansion</h2>
<p>The answer comes in the first of many forms of expansion bash provides us with. Welcome to <dfn>Pathname Expansion</dfn>:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>cd ~/Downloads</kbd>
<span class="prompt">$ </span><kbd>rm -v <mark>*</mark></kbd>
removed '05 Between Angels and Insects.ogg'
removed '07 Wake Up.ogg'
<span class="prompt">$ </span><kbd>ls</kbd>
<span class="prompt">$ </span>
</pre>
<p>What happened to the filenames we wanted to delete? We've replaced them with a pattern that tells bash to <em>expand the pathnames for us</em>. <dfn>Expansion</dfn> is the practice of replacing a part of our command code with a situationally specific piece of code. In this case, we want to replace <code>*</code> with the pathname of every single file in our downloads directory. Replacing patterns with pathnames is therefore known as
pathname expansion.</p>
<p>In our example above, bash notices that you have put a pathname pattern on the command line in the place where it would expect to see arguments. It has then taken this pathname pattern, and went looking on the file system for every pathname that it could find which matches this pattern of ours. It so happens, that the pattern <code>*</code> matches the name of every single file in the current directory. As a result, bash replaces the pattern in our command line with the pathname of every
single file in the current directory. We don't have to do the work ourselves anymore! Once bash replaces our <code>*</code> with <code>'05 Between Angels and Insects.ogg' '07 Wake Up.ogg'</code>, bash proceeds to invoke the <code>rm</code> command with the full set of arguments: <code>-v '05 Between Angels and Insects.ogg' '07 Wake Up.ogg'</code>. As a result, our downloads directory is emptied as intended. Brilliant.</p>
<aside>
<p>It is important to understand that while we see an apparent <code>*</code> argument in our code to <code>rm</code>, we are <em>not actually passing <code>*</code> to <code>rm</code></em>. In fact, the <code>rm</code> command will never even see our pathname expansion pattern. The pattern is evaluated and expanded by bash well before <code>rm</code> is even started. As far as <code>rm</code> knows, it simply receives a <code>-v</code> argument followed by the exact and full name of
every single file in the directory. Expansion is always performed by <em>bash</em> itself, and always <em>before</em> actually running the command!</p>
</aside>
<p>Bash can perform all sorts of pathname expansions for us. To perform a pathname expansion, we simply write a syntactical <dfn>glob pattern</dfn> in the place where we want to expand pathnames. A glob is the name of the type of pattern supported by the bash shell. Here are the various basic glob patterns supported by the bash shell:</p>
<table>
<tr>
<th>Glob</th>
<th>Meaning</th>
</tr>
<tr>
<th><code class="syntax"><strong>*</strong></code></th>
<td>A star or asterix matches any kind of text, even no text at all.</td>
</tr>
<tr>
<th><code class="syntax"><strong>?</strong></code></th>
<td>A question mark matches any one single character.</td>
</tr>
<tr>
<th><code class="syntax"><strong>[</strong><var>characters</var><strong>]</strong></code></th>
<td>A set of characters within rectangular braces matches a single character, only if it's in the given set.</td>
</tr>
<tr>
<th><code class="syntax"><strong>[[:</strong><var>classname</var><strong>:]]</strong></code></th>
<td>When there is a set of colons directly inside the rectangular braces, you can specify the name of a class of characters instead of having to enumerate each character yourself.<br>
Bash knows about various kinds of character classes. For example, if you use the <code>[[:<var>alnum</var>:]]</code> pattern, bash will match it against a character only if it is alphanumeric. Supported character classes include:<br>
<var>alnum</var>, <var>alpha</var>, <var>ascii</var>, <var>blank</var>, <var>cntrl</var>, <var>digit</var>, <var>graph</var>, <var>lower</var>, <var>print</var>, <var>punct</var>, <var>space</var>, <var>upper</var>, <var>word</var>, <var>xdigit</var></td>
</tr>
</table>
<p>We can combine these glob patterns together to describe all sorts of pathname combinations. We can also combine it with literal characters to tell bash that part of the pattern should include exact text:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>ls</kbd><em>Without arguments, <code>ls</code> simply lists the full contents of a directory.</em>
myscript.txt
mybudget.xsl
hello.txt
05 Between Angels and Insects.ogg
07 Wake Up.ogg
<span class="prompt">$ </span><kbd>ls *</kbd><em>While the effect is the same, this command actually enumerates every single file</em>
myscript.txt<em>in the directory to the <code>ls</code> in its arguments!</em>
mybudget.xsl
hello.txt
05 Between Angels and Insects.ogg
07 Wake Up.ogg
<span class="prompt">$ </span><kbd>ls *.txt</kbd><em>When we include the literal string <code>.txt</code>, the only pathnames that still match the pattern</em>
myscript.txt<em>are those that start with any kind of text and end with the literal string <code>.txt</code>.</em>
hello.txt
<span class="prompt">$ </span><kbd>ls 0<mark class="green">?</mark>' '<mark class="blue">*</mark>.ogg</kbd><em>Here we're combining patterns, looking for any pathname start starts with a <code>0</code>,</em>
0<mark class="green">5</mark> <mark class="blue">Between Angels and Insects</mark>.ogg<em>followed by any single character, followed by a <strong>literal</strong> space, ending in <code>.ogg</code>.</em>
0<mark class="green">7</mark> <mark class="blue">Wake Up</mark>.ogg
<span class="prompt">$ </span><kbd>ls <mark>[0-9]</mark>*</kbd><em>In a character set, we can use <code>-</code> to indicate a range of characters. This will match</em>
<mark>0</mark>5 Between Angels and Insects.ogg<em>a pathname starting with one character between <code>0</code> and <code>9</code> followed by any other text.</em>
<mark>0</mark>7 Wake Up.ogg
<span class="prompt">$ </span><kbd>ls <mark>[[:digit:]][[:digit:]]</mark>*</kbd><em>Character classes are really nice because they speak for us: they tell us exactly</em>
<mark>05</mark> Between Angels and Insects.ogg<em>what our intent here is. We want any pathname that start with two digits.</em>
<mark>07</mark> Wake Up.ogg
<span class="prompt">$ </span><kbd>ls [[:digit:]][[:digit:]]</kbd><em>Your pattern needs to be complete! None of our filenames is only just two digits.</em>
<span class="prompt">$ </span>
</pre>
<p>It is also important to understand that these globs will never jump into subdirectories. They only match against file names in their own directory. If we want a glob to go looking at the pathnames in a different directory, we need to explicitly tell it with a literal pathname:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>ls ~/Downloads/*.txt</kbd><em>Enumerate all pathnames in <code>~/Downloads</code> that end with <code>.txt</code>.</em>
/Users/lhunath/Downloads/myscript.txt
/Users/lhunath/Downloads/hello.txt
<span class="prompt">$ </span><kbd>ls ~/*/hello.txt</kbd><em>Globs can even search through many directories! Here bash will search</em>
/Users/lhunath/Documents/hello.txt<em>through <strong>all directories</strong> in our home directory for a file that's called <code>hello.txt</code>.</em>
/Users/lhunath/Downloads/hello.txt
</pre>
<p>Pathname expansion is an incredibly powerful tool to avoid having to specify exact pathnames in our arguments, or to go looking through our file system for the files we need.</p>
<p>Finally, bash has also built support in for more advanced glob patterns. These globs are called: <dfn>extended globs</dfn>. By default, support for them is disabled, but we can easily enable it in our current shell with the command:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>shopt -s extglob</kbd>
</pre>
<p>Once extended globs are enabled, the above table of glob pattern operators is extended with the following additional operators:</p>
<table>
<tr>
<th>Extended Glob</th>
<th>Meaning</th>
</tr>
<tr>
<th><code class="syntax"><strong>+(</strong><var>pattern</var>[ <strong>|</strong> <var>pattern</var> ... ]<strong>)</strong></code></th>
<td>Matches when any of the patterns in the list appears, once or many times over. Reads: <q>at least one of ...</q>.</td>
</tr>
<tr>
<th><code class="syntax"><strong>*(</strong><var>pattern</var>[ <strong>|</strong> <var>pattern</var> ... ]<strong>)</strong></code></th>
<td>Matches when any of the patterns in the list appears, once, <em>not at all</em>, or many times over. Reads: <q>however many of ...</q>.</td>
</tr>
<tr>
<th><code class="syntax"><strong>?(</strong><var>pattern</var>[ <strong>|</strong> <var>pattern</var> ... ]<strong>)</strong></code></th>
<td>Matches when any of the patterns in the list appears, once or not at all. Reads: <q>maybe one of ...</q>.</td>
</tr>
<tr>
<th><code class="syntax"><strong>@(</strong><var>pattern</var>[ <strong>|</strong> <var>pattern</var> ... ]<strong>)</strong></code></th>
<td>Matches when any of the patterns in the list appears just once. Reads: <q>one of ...</q>.</td>
</tr>
<tr>
<th><code class="syntax"><strong>!(</strong><var>pattern</var>[ <strong>|</strong> <var>pattern</var> ... ]<strong>)</strong></code></th>
<td>Matches only when none of the patterns in the list appear. Reads: <q>none of ...</q>.</td>
</tr>
</table>
<p>These operators are at first a little more confusing to understand, but they are a great way of adding logic to patterns:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>ls <mark>+([[:digit:]])</mark>' '*.ogg</kbd><em>Filenames that start with one or more digits.</em>
<mark>05</mark> Between Angels and Insects.ogg
<mark>07</mark> Wake Up.ogg
<span class="prompt">$ </span><kbd>ls *.jp<mark>?(e)</mark>g</kbd><em>Filenames that end either in <code>.jpg</code> or <code>.jpeg</code>.</em>
img_88751.jp<mark></mark>g
igpd_45qr.jp<mark>e</mark>g
<span class="prompt">$ </span><kbd>ls *.<mark>@(jpg|jpeg)</mark></kbd><em>Same thing, perhaps written more clearly!</em>
img_88751.<mark>jpg</mark>
igpd_45qr.<mark>jpeg</mark>
<span class="prompt">$ </span><kbd>ls !(my*).txt</kbd><em>All the <code>.txt</code> files that do <strong>not</strong> begin with <code>my</code>.</em>
hello.txt
<span class="prompt">$ </span><kbd>ls !(my)*.txt</kbd><em>Can you guess why this one matches <code>myscript.txt</code>?</em>
myscript.txt
hello.txt
</pre>
<p>Extended glob patterns can be extremely useful at times, but they can also be confusing and misleading. Let's focus on the last example: why does <code>!(my)*.txt</code> expand the pathname <code>myscript.txt</code>? Isn't <code>!(my)</code> supposed to only match when the pathname does <em>not</em> have a <code>my</code> in this position? You are correct, it is! And yet, bash expands a pathname that begins with <code>my</code>!</p>
<p>The answer here is that bash will happily match this part of the pattern against the <code>m</code> at the beginning (which is not the same as <code>my</code>) or even empty space at the start of the filename. This means that in order for the pathname to still be eligible for expansion, the <em>rest of our pattern</em> needs to match against the remainder of
our pathname. And it so happens that we have a <code>*</code> glob right after the <code>!(my)</code> glob which will happily match the entirety of the filename. In this situation, the <code>!(my)</code> part matches against
the <code>m</code> character in the beginning of the name, the <code>*</code> matches against the <code>yscript</code> part, and the <code>.txt</code> suffix of the pattern matches against the trailing <code>.txt</code> of our pathname. The pattern matches the name, so the name is expanded! When we include the <code>*</code> inside the <code>!()</code> pattern, this no longer works and the match fails against this pathname:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>ls <mark class="green">!(my)</mark><mark class="blue">*</mark>.txt</kbd>
<mark class="green">m</mark><mark class="blue">yscript</mark>.txt
<mark class="green">hello</mark>.txt
<span class="prompt">$ </span><kbd>ls <mark>!(my*)</mark>.txt</kbd>
<mark>hello</mark>.txt
</pre>
<h2>Tilde Expansion</h2>
<p>There is a different kind of expansion which we have been silently using in this guide without explicitly explaining it. It's called the <dfn>Tilde Expansion</dfn> and it involves replacing a tilde (<code>~</code>) in a pathname with the path to the current user's home directory:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>echo 'I live in: ' ~</kbd><em>Note that expansions must not be quoted or they will become <strong>literal</strong>!</em>
I live in: /Users/lhunath
</pre>
<p>Tilde expansion is slightly special in bash, compared to pathname expansion, since it happens especially early in the parser phase. This is merely a detail, but it is important to note that tilde expansion is different from pathname expansion. We are not performing a search and trying to match filenames against glob patterns. We are simply replacing a tilde with an explicit pathname.</p>
<p>In addition to simple tildes, we can also expand the home directory of another user by putting the user's name right after the tilde:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>echo 'My boss lives in: ' ~root</kbd>
My boss lives in: /var/root
</pre>
<h2>Command Substitution</h2>
<p>We now have a pretty good idea of what <dfn>expansion</dfn> means: we replace a syntactical token in our command by the situationally-specific equivalent value of that token. Thus far, we've only expanded pathnames, either as the result of a pathname expansion pattern or a tilde expansion operation.</p>
<p>But expansion can be used for so much more. We can use expansion to expand almost any kind of data into our command's arguments. <dfn>Command Substitution</dfn> is an extremely popular method of expanding data into command arguments. With <dfn>Command Substitution</dfn>, we effectively write a command within a command, and we ask bash to expand the inner command into its <em>output</em> and use that output as argument data for the main command:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>echo 'Hello world.' > hello.txt</kbd>
<span class="prompt">$ </span><kbd>cat hello.txt</kbd>
Hello world.
<span class="prompt">$ </span><kbd>echo "The file <hello.txt> contains: <mark>$(cat hello.txt)</mark>"</kbd>
The file <hello.txt> contains: <mark>Hello world.</mark>
</pre>
<p>What have we done here?<br>
We start out pretty simple: we create a file called <code>hello.txt</code> and put the string <code>Hello world.</code> into it. We then use the <code>cat</code> command to output the contents of the file. We can see the file contains the string we saved into it.</p>
<p>But then things get interesting: what we want to do here is output a message to the user that explains in a nice sentence what the string in our file is. To do this, we want to make the file's contents a "part of"
the sentence that we <code>echo</code> out. However, while we write the code for this sentence, there is no telling what the contents of the file is, so how can we type out the correct sentence in our script? The answer is expansion: We know how to get the contents of a file using <code>cat</code>, so here we <em>expand</em> the output of the <code>cat</code> command <em>into</em> our <code>echo</code> sentence. Bash will first run <code>cat hello.txt</code>, take the output of this command
(which is our string <code>Hello world.</code>) and then expand our <dfn>Command Substitution</dfn> syntax (the <code>$(cat ...)</code> part) into that output. Only after this expansion, bash will try to run the <code>echo</code> command. And can you guess what the argument to the <code>echo</code> command has become after our <dfn>Command Substitution</dfn> has expanded in-place? The answer is:<br>
<code>echo "The file <hello.txt> contains: Hello world."</code></p>
<p>This is the very first kind of value expansion that we've learned about. Value expansions allow us to expand data into command arguments. They are extremely useful and you will use them all the time. Bash has a fairly consistent syntax with regards to value expansions: they all start with a <code>$</code> symbol.</p>
<p><dfn>Command Substitution</dfn> essentially expands the <em>value of a command that was executed in a subshell</em>. As such, the syntax is a combination of the value-expansion prefix <code>$</code> followed by the subshell to expand: <code>(...)</code>. A subshell is essentially a small new bash process that is used to run a command while the main bash shell waits for the result. We'll learn more about subshells in a future chapter. Suffice it to say that the syntax for
expansions in bash is very consistent and very deliberate, which certainly helps with learning it!</p>
<aside class="rule">
<p>Very observant readers might have noticed that this guide tends to use single quotes to quote its strings but in this latest example switched to double quotes for the sentence that contained the expansion syntax. This is intentional: all value expansions (ie. all syntax with a <code>$</code> prefix) can only expand inside quoted arguments if the argument was <em>double-quoted</em>. Single quotes will turn the dollar-syntax into literal characters, causing bash to output the
dollar rather than expand its value in-place! It is thus important to double-quote all our arguments that contain value expansions.</p>
<p>
<q>Value expansions (<code>$...</code>) must <strong>always</strong> be double-quoted.</q>
</p>
</aside>
<aside class="warn">
<p>Never leave a value expansion unquoted. If you do, bash will tear the value apart using word-splitting, delete all whitespace from it and perform hidden pathname expansion on all the words in it!</p>
</aside>
<p>As a closing note, I will make brief mention of the deprecated <code>`...`</code> syntax. Old-style bourne shells used this syntax for <dfn>Command Substitution</dfn> instead of the more modern <code>$(...)</code> syntax. In bash and all modern POSIX shells, both syntaxes are supported, but it is highly recommended that you <em>stop</em> using the backtick (<code>`</code>) syntax and change this syntax into the value-expansion equivalent whenever you see it used in the wild. Although they are
functionally equivalent, the backtick variant has some very important downsides:</p>
<ul>
<li>The backtick syntax looks a <em>lot</em> like quoting. This has caused widespread confusion among users. Even to the trained eye, it is sometimes hard to not forget that backtick expansions <em>still need double-quotes around them to be safe</em>, just like all other value expansions.</li>
<li>The backtick syntax is inconsistent with value expansions. The beauty of <code>$(...)</code> is that is clearly communicates that we are expanding a value into place here, just like all other dollar-style value expansions do. This clarity is absent with the backtick syntax.</li>
<li>The backtick syntax turns quoting and nesting into an exercise in absurdity. It requires a maze of backslash-escaping which makes it almost impossible to parse and nearly guarantees that you will make mistakes: <code>echo "`echo \"\`echo \\"hello\\"\`\"`"</code> vs. <code>echo "$(echo "$(echo "hello")")"</code></li>
</ul>
</section>
<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">
<span class="prompt">$ </span><kbd>name=lhunath</kbd><em>Assign the value <code>lhunath</code> to the variable <code>name</code></em>
<span class="prompt">$ </span><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 of the parameter's value 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">
<span class="prompt">$ </span><kbd>name <mark>=</mark> <mark>lhunath</mark></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">
<span class="prompt">$ </span><kbd>name=lhunath</kbd>
<span class="prompt">$ </span><kbd>item=' 4. Milk'</kbd><em>Use quotes to make the spaces literal.</em>
</pre>
<p>We can even combine this assignment syntax with other value expansions:</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>contents="$(cat hello.txt)"</kbd>
</pre>
<p>Here, we perform a <dfn>Command Substitution</dfn>, expanding the contents of the <code>hello.txt</code> file into our assignment syntax, which subsequently results in that contents getting assigned to the <var>contents</var> variable.</p>
<h3>Parameter 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. <dfn>Parameter Expansion</dfn> 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>
<aside class="rule">
<p>You might notice a trend here, but it bears consistent repeating:<br>
<q>Parameter expansions (and all other value expansions) should <strong>always</strong> be double-quoted.</q></p>
</aside>
<p>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">
<span class="prompt">$ </span><kbd>name=Britta time=23.73</kbd><em>We want to expand <code>time</code> and add an <code>s</code> for seconds</em>
<span class="prompt">$ </span><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 .
<span class="prompt">$ </span><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: parameter 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. Remember that this operator only changes the value that is expanded; it does not change the original value that's sitting in your variable.</p>
<pre lang="bash">
<span class="prompt">$ </span><kbd>name=Britta time=23.73</kbd>
<span class="prompt">$ </span><kbd>echo "$name's current record is ${time%.*} seconds and ${time#*.} hundredths."</kbd>
Britta's current record is 23 seconds and 73 hundredths.
<span class="prompt">$ </span><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; the operator only affects the value that gets expanded into place. You'll also notice that we can use glob patterns here, just like we did during pathname expansion, to match against the values in our parameter.</p>
<p>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='https://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>https:/</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>https://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>https://guide.bash.academy<mark>/variables.html</mark>
↓
https://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>https:<mark>//guide.bash.academy/variables.html</mark>
↓
https:</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>https://guide<mark>.</mark>bash.academy/variables.html
↓
https://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>https://guide<mark>.</mark>bash<mark>.</mark>academy/variables<mark>.</mark>html
↓
https://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>http:</mark>}"</kbd></td>
<td>
<pre><mark>https:</mark>//guide.bash.academy/variables.html
↓
http://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>https://guide.bash.academy/variables<mark>.html</mark>
↓
https://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>https://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>8</mark>}"</kbd></td>
<td>
<pre>https://<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. You can even count <var>start</var> from the end rather than the beginning by using a (space followed by a) negative value.</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
↓
HTTps://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 lang="bash" class="exercise"><samp><kbd>greeting=hello</kbd></samp></pre>
<h4>EXPAN.2. Show the contents of the variable <var>greeting</var>.</h4>
<pre lang="bash" 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 lang="bash" 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 lang="bash" 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 a period (<code>.</code>) at the end.</h4>
<pre lang="bash" 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 lang="bash" 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 lang="bash" 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 lang="bash" 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> │
╰────────────────────────────────╯
<span class="prompt">$ </span><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. Environment 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">
<span class="prompt">$ </span><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
echo "The Name Script"
echo "usage: names 'My Full Name'"; echo
first=${1%% *} last=${1##* } middle=${1#$first} middle=${middle%$last}
echo "Your first name is: $first"
echo "Your last name is: $last"
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">
<span class="prompt">$ </span><kbd>chmod +x names</kbd>
<span class="prompt">$ </span><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:
<span class="prompt">$ </span><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">
<span class="prompt">$ </span><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">
<span class="prompt">$ </span><kbd>set -- 'New First Argument' Second Third 'Fourth Argument'</kbd>
<span class="prompt">$ </span><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>
<span class="prompt">$ </span><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">
<span class="prompt">$ </span><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>
<aside class="rule">
<p>Note that our argument containing bash code is <strong><code>'single-quoted'</code></strong>:</p>
<q>Whenever we put <em>code</em> in a <em>string</em> (such as in the case of passing it in as an argument), the code <em>should</em> be single-quoted.</q>
<p>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.</p>
</aside>
<p>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>
<p>To illustrate this point, compare our good example from above:</p>
<pre lang="bash" class="good">
<span class="prompt">$ </span><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">
<span class="prompt">$ </span><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:
<span class="prompt">$ </span><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">
<span class="prompt">$ </span><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 number 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.