summaryrefslogtreecommitdiff
path: root/how_to_create_templates_with_shell_cat_and_envsubst.html
blob: 8eb730d16af176912841a0775396bf3942d3e2d1 (plain)
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
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="aki">
<meta name="tags" content="tutorial, template, templating, cat, shell, envsubst">
<link rel="icon" type="image/png" href="favicon.png">
<link rel="stylesheet" type="text/css" href="style.css">

<title>How to Create Templates With Shell, cat and envsubst</title>

<nav><p><a href="https://ignore.pl">ignore.pl</a></p></nav>

<article>
<h1>How to Create Templates With Shell, cat and envsubst</h1>
<p class="subtitle">Published on 2020-07-14 20:26:00+02:00
<p>Now something trivial and fun. Creating templates of documents or configurations in shell. There are two reasons why
I considered doing that instead of using some more verbose utilities. First off, availability - I have POSIX compliant
shell pretty much everywhere I go. It's a big part of my usual environment and because of that I'm used to it. That's
the second reason - frequency I use it with. It's good to mention that it's one of more verbose utilities out there.
<p>Let's start with plain shell. I actually use this method to serve the blog posts via fcgiwrap. I use it with a single
file, but it can accept external files. However those files won't have their content expanded in any way. If you keep it
in a one script file then it's possible. Basically, I'm talking about <code>cat</code>:</p>
<pre>
#!/bin/sh
cat /dev/fd/3 $@ /dev/fd/4 3&lt;&lt;BEFORE 4&lt;&lt;AFTER
&lt;!doctype html&gt;
&lt;html lang="en"&gt;
BEFORE
&lt;script src=""&gt;&lt;/script&gt;
AFTER
</pre>
<img src="how_to_create_templates_with_shell_cat_and_envsubst-1.png" alt="actual cat">
<p>What you see here is a combination of
<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04">heredoc</a> and plain
redirection. It's done like this to avoid calling <code>cat</code> more than once. The script simply concatenates BEFORE
heredoc with any arguments passed to the script, and finally with AFTER heredoc. The output is similar to what you see
in source of this page. The template is trimmed for the sake of the example.
<p>Now, now. Heredoc can easily expand variables but if we were to <code>cat before.inl $@ after.inl</code> it wouldn't
work. For that I use
<code><a href="https://www.gnu.org/software/gettext/manual/html_node/envsubst-Invocation.html">envsubst</a></code>.
Consider file called <i>template.conf</i>:
<pre>
server {
	listen 80;
	server_name $DOMAIN$ALIASES;
	root /srv/http/$DOMAIN/public;
}
</pre>
<p>It could be wrapped in cat and heredoc, but for me it's not desired. Let's say I want my configuration templates to
be exactly that not executable scripts. There are three different solutions that I've heard: <code>eval</code>,
<code>sed</code> and <code>envsubst</code>. <code>Eval</code> can lead to dangerous situations and minor problems with
whitespace. <code>Sed</code> is what I believe is the most typical and straight-forward solution. As for the last one,
it goes like this:</p>
<pre>
$ export DOMAIN=example.tld
$ export ALIASES=' www.example.tld'
$ envsubst '$DOMAIN$ALIASES' &lt;template.conf
server {
	listen 80;
	server_name example.tld www.example.tld;
	root /srv/http/example.tld/public;
}
</pre>
<p>First, you set the variables (if they are not yet there). Then call the <code>envsubst</code> with a single variable
and redirect the template file into it. This single variable is called <em>SHELL-FORMAT</em>. It restricts the variables
that will be substituted in the input. It's completely optional, but if you have some <code>$server_name</code> you
don't want to substitute, then it's kind of useful. It actually doesn't have any format, you just need to reference the
variables in it: <code>'$DOMAIN$ALIASES'</code>, <code>'$DOMAIN $ALIASES'</code>, and <code>'$DOMAIN,$ALIASES'</code>
all work the same.
</article>
<script src="https://stats.ignore.pl/track.js"></script>