1 Bash:Lesser Known Bits
2 ======================
3 :author: Aaron Ball
4 :email: nullspoon@iohq.net
5
6 == {doctitle}
7
8 I won't lie, bash is my shell of choice (as if that's not obvious). Sure, the
9 ability to handle arrow keys, a command history, colors, and escape sequences
10 for terminal formatting are all great pieces, but most other shells can do
11 those things as well. What really makes bash stand out? There's a pretty good
12 list of things that are lesser known but are super useful, albeit not always
13 often though. All of these are well documented in the bash man page, but that
14 one is not exactly easy to find stuff in unless you know what you're looking
15 for. Running it through the wc command, the bash man page apparently has 41,452
16 words. All that aside though, this is a list of some lesser known things I use
17 occasionally (about once a week-ish) from our friend bash.
18
19
20 [[one-liner-loops]]
21 == One-liner Loops
22
23 This is one that is supported by most if not all of the other shells out there,
24 but it is still super useful and I don't see it used often. A one-liner loop is
25 effectively a very short (one line in fact) script used to perform a small
26 number of operations (it gets confusing if you do too many) in bulk. A good
27 example here is with a server environment of any size greater than I'd say two.
28 I frequently need to check lots of servers for something, be it the existence
29 of a file, the status of a file in comparison with a local copy (diffs), bulk
30 modifying remote files using sed, etc.
31
32 Recently though, I needed to verify the installed version of sudo
33 specifically on a list of about 50 servers. I sent the list of servers
34 to a text file, one server per line, and did the following and had my
35 answer within about 30 seconds (it takes a few hundred milliseconds for
36 ssh connections to establish on our atrociou...er...awesome network).
37
38 ----
39 for i in $(cat ./servers.list); do echo $i; ssh user@$i 'sudo -V | grep "I/O plugin version"'; done
40 ----
41
42 Presto! A big list of sudo versions across the entire environment.
43
44
45 [[process-substitution]]
46 == Process Substitution
47
48 This one is really great. Some commands require one or more file paths
49 to do what they need to do. A good example is diff. The diff command
50 requires two file path parameters: file a and file b. What if you want
51 to diff the outputs of two remote files though? Using process
52 substitution, we can cat out a remote file using the typical command,
53 +ssh user@server 'cat /etc/something'+, and have the output
54 go to a local temp file for the life of the command calling it so we
55 have something to work on. For example...
56
57 ----
58 diff /etc/something <(ssh user@server 'cat /etc/something')
59 ----
60
61 What we have here is a diff of the local /etc/something file and the remote
62 /etc/something. The ssh connection string is encapsulated in a +<()+. This is
63 the process substitution. This doesn't just work with remote files though. Say
64 for instance you wanted to diff the contents of a directory on a local system
65 and a remote system. Here's how you'd do that.
66
67 ... or comparing the output of two remote commands...
68
69 ----
70 diff <(ls -1 /var/log/) <(ssh user@server 'ls -1 /var/log/')
71 ----
72
73 Here we used process substitution to write the output of +ls -l /var/log+ to a
74 temp file, then write the output of the same command run on another system over
75 ssh to yet another temp file, then we use diff as usual to show us what is
76 different. If you really wanted to get crazy, you could throw this into a bash
77 one-liner loop and run the diff on multiple systems.
78
79
80 [[brace-expansion]]
81 == Brace Expansion
82
83 Brace expansion is really neat and I think super handy. This is the one I don't
84 have a lot of use for though. It gets used about once every few scripts or
85 about once or twice a month. Brace expansion is effectively on-the-fly array
86 loops for commands. For a simple example, say you wanted to create three
87 directories: dev, test, and prod. To create these without brace expansion,
88 you'd have to run _mkdir_ three times. With brace expansion, you can do this
89
90 ----
91 mkdir {dev,test,prod}
92 ----
93
94 That's cool, but what's REALLY cool is that you can use this with nested
95 directories. Say for isntance we are creating a small (and poorly designed) dev
96 environment. Inside of each we want the directories bin, etc, lib, var (we're
97 just making 'em up now). Here's how you'd do that in one command
98
99 ----
100 mkdir {dev,test,prod}/{bin,etc,lib,var}
101 ----
102
103 That is the equivelant of <syntaxhighlight lang="bash"> mkdir dev/bin
104 mkdir dev/etc mkdir dev/lib mkdir dev/var mkdir test/bin mkdir test/etc
105 mkdir test/lib mkdir test/var mkdir prod/bin mkdir prod/etc mkdir
106 prod/lib mkdir prod/var </syntaxhighlight>
107
108 Another application for this is if you want to cat out a big list of
109 specific files without catting out the entire directory (I did this one
110 earlier this morning actually). Say you have 20 files called
111 *list.<num>* (0-19) and you want to cat out numbers 1-9. Now, there are
112 a lot of ways to do this of course, but this is how you can do it with
113 brace expansion.
114
115 ----
116 cat list.{1,2,3,4,5,6,7,8,9}
117 ----
118
119 ...or even shorter...
120
121 ----
122 cat list.{1..9}
123 ----
124
125 Those are the equivelant of
126
127 ----
128 cat list.1 list.2 list.3 list.4 list.5 list.6 list.7 list.8 list.9
129 ----
130
131 How's that for time saving.
132
133
134 Category:Bash
135 Category:Shells
136 Category:Linux
137
138
139 // vim: set syntax=asciidoc:
|