Documentation
¶
Index ¶
- func EnvSubst(b []byte) (subst []byte, err error)
- func EnvSubst2(b []byte) (subst []byte, err error)
- func GetenvMap(name, delim, sep string) map[string][]string
- func GetenvSlice(name, delim string) []string
- func IsDir(path string) (bool, error)
- func IsFile(path string) (bool, error)
- func ListFiles(recursive bool, paths ...string) ([]string, error)
- func LoadEnv(b []byte) (map[string]string, error)
- func OpenFileReadOnly(path string) (*os.File, error)
- func OpenFileReadWrite(path string) (*os.File, error)
- func OpenFileWriteOnly(path string) (*os.File, error)
- func ReadFiles(recursive bool, paths ...string) (map[string][]byte, error)
- func ResolveEnv(in []byte) ([]byte, error)
- type EnvError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EnvSubst ¶
EnvSubst substitute environmental variable in the given bytes. See the ResolveEnv for available variable syntax. EnvSubst does not support nested variables. Use EnvSubst2 to allow 2 levels nested variable. Note that escaping variable like '\${FOO}' is not supported.
Example ¶
package main import ( "fmt" "os" "github.com/aileron-projects/go/zos" ) func main() { os.Setenv("FOO", "foo") os.Setenv("BAR", "FOO") b1, _ := zos.EnvSubst([]byte(`{FOO}=${FOO}`)) fmt.Println(string(b1)) b2, _ := zos.EnvSubst([]byte(`{{BAR}}=${${BAR}}`)) fmt.Println(string(b2)) // Nested env is not supported. }
Output: {FOO}=foo {{BAR}}=${FOO}
Example (All) ¶
package main import ( "fmt" "os" "github.com/aileron-projects/go/zos" ) func main() { os.Setenv("ABC", "abcdefg") os.Setenv("FOO", "foo") os.Setenv("BAR", "BAR") os.Setenv("ARR_X", "xxx") os.Setenv("ARR_Y", "yyy") 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, _ := zos.EnvSubst(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
func EnvSubst2 ¶
EnvSubst2 substitute environmental variable in the given bytes. See the ResolveEnv for available variable syntax. EnvSubst does support nested variables up to 2 levels. ${FOO_${BAR}} is allowed but ${FOO_${BAR_${BAZ}}}} is not allowed. Note that escaping variable like '\${FOO}' is not supported.
Example ¶
package main import ( "fmt" "os" "github.com/aileron-projects/go/zos" ) func main() { os.Setenv("FOO", "foo") os.Setenv("BAR", "FOO") b1, _ := zos.EnvSubst2([]byte(`{FOO}=${FOO}`)) fmt.Println(string(b1)) b2, _ := zos.EnvSubst2([]byte(`{{BAR}}={FOO}=${${BAR}}`)) fmt.Println(string(b2)) // Nested env is not supported. }
Output: {FOO}=foo {{BAR}}={FOO}=foo
func GetenvMap ¶
GetenvMap get map data from environmental variable. delim is the delimiter that separates key value pairs. sep is the separator string that separates key and value. For example ENV="foo=f1,foo=f2,bar=b1,baz" has ',' as delimiter and '=' as separator. It results in {"foo":["f1","f2"], "bar":["b1"], "baz":[""]} Key-value pairs without separator are considered as key only. In that cases, key is saved in the returned map with empty string value.
Example ¶
package main import ( "fmt" "os" "github.com/aileron-projects/go/zos" ) func main() { os.Setenv("FOO", "key1:val1,key2:val2") os.Setenv("BAR", "key1-alice|key1-bob|key2") fmt.Println(zos.GetenvMap("FOO", ",", ":")) fmt.Println(zos.GetenvMap("BAR", "|", "-")) }
Output: map[key1:[val1] key2:[val2]] map[key1:[alice bob] key2:[]]
func GetenvSlice ¶
GetenvSlice get slice data from environmental variable. delim is the delimiter that separates each values.
Example ¶
package main import ( "fmt" "os" "github.com/aileron-projects/go/zos" ) func main() { os.Setenv("FOO", "foo1,foo2,foo3") os.Setenv("BAR", "bar1|bar2|bar3") fmt.Println(zos.GetenvSlice("FOO", ",")) fmt.Println(zos.GetenvSlice("BAR", "|")) }
Output: [foo1 foo2 foo3] [bar1 bar2 bar3]
func IsDir ¶
IsDir returns if the given path is directory or not. It returns true even if the directory is symbolic link.
func IsFile ¶
IsFile returns if the given path is file or not. It returns false for non regular files such as
- Directory
- Symbolic link
- Device file
- Unix domain socket file
See fs.ModeType.
func ListFiles ¶
ListFiles returns file paths of the given paths. Paths can be directories or file and can be relative path or absolute path. If recursive is true, it looks for all sub directories recursively. Note that returned paths are cleaned by filepath.Clean.
func LoadEnv ¶
LoadEnv loads environmental variable from the given bytes. Typically LoadEnv is used for loading .env file. LoadEnv resolves embedded environmental variables in the b. THe syntax for substituting environmental variable follows the specification of ResolveEnv.
References:
Input specifications:
Single line: # Following declaration results in BAR. # 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 line: # The following definition of FOO results in "BARBAZ". # Line breaks of LF or 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 or double quotes. # It must have at least 1 white space before '#' if the comment is after value. # 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 character following the 3 rules. # 1. '\\' always escapes special character of ', ", \\, # # 2. '\\' is ignored when it is not in the scope of single 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: # LoadEnv resolves environmental variables. FOO=BAR${BAZ}
func OpenFileReadOnly ¶
OpenFileReadOnly returns the file with read-only mode. Unlike os.Open, it use os.ModePerm for permission. See also io.OpenFile, os.Open.
func OpenFileReadWrite ¶
OpenFileReadWrite returns the file with read write mode. See also os.MkdirAll and io.OpenFile. It
- creates directory if not exists.
- creates file if not exists.
- opens file with append mode.
func OpenFileWriteOnly ¶
OpenFileWriteOnly returns the file with write-only mode. Unlike os.Create, it use os.ModePerm for permission. See also os.MkdirAll, io.OpenFile and os.Create. It
- creates directory if not exists.
- creates file if not exists.
- opens file with append mode.
func ReadFiles ¶
ReadFiles reads files of the given paths. Paths can be absolute or relative, and file or directory. It check all sub directories and read files if the first argument recursive is true. It returns an empty map and nil error if no files found. note that paths in the map key are cleaned by filepath.Clean.
func ResolveEnv ¶
ResolveEnv substitutes a single environmental variable expression. Supported expressions 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/zos" ) func main() { os.Setenv("ABC", "abcdefg") os.Setenv("FOO", "foo") os.Setenv("BAR", "BAR") os.Setenv("ARR_X", "xxx") os.Setenv("ARR_Y", "yyy") must := func(b []byte, err error) string { if err != nil { panic(err) } return string(b) } fmt.Println("${FOO} ------------", must(zos.ResolveEnv([]byte("${FOO}")))) fmt.Println("${BAZ:-default} ---", must(zos.ResolveEnv([]byte("${BAZ:-default}")))) fmt.Println("${BAZ-default} ---", must(zos.ResolveEnv([]byte("${BAZ-default}")))) fmt.Println("${BAZ:=default} ---", must(zos.ResolveEnv([]byte("${BAZ:=default}")))) fmt.Println("${BAZ=default} ---", must(zos.ResolveEnv([]byte("${BAZ=default}")))) fmt.Println("${BAZ:?default} ---", must(zos.ResolveEnv([]byte("${BAZ:?default}")))) fmt.Println("${BAZ?default} ---", must(zos.ResolveEnv([]byte("${BAZ?default}")))) fmt.Println("${BAZ:+default} ---", must(zos.ResolveEnv([]byte("${BAZ:+default}")))) fmt.Println("${BAZ+default} ---", must(zos.ResolveEnv([]byte("${BAZ+default}")))) fmt.Println("${ABC:3} ----------", must(zos.ResolveEnv([]byte("${ABC:3}")))) fmt.Println("${ABC:3:3} --------", must(zos.ResolveEnv([]byte("${ABC:3:3}")))) fmt.Println("${!ARR*} ----------", must(zos.ResolveEnv([]byte("${!ARR*}")))) fmt.Println("${!ARR@} ----------", must(zos.ResolveEnv([]byte("${!ARR@}")))) fmt.Println("${#FOO} ----------", must(zos.ResolveEnv([]byte("${#FOO}")))) fmt.Println("${FOO#[a-z]} -----", must(zos.ResolveEnv([]byte("${FOO#[a-z]}")))) fmt.Println("${FOO##[a-z]} ----", must(zos.ResolveEnv([]byte("${FOO##[a-z]}")))) fmt.Println("${FOO%[a-z]} -----", must(zos.ResolveEnv([]byte("${FOO%[a-z]}")))) fmt.Println("${FOO%%[a-z]} ----", must(zos.ResolveEnv([]byte("${FOO%%[a-z]}")))) fmt.Println("${FOO/[a-z]/x} ---", must(zos.ResolveEnv([]byte("${FOO/[a-z]/x}")))) fmt.Println("${FOO//[a-z]/x} --", must(zos.ResolveEnv([]byte("${FOO//[a-z]/x}")))) fmt.Println("${FOO/#[a-z]/x} --", must(zos.ResolveEnv([]byte("${FOO/#[a-z]/x}")))) fmt.Println("${FOO/%[a-z]/x} --", must(zos.ResolveEnv([]byte("${FOO/%[a-z]/x}")))) fmt.Println("${FOO^[f]} -------", must(zos.ResolveEnv([]byte("${FOO^[f]}")))) fmt.Println("${FOO^^[o]} ------", must(zos.ResolveEnv([]byte("${FOO^^[o]}")))) fmt.Println("${BAR,[B]} -------", must(zos.ResolveEnv([]byte("${BAR,[B]}")))) fmt.Println("${BAR,,[A]} ------", must(zos.ResolveEnv([]byte("${BAR,,[A]}")))) fmt.Println("${FOO@U} ---------", must(zos.ResolveEnv([]byte("${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