yaml_interpolate is a command-line tool for doing string interpolation inside a YAML file.
That tool exists, since YAML specification not allow string interploation, only alieses and anchors.
That tool is designed to process a single YAML file with both template and data for it. See possible alternatives (1, 2, 3, 4) for other cases / other languages.
Written in C++11 (not in python or ruby, for example), since I find it hard to express logic without strong typing :)
Usage:
./yaml_interpolate [OPTION...]
I/O options:
-i, --input PATH Input yaml file path (or 'stdin'). (required)
-o, --output PATH Output file name (or 'stdout') (default: stdout)
Parsing settings options:
--formats LIST Formats:
moustache: {{ variable.path }}
double_dollar: $variable.path$
double_percent: %variable.path%
(default: moustache)
--separator STRING Separator between node names (default: .)
--regexps LIST Regular expressions to find pattern
Notice, that them must contain single capture group
Help options:
--help Print this help
$ yaml_interpolate --input=input.yaml --output=processed.yaml --formats=moustachestrings:
str1: "Hello"
str2: "world"
# Interpolated value: "Hello, world!"
greeting: "{{strings.str1}}, {{strings.str2}}!"numbers:
- 2.71828
- 6.022e+23
- value: 3.14
# Interpolated value: "Numbers: [ 2.71828, 6.022e+23, 3.14 ]"
text: "Numbers: [ {{numbers.0}}, {{numbers.1}}, {{numbers.2.value}} ]"- Inline form (single
formatsoption):$ yaml_interpolate --input=input.yaml --output=processed.yaml \ --formats=moustache,double_dollar,double_percent - Multi-option form (multiple
formatsoptions):$ yaml_interpolate --input=input.yaml --output=processed.yaml \ --formats=moustache \ --formats=double_dollar \ --formats=double_percent
strings:
str1: "Hello"
str2: "world"
# Interpolated value: "Hello, world!"
greeting1: "{{ strings.str1 }}, {{ strings.str2 }}!" # <-- format: moustache
greeting2: "$strings.str1$, $strings.str2$!" # <-- format: double_dollar
greeting3: "%strings.str1%, %strings.str2%!" # <-- format: double_percent
# Interpolated value: "Greetings: Hello, world!, Hello, world!, Hello, world!"
greetings: "Gretings: %greeting1%, $greeting2$, {{greeting3}}" # <-- CompositeFor example, here is used the next simple regular expressions:
\%\{(\S+)\}- parse"%{tokens}"pattern\$\{(\S+)\}- parse"${tokens}"pattern\$\[(\S+)\]- parse"$[tokens]"pattern
Notice, that some characters in regular expressions must be escaped twice!
For example - passing \${\{(\S+)\} option will be interpreted as $\{(\S+)\}
(not \$ at start, but $ - not what we want).
It must be rewritten in the next forms:
\\${\{(\S+)\}- escaping only\$at start\\$\\{(\\S+)\\}- escaping all special characters (safe way, preferred)[\$][\{](\S+)[\}]- non-escaping way, wrapping special characters. Interpreted as:[$][\{](\S+)[\}]
Read about escaping characters in shell here.
- Inline form (single
regexpsoption):$ yaml_interpolate --input=input.yaml --output=processed.yaml \ --formats=moustache \ --regexps="\\%\\{(\\S+)\\}","\\$\\{(\\S+)\\}","\\$\\[(\\S+)\\]" - Multi-option form (multiple
regexpsoptions):$ yaml_interpolate --input=input.yaml --output=processed.yaml \ --formats=moustache \ --regexps="\\%\\{(\\S+)\\}" \ --regexps="\\$\\{(\\S+)\\}" \ --regexps="\\$\\[(\\S+)\\]"
constants:
pi: 3.1415
e: 2.7182
# Interpolated value: "[ 3.1415, 2.7182, 3.1415, 2.7182 ]"
text: "[ {{constants.pi}}, %{constants.e}, ${constants.pi}, $[constants.e] ]"Multi-document processing supported - a YAML character stream may contain several documents. Each document is completely independent from the rest.
---
name: "Tomato"
color: "red"
text: "{{name}} is {{color}}" # <-- Interpolated value: "Tomato is red"
---
name: "Kiwi"
color: "green"
text: "{{name}} is {{color}}" # <-- Interpolated value: "Kiwi is green"
---
name: "Blueberry"
color: "blue"
text: "{{name}} is {{color}}" # <-- Interpolated value: "Blueberry is blue"Piping is possible by redirecting output into stdout - for reading by another tool. For example - redirecting into yq (analogue of jq):
$ yaml_interpolate --input=input.yaml --output=stdout --formats=moustache | yq eval '.some.field' -Reading from stdin is also possible - to use this tool in a pipeline. For example:
- Passing minified YAML string
- Processing by
yaml_interpolate - Extraction from
textnode: "Hello, world!"
$ echo "{data: {str: world}, text: 'Hello, {{data.str}}!'}" | yaml_interpolate --input=stdin --output=stdout | yq eval '.text' -- You must always specify full node path - relative search not allowed, since the content of a YAML file constitutes a
directed graph, not atree.root_node: leaf1: "Leaf 1 text" leaf2: "Leaf 2 text" leaf3: "{{leaf1}} & {{leaf2}}" # <-- Dont works, use '{{root_node.leaf1}} & {{root_node.leaf2}}' leaf4: "{{.leaf5.leaf1}} & {{.leaf5.leaf2}}" # <-- Dont works, use '{{root_node.leaf5.leaf1}} & {{root_node.leaf5.leaf2}}' leaf5: leaf1: "Leaf 6 text" leaf2: "Leaf 7 text"
- Be careful and avoid circular depency and recursion.
root_node: # Dont do this: leaf1: "{{root_node.leaf2}}" leaf2: "{{root_node.leaf1}}" leaf3: "{{root_node.leaf2}} & {{root_node.leaf3}}"
- Nodes keys not transformed, only values.
$ mkdir build
$ cd build
$ bash ../build.sh ../