Env File Parser
Parse dotenv files with export prefixes, quoted values, inline comments, and strict key validation, returning a plain map.
Code
package main
import (
"fmt"
"regexp"
"sort"
"strconv"
"strings"
)
var envKeyPattern = regexp.MustCompile("^[A-Za-z_][A-Za-z0-9_]*$")
func parseEnvValue(raw string) (string, error) {
if len(raw) >= 2 && raw[0] == '\'' && raw[len(raw)-1] == '\'' {
return raw[1 : len(raw)-1], nil
}
if len(raw) >= 2 && raw[0] == '"' && raw[len(raw)-1] == '"' {
unquoted, err := strconv.Unquote(raw)
if err != nil {
return "", fmt.Errorf("bad quoted value: %w", err)
}
return unquoted, nil
}
if index := strings.Index(raw, " #"); index >= 0 {
raw = strings.TrimSpace(raw[:index])
}
return raw, nil
}
func ParseEnv(data []byte) (map[string]string, error) {
values := make(map[string]string)
for number, raw := range strings.Split(string(data), "\n") {
line := strings.TrimSpace(strings.TrimSuffix(raw, "\r"))
if line == "" || strings.HasPrefix(line, "#") {
continue
}
line = strings.TrimSpace(strings.TrimPrefix(line, "export "))
key, value, ok := strings.Cut(line, "=")
if !ok {
return nil, fmt.Errorf("line %d: missing '='", number+1)
}
key = strings.TrimSpace(key)
if !envKeyPattern.MatchString(key) {
return nil, fmt.Errorf("line %d: invalid key %q", number+1, key)
}
parsed, err := parseEnvValue(strings.TrimSpace(value))
if err != nil {
return nil, fmt.Errorf("line %d: %w", number+1, err)
}
values[key] = parsed
}
return values, nil
}
func main() {
document := []byte("# service settings\n" +
"export API_URL=https://api.example.test\n" +
"export APP_NAME=\"Demo \\\"API\\\"\"\n" +
"EMPTY=\n" +
"DB_PASSWORD='p@ss word'\n" +
"PORT=8080 # public port\n")
values, err := ParseEnv(document)
if err != nil {
panic(err)
}
keys := make([]string, 0, len(values))
for key := range values {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
fmt.Printf("%s=%q\n", key, values[key])
}
_, err = ParseEnv([]byte("1BAD=x"))
fmt.Println("invalid key rejected:", err != nil)
}Report a problem with this exampleOpens a pre-filled issue on GitHub.