-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathshx
executable file
·561 lines (489 loc) · 23.3 KB
/
shx
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
#! /usr/bin/env bash
SHX_VERSION="1.0.0"
SHX_CACHE=true
_SHX_COMPILED_TEMPLATE_CACHE=("")
## @command shx
shx() {
declare -a __shx__mainCliCommands=("shx")
declare -a __shx__originalCliCommands=("$@")
local __shx__mainCliCommandDepth="1"
__shx__mainCliCommands+=("$1")
local __shx__mainCliCommands_command1="$1"
shift
case "$__shx__mainCliCommands_command1" in
"compile")
## @command shx compile
## @param $1 Template to compile (_string or path to file_)<br><br>
## @param $1 The first wo arguments may be `--out` `[variableName]`,
## @param $1 in which case the compiled result will _not_ be
## @param $1 printed and will instead be assigned to the provided variable.
## @param $3 Template to compile (_string or path to file_)<br>if `$1` and `$2` are `--out` `[variableName]` respectively
##
## Compiles a provided template to a result which can be stored and
## later evaluated via [`shx evaluate`](#shx-evaluate).
##
## This allows you to perform template parsing only once and
## evaluate templates without the computational penalty of parsing.
##
## > Note: alternatively you may want to consider [Template caching](#template-caching)
##
## @example Compile Template
## template="<h1><%%= $1 %%></h1>"
## compiledTemplate="$( shx compile "$template" )"
##
## # Later ...
##
## shx evaluate "$compiledTemplate" "My Title"
## # => "<h1>My Title</h1>"
##
## @example Store in Variable
## template="<h1><%%= $1 %%></h1>"
## shx compile "$template" compiledTemplate
## # ^--- the compilated template is stored in $compiledTemplate
##
## # Later ...
##
## shx evaluate "$compiledTemplate" "My Title"
## # => "<h1>My Title</h1>"
##
local __shx__outVariableName=''
[ "$1" = "--out" ] && { shift; __shx__outVariableName="$1"; shift; }
# Undocumented option, get the code for the template without evaluating it: --code
local __shx__providedTemplate="$1"; shift
[ -f "$__shx__providedTemplate" ] && __shx__providedTemplate="$(<"$__shx__providedTemplate")"
# Like most similar implementations across programming languages,
# the template render process builds up a script with lots of printf
# statements alongside the <% shell source %> code to run and
# then the result is created by evaluating the script.
#
# This is _not_ a side-effect-free/safe templating engine a la Liquid and friends
#
local __shx__outputScriptToEval=''
local __shx__stringBuilder=''
local __shx__stringBuilderComplete=false
local __shx__valueBlock=''
local __shx__valueBlockOpen=false
local __shx__codeBlockDefinition=''
local __shx__codeBlockDefinitionOpen=false
local __shx__heredocCount=0
local __shx__newLine=$'\n'
# We legit loop thru all the characters.
local __shx__cursor=0
while [ "$__shx__cursor" -lt "${#__shx__providedTemplate}" ]
do
if [ "${__shx__providedTemplate:$__shx__cursor:3}" = "<%=" ]
then
[ "$__shx__codeBlockDefinitionOpen" = true ] && { echo "shx [RenderError] <%= was started but there is a <% block already open with content: '$__shx__codeBlockDefinition'" >&2; return 1; }
[ "$__shx__valueBlockOpen" = true ] && { echo "shx [RenderError] <%= was started but there is another <%= already open with content: '$__shx__valueBlock'" >&2; return 1; }
__shx__valueBlockOpen=true
__shx__stringBuilderComplete=true
: "$(( __shx__cursor += 2 ))"
elif [ "${__shx__providedTemplate:$__shx__cursor:2}" = "<%" ]
then
[ "$__shx__codeBlockDefinitionOpen" = true ] && { echo "shx [RenderError] %> block was closed but there is another <% currently open with content: '$__shx__codeBlockDefinition'" >&2; return 1; }
[ "$__shx__valueBlockOpen" = true ] && { echo "shx [RenderError] %> block was closed but there is a <%= currently open with content: '$__shx__valueBlock'" >&2; return 1; }
__shx__codeBlockDefinitionOpen=true
__shx__stringBuilderComplete=true
: "$(( __shx__cursor++ ))"
elif [ "${__shx__providedTemplate:$__shx__cursor:3}" = "-%>" ]
then
if [ "$__shx__valueBlockOpen" = true ]
then
__shx__valueBlockOpen=false
__shx__valueBlock="${__shx__valueBlock# }"
__shx__outputScriptToEval+="${__shx__newLine}printf '%s' \"${__shx__valueBlock% }\"${__shx__newLine}"
__shx__valueBlock=''
elif [ "$__shx__codeBlockDefinitionOpen" = true ]
then
__shx__codeBlockDefinitionOpen=false
__shx__codeBlockDefinition="${__shx__codeBlockDefinition# }"
__shx__outputScriptToEval+="${__shx__newLine}${__shx__codeBlockDefinition% }${__shx__newLine}"
__shx__codeBlockDefinition=''
else
echo "shx [RenderError] unexpected %> encountered, no <% or <%= blocks are currently open" >&2
return 1
fi
: "$(( __shx__cursor += 3 ))"
elif [ "${__shx__providedTemplate:$__shx__cursor:2}" = "%>" ]
then
if [ "$__shx__valueBlockOpen" = true ]
then
__shx__valueBlockOpen=false
__shx__valueBlock="${__shx__valueBlock# }"
__shx__outputScriptToEval+="${__shx__newLine}printf '%s' \"${__shx__valueBlock% }\"${__shx__newLine}"
__shx__valueBlock=''
elif [ "$__shx__codeBlockDefinitionOpen" = true ]
then
__shx__codeBlockDefinitionOpen=false
__shx__codeBlockDefinition="${__shx__codeBlockDefinition# }"
__shx__outputScriptToEval+="${__shx__newLine}${__shx__codeBlockDefinition% }${__shx__newLine}"
__shx__codeBlockDefinition=''
else
echo "shx [RenderError] unexpected %> encountered, no <% or <%= blocks are currently open" >&2
return 1
fi
: "$(( __shx__cursor++ ))"
elif [ "$__shx__valueBlockOpen" = true ]
then
__shx__valueBlock+="${__shx__providedTemplate:$__shx__cursor:1}"
elif [ "$__shx__codeBlockDefinitionOpen" = true ]
then
__shx__codeBlockDefinition+="${__shx__providedTemplate:$__shx__cursor:1}"
else
__shx__stringBuilder+="${__shx__providedTemplate:$__shx__cursor:1}"
fi
if [ "$__shx__stringBuilderComplete" = true ]
then
__shx__stringBuilderComplete=false
if [ -n "$__shx__stringBuilder" ]
then
: "$(( __shx__heredocCount++ ))"
__shx__outputScriptToEval+="${__shx__newLine}IFS= read -r -d '' __SHX_HEREDOC_$__shx__heredocCount << 'SHX_PRINT_BLOCK'${__shx__newLine}"
__shx__outputScriptToEval+="$__shx__stringBuilder"
__shx__outputScriptToEval+="${__shx__newLine}SHX_PRINT_BLOCK"
__shx__outputScriptToEval+="${__shx__newLine}printf '%s' \"\${__SHX_HEREDOC_$__shx__heredocCount%$'\\n'}\""
__shx__outputScriptToEval+="${__shx__newLine}unset __SHX_HEREDOC_$__shx__heredocCount"
__shx__stringBuilder=''
fi
fi
: "$(( __shx__cursor++ ))"
done
if [ -n "$__shx__stringBuilder" ]
then
__shx__outputScriptToEval+="${__shx__newLine}IFS= read -r -d '' __SHX_HEREDOC_$__shx__heredocCount << 'SHX_PRINT_BLOCK'${__shx__newLine}"
__shx__outputScriptToEval+="$__shx__stringBuilder"
__shx__outputScriptToEval+="${__shx__newLine}SHX_PRINT_BLOCK"
__shx__outputScriptToEval+="${__shx__newLine}printf '%s' \"\${__SHX_HEREDOC_$__shx__heredocCount%$'\\\n'}\""
__shx__outputScriptToEval+="${__shx__newLine}unset __SHX_HEREDOC_$__shx__heredocCount"
__shx__stringBuilder=''
fi
[ "$__shx__codeBlockDefinitionOpen" = true ] && { echo "shx [RenderError] <% block was not closed: '$__shx__codeBlockDefinition'" >&2; return 1; }
[ "$__shx__valueBlockOpen" = true ] && { echo "shx [RenderError] <%= was not closed: '$__shx__valueBlock'" >&2; return 1; }
# local __shx__COMPILED_TEMPLATE="$( printf '%s' "$__shx__outputScriptToEval" )"
local __shx__COMPILED_TEMPLATE="$__shx__outputScriptToEval"
if [ "$__shx__printCodeOnly" = true ]
then
echo "$__shx__COMPILED_TEMPLATE"
return 0
fi
unset __shx__cursor
unset __shx__outputScriptToEval
unset __shx__stringBuilder
unset __shx__stringBuilderComplete
unset __shx__valueBlock
unset __shx__valueBlockOpen
unset __shx__codeBlockDefinition
unset __shx__codeBlockDefinitionOpen
unset __shx__heredocCount
unset __shx__printCodeOnly
unset __shx__newLine
if [ -n "$__shx__outVariableName" ]
then
printf -v "$__shx__outVariableName" '%s' "$__shx__COMPILED_TEMPLATE"
else
printf '%s' "$__shx__COMPILED_TEMPLATE"
fi
## @
;;
"evaluate")
## @command shx evaluate
## @param $1 Compiled template provided via [`shx compile`](#shx-compile)
##
## Evaluates a previously compiled template.
local __shx__COMPILED_TEMPLATE="$1"; shift
eval "$__shx__COMPILED_TEMPLATE"
## @
;;
"render")
## @command shx render
## @param $1 Template to compile (_string or path to file_)
## @param $@ Any number of arguments.<br>Arguments which will be available to the evaluated template,<br>e.g. `$1` or `$*` or `$@`
##
## Render the provided template and evaluate the result, printing the template result to `STDOUT`.
##
## @example Simple String
## template='<%% for arg in "$@"; do %%>Arg:<%%= $arg %%> <%% done %%>'
## shx render "$template" "Hello" "World!"
## # => "Arg: Hello Arg: World!"
##
# Undocumented option, get the code for the template without evaluating it: --code
local __shx__printCodeOnly=false
[ "$1" = "--code" ] && { __shx__printCodeOnly=true; shift; }
# Shift so that templates can properly read in provided "$1" "$@" etc to the `render` function
local __shx__originalTemplateArgument="$1"; shift
local __shx__providedTemplate="$__shx__originalTemplateArgument"
#
# Begin Cache Lookup
#
local __shx__cacheEncodedItem_indexOfCompiledTemplate=''
if [ -f "$__shx__providedTemplate" ] && [ "$SHX_CACHE" = true ]
then
# Build up the new cache lookup field (may have MTIME file changes)
declare -a __shx__cacheLookupIndex=()
# Loop Thru Every Item in the Cache, including it's Filename, Mtime,
# and index to compiled template in the cache
local __shx__cacheEncodedItem=''
while IFS="" read -r __shx__cacheEncodedItem
do
local __shx__cacheUpdatedEncodedItem=''
local __shx__cacheEncodedItem_filename="${__shx__cacheEncodedItem##*|}"
# Found the item
if [ "$__shx__cacheEncodedItem_filename" = "$__shx__providedTemplate" ]
then
# Get and check the mtime
local __shx__currentTemplateFileMtime="$( date +"%s" -r "$__shx__providedTemplate" 2>/dev/null || stat -x "$__shx__providedTemplate" | grep "Modify" )"
# MTIME
local __shx__cacheEncodedItem_mtime="${__shx__cacheEncodedItem#*>}"
__shx__cacheEncodedItem_mtime="${__shx__cacheEncodedItem_mtime%%|*}"
# Index
__shx__cacheEncodedItem_indexOfCompiledTemplate="${__shx__cacheEncodedItem%%*<}"
__shx__cacheEncodedItem_indexOfCompiledTemplate="${__shx__cacheEncodedItem_indexOfCompiledTemplate%>*}"
if [ "$__shx__currentTemplateFileMtime" = "$__shx__cacheEncodedItem_mtime" ]
then
# Equal! Just eval the previously compiled template
eval "${_SHX_COMPILED_TEMPLATE_CACHE[$__shx__cacheEncodedItem_indexOfCompiledTemplate]}" && return $?
else
# Present but not equal, note to update it via its index
# Update the item with the new MTIME
local __shx__cacheUpdatedEncodedItem="$__shx__cacheEncodedItem_indexOfCompiledTemplate>$__shx__currentTemplateFileMtime|$__shx__cacheEncodedItem_filename"
fi
fi
if [ -n "$__shx__cacheUpdatedEncodedItem" ]
then
__shx__cacheLookupIndex+=("$__shx__cacheUpdatedEncodedItem\n")
else
__shx__cacheLookupIndex+=("$__shx__cacheEncodedItem\n")
fi
done < <( printf "${_SHX_COMPILED_TEMPLATE_CACHE[0]}" )
# Update the cache index
_SHX_COMPILED_TEMPLATE_CACHE[0]="${__shx__cacheLookupIndex[*]}"
# If no template was found and eval'd and returned from the cache, grab a new one from the filesystem
__shx__providedTemplate="$(<"$__shx__providedTemplate")"
fi
#
# End Cache Lookup
#
# Like most similar implementations across programming languages,
# the template render process builds up a script with lots of printf
# statements alongside the <% shell source %> code to run and
# then the result is created by evaluating the script.
#
# This is _not_ a side-effect-free/safe templating engine a la Liquid and friends
#
local __shx__outputScriptToEval=''
local __shx__stringBuilder=''
local __shx__stringBuilderComplete=false
local __shx__valueBlock=''
local __shx__valueBlockOpen=false
local __shx__codeBlockDefinition=''
local __shx__codeBlockDefinitionOpen=false
local __shx__heredocCount=0
local __shx__newLine=$'\n'
# We legit loop thru all the characters.
local __shx__cursor=0
while [ "$__shx__cursor" -lt "${#__shx__providedTemplate}" ]
do
if [ "${__shx__providedTemplate:$__shx__cursor:3}" = "<%=" ]
then
[ "$__shx__codeBlockDefinitionOpen" = true ] && { echo "shx [RenderError] <%= was started but there is a <% block already open with content: '$__shx__codeBlockDefinition'" >&2; return 1; }
[ "$__shx__valueBlockOpen" = true ] && { echo "shx [RenderError] <%= was started but there is another <%= already open with content: '$__shx__valueBlock'" >&2; return 1; }
__shx__valueBlockOpen=true
__shx__stringBuilderComplete=true
: "$(( __shx__cursor += 2 ))"
elif [ "${__shx__providedTemplate:$__shx__cursor:2}" = "<%" ]
then
[ "$__shx__codeBlockDefinitionOpen" = true ] && { echo "shx [RenderError] %> block was closed but there is another <% currently open with content: '$__shx__codeBlockDefinition'" >&2; return 1; }
[ "$__shx__valueBlockOpen" = true ] && { echo "shx [RenderError] %> block was closed but there is a <%= currently open with content: '$__shx__valueBlock'" >&2; return 1; }
__shx__codeBlockDefinitionOpen=true
__shx__stringBuilderComplete=true
: "$(( __shx__cursor++ ))"
elif [ "${__shx__providedTemplate:$__shx__cursor:3}" = "-%>" ]
then
if [ "$__shx__valueBlockOpen" = true ]
then
__shx__valueBlockOpen=false
__shx__valueBlock="${__shx__valueBlock# }"
__shx__outputScriptToEval+="${__shx__newLine}printf '%s' \"${__shx__valueBlock% }\"${__shx__newLine}"
__shx__valueBlock=''
elif [ "$__shx__codeBlockDefinitionOpen" = true ]
then
__shx__codeBlockDefinitionOpen=false
__shx__codeBlockDefinition="${__shx__codeBlockDefinition# }"
__shx__outputScriptToEval+="${__shx__newLine}${__shx__codeBlockDefinition% }${__shx__newLine}"
__shx__codeBlockDefinition=''
else
echo "shx [RenderError] unexpected %> encountered, no <% or <%= blocks are currently open" >&2
return 1
fi
: "$(( __shx__cursor += 3 ))"
elif [ "${__shx__providedTemplate:$__shx__cursor:2}" = "%>" ]
then
if [ "$__shx__valueBlockOpen" = true ]
then
__shx__valueBlockOpen=false
__shx__valueBlock="${__shx__valueBlock# }"
__shx__outputScriptToEval+="${__shx__newLine}printf '%s' \"${__shx__valueBlock% }\"${__shx__newLine}"
__shx__valueBlock=''
elif [ "$__shx__codeBlockDefinitionOpen" = true ]
then
__shx__codeBlockDefinitionOpen=false
__shx__codeBlockDefinition="${__shx__codeBlockDefinition# }"
__shx__outputScriptToEval+="${__shx__newLine}${__shx__codeBlockDefinition% }${__shx__newLine}"
__shx__codeBlockDefinition=''
else
echo "shx [RenderError] unexpected %> encountered, no <% or <%= blocks are currently open" >&2
return 1
fi
: "$(( __shx__cursor++ ))"
elif [ "$__shx__valueBlockOpen" = true ]
then
__shx__valueBlock+="${__shx__providedTemplate:$__shx__cursor:1}"
elif [ "$__shx__codeBlockDefinitionOpen" = true ]
then
__shx__codeBlockDefinition+="${__shx__providedTemplate:$__shx__cursor:1}"
else
__shx__stringBuilder+="${__shx__providedTemplate:$__shx__cursor:1}"
fi
if [ "$__shx__stringBuilderComplete" = true ]
then
__shx__stringBuilderComplete=false
if [ -n "$__shx__stringBuilder" ]
then
: "$(( __shx__heredocCount++ ))"
__shx__outputScriptToEval+="${__shx__newLine}IFS= read -r -d '' __SHX_HEREDOC_$__shx__heredocCount << 'SHX_PRINT_BLOCK'${__shx__newLine}"
__shx__outputScriptToEval+="$__shx__stringBuilder"
__shx__outputScriptToEval+="${__shx__newLine}SHX_PRINT_BLOCK"
__shx__outputScriptToEval+="${__shx__newLine}printf '%s' \"\${__SHX_HEREDOC_$__shx__heredocCount%$'\\n'}\""
__shx__outputScriptToEval+="${__shx__newLine}unset __SHX_HEREDOC_$__shx__heredocCount"
__shx__stringBuilder=''
fi
fi
: "$(( __shx__cursor++ ))"
done
if [ -n "$__shx__stringBuilder" ]
then
__shx__outputScriptToEval+="${__shx__newLine}IFS= read -r -d '' __SHX_HEREDOC_$__shx__heredocCount << 'SHX_PRINT_BLOCK'${__shx__newLine}"
__shx__outputScriptToEval+="$__shx__stringBuilder"
__shx__outputScriptToEval+="${__shx__newLine}SHX_PRINT_BLOCK"
__shx__outputScriptToEval+="${__shx__newLine}printf '%s' \"\${__SHX_HEREDOC_$__shx__heredocCount%$'\\\n'}\""
__shx__outputScriptToEval+="${__shx__newLine}unset __SHX_HEREDOC_$__shx__heredocCount"
__shx__stringBuilder=''
fi
[ "$__shx__codeBlockDefinitionOpen" = true ] && { echo "shx [RenderError] <% block was not closed: '$__shx__codeBlockDefinition'" >&2; return 1; }
[ "$__shx__valueBlockOpen" = true ] && { echo "shx [RenderError] <%= was not closed: '$__shx__valueBlock'" >&2; return 1; }
# local __shx__COMPILED_TEMPLATE="$( printf '%s' "$__shx__outputScriptToEval" )"
local __shx__COMPILED_TEMPLATE="$__shx__outputScriptToEval"
if [ "$__shx__printCodeOnly" = true ]
then
echo "$__shx__COMPILED_TEMPLATE"
return 0
fi
if [ -f "$__shx__originalTemplateArgument" ] && [ "$SHX_CACHE" = true ]
then
if [ -n "$__shx__cacheEncodedItem_indexOfCompiledTemplate" ] # Existing item in the cache to update
then
_SHX_COMPILED_TEMPLATE_CACHE[$__shx__cacheEncodedItem_indexOfCompiledTemplate]="$__shx__COMPILED_TEMPLATE"
else
# Add a new item
local __shx__actualMtime="$( date +"%s" -r "$__shx__originalTemplateArgument" 2>/dev/null || stat -x "$__shx__originalTemplateArgument" | grep "Modify" )"
local __shx__itemIndexLine="${#_SHX_COMPILED_TEMPLATE_CACHE[@]}>$__shx__actualMtime|$__shx__originalTemplateArgument"
_SHX_COMPILED_TEMPLATE_CACHE[0]+="$__shx__itemIndexLine\n"
_SHX_COMPILED_TEMPLATE_CACHE+=("$__shx__COMPILED_TEMPLATE")
fi
fi
unset __shx__cursor
unset __shx__outputScriptToEval
unset __shx__stringBuilder
unset __shx__stringBuilderComplete
unset __shx__valueBlock
unset __shx__valueBlockOpen
unset __shx__codeBlockDefinition
unset __shx__codeBlockDefinitionOpen
unset __shx__heredocCount
unset __shx__printCodeOnly
unset __shx__newLine
unset __shx__originalTemplateArgument
unset __shx__providedTemplate
unset __shx__cacheEncodedItem_indexOfCompiledTemplate
unset __shx__cacheLookupIndex
unset __shx__cacheEncodedItem
unset __shx__cacheUpdatedEncodedItem
unset __shx__cacheEncodedItem_filename
unset __shx__currentTemplateFileMtime
unset __shx__cacheEncodedItem_mtime
unset __shx__cacheUpdatedEncodedItem
eval "$__shx__COMPILED_TEMPLATE"
## @
;;
"--version")
## @command shx --version
## Displays the current version of `shx.sh`
echo "shx version $SHX_VERSION"
## @
;;
*)
echo "shx version $SHX_VERSION"
echo '
Shell Script Templates
> https://shx.shellbox.sh
`shx` is a minimal BASH template renderer:
Example template:
<html>
<head>
<title><%= $title %></title>
</head>
<body>
<ul>
<% for item in "${items[@]}"; do %>
<li><%= $item %></li>
<% done %>
</ul>
</body>
</html>
To render a template, provide a file path to `shx render`:
$ shx render file.shx
# => <html> ...
You can optionally pass command-line arguments to `shx render`
Example template using command-line arguments:
<html>
<head>
<title><%= $1 %><% shift %></title>
</head>
<body>
<ul>
<% for item in "$@"; do %>
<li><%= $item %></li>
<% done %>
</ul>
</body>
</html>
Call `shx render` with arguments:
$ shx render file.shx "My Title" "Hello" "World"
# => <h1>My Title</h1>
# => ...
# => <li>Hello</li>
# => <li>World</li>
You can also provide a simple text string without a file:
$ shx render ''<h1><%= $1 %></h1>'' "Hello"
# => "<h1>Hello</h1>"
Note: If you run `shx` as a binary, only exported variables
will be available.
If you use `shx` as a library, all variables in the
current scope (including `local` variables) will
be available for use:
source shx.sh
myFunction() {
local answer=42
shx render "<h1>The answer is: <%= $answer %></h1>
}
myFunction
# => "<h1>The answer is 42</h1>
Syntax Highlighting:
Name shx templates using a .erb or .asp or similar file extension
for best syntax highlighting experience in your preferred text editor
-or- configure .shx to use one of these syntax highlighers.
'
;;
esac
}
[ "${BASH_SOURCE[0]}" = "$0" ] && "shx" "$@"