Documentation
¶
Index ¶
- func Getenv[T ValueType](key string) (T, error)
- func GetenvMap[T any](key, delim, sep string) (map[string]T, error)
- func GetenvSlice[T any](key, delim string) ([]T, error)
- func Load(files ...string) (map[string]string, error)
- func LoadReaders(readers ...io.Reader) (map[string]string, error)
- func Parse(b []byte) (map[string]string, error)
- func ParseReader(r io.Reader) (map[string]string, error)
- func Resolve(exp string) (string, error)
- func Subst(b []byte) ([]byte, error)
- type Error
- type ValueType
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Getenv ¶
Getenv returns value of the environmental variable.
Example ¶
package main
import (
"fmt"
"os"
"github.com/aileron-projects/go-env"
)
func main() {
os.Setenv("FOO", "foo")
os.Setenv("BAR", "123")
os.Setenv("BAZ", "true")
os.Setenv("ERR", "string")
fmt.Println(env.Getenv[string]("FOO"))
fmt.Println(env.Getenv[int]("BAR"))
fmt.Println(env.Getenv[bool]("BAZ"))
fmt.Println(env.Getenv[int]("ERR"))
}
Output: foo <nil> 123 <nil> true <nil> 0 go-env/env: getenv: key=ERR [strconv.Atoi: parsing "string": invalid syntax]
func GetenvMap ¶
GetenvMap returns map data of the environmental variable. delim is the delimiter that separates key-value pairs. sep is the separator that separates key and value. For example, delimiter is "," and seperater is "=" for KEY="foo=alice,bar=bob".
Example ¶
package main
import (
"fmt"
"os"
"github.com/aileron-projects/go-env"
)
func main() {
os.Setenv("FOO", "key1=val1,key2=val2")
os.Setenv("BAR", "key1:val1|key2:val2|key3")
os.Setenv("BAZ", "key1=123,key2=456")
os.Setenv("ERR", "key=string")
fmt.Println(env.GetenvMap[string]("FOO", "", "")) // Default delimiter "," and seperator "="
fmt.Println(env.GetenvMap[string]("BAR", "|", ":")) // Custom delimiter and seperator
fmt.Println(env.GetenvMap[int]("BAZ", "", ""))
fmt.Println(env.GetenvMap[int]("ERR", "", ""))
}
Output: map[key1:val1 key2:val2] <nil> map[key1:val1 key2:val2 key3:] <nil> map[key1:123 key2:456] <nil> map[] go-env/env: getenv: key=ERR [strconv.Atoi: parsing "string": invalid syntax]
func GetenvSlice ¶
GetenvSlice returns values of the environmental variable. delim is the delimiter that separates values. For example, delim will be "," for values like "KEY=foo,bar,baz".
Example ¶
package main
import (
"fmt"
"os"
"github.com/aileron-projects/go-env"
)
func main() {
os.Setenv("FOO", "alice,bob")
os.Setenv("BAR", "alice|bob")
os.Setenv("BAZ", "123,456")
os.Setenv("ERR", "string")
fmt.Println(env.GetenvSlice[string]("FOO", ",")) // Default delim is ","
fmt.Println(env.GetenvSlice[string]("BAR", "|")) // Custom delimiter
fmt.Println(env.GetenvSlice[int]("BAZ", ""))
fmt.Println(env.GetenvSlice[int]("ERR", ""))
}
Output: [alice bob] <nil> [alice bob] <nil> [123 456] <nil> [] go-env/env: getenv: key=ERR [strconv.Atoi: parsing "string": invalid syntax]
func Load ¶
Load loads environmental variables from files. It sets parsed values with os.Setenv. Duplicated keys are always overwritten. The default ".env" is loaded if no files provided. See Parse for file formats and Resolve for enviromental variable expressions.
func LoadReaders ¶
LoadReaders loads environmental variables from files. It sets parsed values with os.Setenv. Duplicated keys are always overwritten. Unline Load, LoadReaders do nothing even when no readers were provided.
func Parse ¶
Parse parses environmental variable from the given bytes. Typically Parse parses variables from files such as .env file. Parse resolves embedded environmental variables in the b. See Resolve for expressions.
References:
Input specifications:
Single line:
# Single quotes and double quotes are removed if entire value is enclosed.
# "export" can be placed before env name.
FOO=BAR >> BAR
FOO="BAR" >> BAR
FOO='BAR' >> BAR
FOO='B"R' >> B"R
FOO="B'R" >> B'R
export FOO=BAR >> BAR
Multiple lines:
# The following definition of FOO results in "BARBAZ".
# Line breaks of LF and CRLF are removed.
# BOTH single quotes and double quotes can be used to enclose multiple lines.
FOO="
BAR
BAZ
"
Comments:
# Sharp '#' can be used for commenting.
# It must not be in the scope of single quotes and double quotes.
# It must have at least 1 white space before '#' if the comment is inlined.
# comment >> Comment is appropriately parsed.
FOO=BAR # comment >> Comment is appropriately parsed.
FOO=BAR# comment >> '#' is not parsed as comment. It considered as a part of value.
Escapes:
# '\\' can be used for escaping characters by following the 3 rules.
# 1. '\\' always escapes special character of ', ", \\, #
# 2. '\\' is ignored when it is not in the scope of single quotes or double quotes.
# 3. '\\'n or "\n" in the scope of single or doubles quotes results in line breaks of LF.
FOO=B\"R >> B"R
FOO=B\'R >> B'A
FOO="B\"R" >> B"R
FOO=B\R >> BR (Its not in a scope of single or double quotes.)
FOO="B\nR" >> B<LF>R (\n is, if in a scope of quotes, converted into a line break.)
Environmental variables:
# Load resolves environmental variables.
FOO=BAR${BAZ}
func ParseReader ¶
Parse parses environmental variables from the reader. See Parse for more details.
func Resolve ¶
Resolve substitutes a single environmental variable. Supported patterns are listed below. Expressions are basically derived from shell parameter substitution. Note that the substitution behavior is NOT exactly the same as bash.
- https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
- https://tldp.org/LDP/abs/html/parameter-substitution.html
- https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02
Rules:
Expressions:
01: ${parameter} --- See the substitution rule table below.
02: ${parameter:-word} --- See the substitution rule table below.
03: ${parameter-word} --- See the substitution rule table below.
04: ${parameter:=word} --- See the substitution rule table below.
05: ${parameter=word} --- See the substitution rule table below.
06: ${parameter:?word} --- See the substitution rule table below.
07: ${parameter?word} --- See the substitution rule table below.
08: ${parameter:+word} --- See the substitution rule table below.
09: ${parameter+word} --- See the substitution rule table below.
10: ${parameter:offset} --- Trim characters before offset.
11: ${parameter:offset:length} --- Trim characters before offset and after offset+length.
12: ${!prefix*} --- Join the parameter name which has the prefix with a white space (Same with ${!prefix*}).
13: ${!prefix@} --- Currently fallback to #12.
14: ${#parameter} --- Length of value.
15: ${parameter#word} --- Currently fallback to #16.
16: ${parameter##word} --- Remove prefix of the value which matched to the word. Longest match if pattern specified.
17: ${parameter%word} --- Currently fallback to #18.
18: ${parameter%%word} --- Remove suffix of the value which matched to the word. Longest match if pattern specified.
19: ${parameter/pattern/string} --- Replace the first value which matched to the pattern to string.
20: ${parameter//pattern/string} --- Replace all values which matched to the pattern to string.
21: ${parameter/#pattern/string} --- Replace the prefix to string if matched to the pattern.
22: ${parameter/%pattern/string} --- Replace the suffix to string if matched to the pattern.
23: ${parameter^pattern} --- Convert initial character to upper case if matched to the pattern.
24: ${parameter^^pattern} --- Convert all characters which matched to the pattern to upper case.
25: ${parameter,pattern} --- Convert initial character to lower case if matched to the pattern.
26: ${parameter,,pattern} --- Convert all characters which matched to the pattern to lower case.
27: ${parameter@operator} --- Process value with the operator.
Substitution rules:
| # | expression | parameter Set | parameter Set | parameter Unset |
| | | and Not Null | But Null | |
| --- | ------------------ | -------------------- | --------------- | --------------- |
| 01 | ${parameter} | substitute parameter | substitute null | substitute null |
| 02 | ${parameter:-word} | substitute parameter | substitute word | substitute word |
| 03 | ${parameter-word} | substitute parameter | substitute null | substitute word |
| 04 | ${parameter:=word} | substitute parameter | substitute word | assign word |
| 05 | ${parameter=word} | substitute parameter | substitute null | assign word |
| 06 | ${parameter:?word} | substitute parameter | error | error |
| 07 | ${parameter?word} | substitute parameter | substitute null | error |
| 08 | ${parameter:+word} | substitute word | substitute null | substitute null |
| 09 | ${parameter+word} | substitute word | substitute word | substitute null |
parameter:
[0-9a-zA-Z_]+
word:
[^\$]*
pattern:
c : matches to the character ('$' is not allowed).
[a-z] : matches specified character range.
.* : matches any length of characters.
.? : matches zero or single characters.
operator:
U : convert all characters to upper case using [strings.ToUpper]
u : convert the first character to upper case using [strings.ToUpper]
L : convert all characters to lower case using [strings.ToLower]
l : convert the first character to lower case using [strings.ToLower]
Example ¶
package main
import (
"fmt"
"os"
"github.com/aileron-projects/go-env"
)
func main() {
os.Setenv("ABC", "abcdefg")
os.Setenv("FOO", "foo")
os.Setenv("BAR", "BAR")
os.Setenv("ARR_X", "xxx")
os.Setenv("ARR_Y", "yyy")
os.Unsetenv("BAZ")
must := func(s string, err error) string {
if err != nil {
panic(err)
}
return s
}
fmt.Println("${FOO} ------------", must(env.Resolve("${FOO}")))
fmt.Println("${BAZ:-default} ---", must(env.Resolve("${BAZ:-default}")))
fmt.Println("${BAZ-default} ---", must(env.Resolve("${BAZ-default}")))
fmt.Println("${BAZ:=default} ---", must(env.Resolve("${BAZ:=default}")))
fmt.Println("${BAZ=default} ---", must(env.Resolve("${BAZ=default}")))
fmt.Println("${BAZ:?default} ---", must(env.Resolve("${BAZ:?default}")))
fmt.Println("${BAZ?default} ---", must(env.Resolve("${BAZ?default}")))
fmt.Println("${BAZ:+default} ---", must(env.Resolve("${BAZ:+default}")))
fmt.Println("${BAZ+default} ---", must(env.Resolve("${BAZ+default}")))
fmt.Println("${ABC:3} ----------", must(env.Resolve("${ABC:3}")))
fmt.Println("${ABC:3:3} --------", must(env.Resolve("${ABC:3:3}")))
fmt.Println("${!ARR*} ----------", must(env.Resolve("${!ARR*}")))
fmt.Println("${!ARR@} ----------", must(env.Resolve("${!ARR@}")))
fmt.Println("${#FOO} ----------", must(env.Resolve("${#FOO}")))
fmt.Println("${FOO#[a-z]} -----", must(env.Resolve("${FOO#[a-z]}")))
fmt.Println("${FOO##[a-z]} ----", must(env.Resolve("${FOO##[a-z]}")))
fmt.Println("${FOO%[a-z]} -----", must(env.Resolve("${FOO%[a-z]}")))
fmt.Println("${FOO%%[a-z]} ----", must(env.Resolve("${FOO%%[a-z]}")))
fmt.Println("${FOO/[a-z]/x} ---", must(env.Resolve("${FOO/[a-z]/x}")))
fmt.Println("${FOO//[a-z]/x} --", must(env.Resolve("${FOO//[a-z]/x}")))
fmt.Println("${FOO/#[a-z]/x} --", must(env.Resolve("${FOO/#[a-z]/x}")))
fmt.Println("${FOO/%[a-z]/x} --", must(env.Resolve("${FOO/%[a-z]/x}")))
fmt.Println("${FOO^[f]} -------", must(env.Resolve("${FOO^[f]}")))
fmt.Println("${FOO^^[o]} ------", must(env.Resolve("${FOO^^[o]}")))
fmt.Println("${BAR,[B]} -------", must(env.Resolve("${BAR,[B]}")))
fmt.Println("${BAR,,[A]} ------", must(env.Resolve("${BAR,,[A]}")))
fmt.Println("${FOO@U} ---------", must(env.Resolve("${FOO@U}")))
}
Output: ${FOO} ------------ foo ${BAZ:-default} --- default ${BAZ-default} --- default ${BAZ:=default} --- default ${BAZ=default} --- default ${BAZ:?default} --- default ${BAZ?default} --- default ${BAZ:+default} --- default ${BAZ+default} --- default ${ABC:3} ---------- defg ${ABC:3:3} -------- def ${!ARR*} ---------- ARR_X ARR_Y ${!ARR@} ---------- ARR_X ARR_Y ${#FOO} ---------- 3 ${FOO#[a-z]} ----- oo ${FOO##[a-z]} ---- oo ${FOO%[a-z]} ----- fo ${FOO%%[a-z]} ---- fo ${FOO/[a-z]/x} --- xoo ${FOO//[a-z]/x} -- xxx ${FOO/#[a-z]/x} -- xoo ${FOO/%[a-z]/x} -- fox ${FOO^[f]} ------- Foo ${FOO^^[o]} ------ fOO ${BAR,[B]} ------- bAR ${BAR,,[A]} ------ BaR ${FOO@U} --------- FOO
func Subst ¶
Subst substitute environmental variable in the given bytes. See the Resolve for available variable syntax. Subst supports nested variables like ${FOO_${BAR}}. Use '\\' to escape the expression. e.g. \$\{FOO\}.
Example ¶
package main
import (
"fmt"
"os"
"github.com/aileron-projects/go-env"
)
func main() {
os.Setenv("FOO", "foo")
os.Setenv("BAR", "FOO")
os.Setenv("BAZ", "BAR")
b1, err := env.Subst([]byte(`\$\{FOO\}=${FOO}`))
fmt.Println(string(b1), err)
b2, err := env.Subst([]byte(`\$\{\$\{BAR\}\}=\$\{FOO\}=${${BAR}}`))
fmt.Println(string(b2), err)
b3, err := env.Subst([]byte(`\$\{\$\{\$\{BAZ\}\}\}=\$\{\$\{BAR\}\}=\$\{FOO\}=${${${BAZ}}}`)) // Resplve nested env.
fmt.Println(string(b3), err) // Nested env is not supported.
}
Output: ${FOO}=foo <nil> ${${BAR}}=${FOO}=foo <nil> ${${${BAZ}}}=${${BAR}}=${FOO}=foo <nil>
Example (All) ¶
package main
import (
"fmt"
"os"
"github.com/aileron-projects/go-env"
)
func main() {
os.Setenv("ABC", "abcdefg")
os.Setenv("FOO", "foo")
os.Setenv("BAR", "BAR")
os.Setenv("ARR_X", "xxx")
os.Setenv("ARR_Y", "yyy")
os.Unsetenv("BAZ")
txt := []byte(`
01: {parameter} => ${FOO}
02: {parameter:-word} => ${BAZ:-default}
03: {parameter-word} => ${BAZ-default}
04: {parameter:=word} => ${BAZ:=default}
05: {parameter=word} => ${BAZ=default}
06: {parameter:?word} => ${BAZ:?default}
07: {parameter?word} => ${BAZ?default}
08: {parameter:+word} => ${BAZ:+default}
09: {parameter+word} => ${BAZ+default}
10: {parameter:offset} => ${ABC:3}
11: {parameter:offset:length} => ${ABC:3:3}
12: {!prefix*} => ${!ARR*}
13: {!prefix@} => ${!ARR@}
14: {#parameter} => ${#FOO}
15: {parameter#word} => ${FOO#[a-z]}
16: {parameter##word} => ${FOO##[a-z]}
17: {parameter%word} => ${FOO%[a-z]}
18: {parameter%%word} => ${FOO%%[a-z]}
19: {parameter/pattern/string} => ${FOO/[a-z]/x}
20: {parameter//pattern/string} => ${FOO//[a-z]/x}
21: {parameter/#pattern/string} => ${FOO/#[a-z]/x}
22: {parameter/%pattern/string} => ${FOO/%[a-z]/x}
23: {parameter^pattern} => ${FOO^[f]}
24: {parameter^^pattern} => ${FOO^^[o]}
25: {parameter,pattern} => ${BAR,[B]}
26: {parameter,,pattern} => ${BAR,,[A]}
27: {parameter@U} => ${FOO@U}
27: {parameter@u} => ${FOO@u}
27: {parameter@L} => ${BAR@L}
27: {parameter@l} => ${BAR@l}
`)
b, _ := env.Subst(txt)
fmt.Println(string(b))
}
Output: 01: {parameter} => foo 02: {parameter:-word} => default 03: {parameter-word} => default 04: {parameter:=word} => default 05: {parameter=word} => default 06: {parameter:?word} => default 07: {parameter?word} => default 08: {parameter:+word} => default 09: {parameter+word} => default 10: {parameter:offset} => defg 11: {parameter:offset:length} => def 12: {!prefix*} => ARR_X ARR_Y 13: {!prefix@} => ARR_X ARR_Y 14: {#parameter} => 3 15: {parameter#word} => oo 16: {parameter##word} => oo 17: {parameter%word} => fo 18: {parameter%%word} => fo 19: {parameter/pattern/string} => xoo 20: {parameter//pattern/string} => xxx 21: {parameter/#pattern/string} => xoo 22: {parameter/%pattern/string} => fox 23: {parameter^pattern} => Foo 24: {parameter^^pattern} => fOO 25: {parameter,pattern} => bAR 26: {parameter,,pattern} => BaR 27: {parameter@U} => FOO 27: {parameter@u} => Foo 27: {parameter@L} => bar 27: {parameter@l} => bAR