The parser is borrowed from systemd
v256
as is. Despite the original parser is slightly oversimplified and allows to do weird things,
see tests.
Common approach is to use environment variables to configure golang
programs.
The question is how to load the environment variables from file?
You can use shell options like source
or eval
in conjunction with sed
...
However, those ways are still awkward and not safe. In addition, you still enable to reuse your source
file.
On the other hand, systemd
is the most widespread system and service manager.
It allows you to load the environment variables right from file,
using EnvironmentFile
option.
Meet the thing that solves the problem.
There are two separate things here: (i) library to parse EnvironmentFile
format and (ii) ready to use binary tool
to run any processes with environment variables from given file.
Most obvious use case is to share literally the same file in systemd service
-file, like that:
[Service]
EnvironmentFile=/opt/golem/golem.env
And in development like that:
XENV=/opt/golem/golem.env xenv go run ./cmd/golem/...
or
cp /opt/golem/golem.env xenv.env
vi xenv.env # tune for development mode
xenv go run ./cmd/golem/...
echo 'TEST = "OK"' >xenv.env
xenv sh -c 'echo $TEST'
OK
The XENV
environment variable can be set to tell xenv
where to look for certain .env
-files.
xenv
will use the first matched file.
echo 'TEST = "OK"' >custom.env
export XENV=/tmp/x.env:./custom.env
xenv sh -c 'echo $TEST'
OK
Install in standard go way
go install github.com/michurin/systemd-env-file/cmd/xenv@latest
The binary will be installed in the directory named by the GOBIN
environment variable,
which defaults to $GOPATH/bin
or $HOME/go/bin
if the GOPATH
environment variable is not set.
Build manually and install to custom place
go build ./cmd/...
install xenv /opt/bin # use your favorite options
go get github.com/michurin/systemd-env-file/@latest
import "github.com/michurin/systemd-env-file/sdenv"
You can play with it right now at go online playground.
Similar to
Environment=
, but reads the environment variables from a text file. The text file should contain newline-separated variable assignments. Empty lines, lines without an=
separator, or lines starting with;
or#
will be ignored, which may be used for commenting. The file must be UTF-8 encoded. Valid characters are unicode scalar values other than noncharacters,U+0000
NUL
, andU+FEFF
byte order mark. Control codes other thanNUL
are allowed.In the file, an unquoted value after the
=
is parsed with the same backslash-escape rules as unquoted text in a POSIX shell, but unlike in a shell, interior whitespace is preserved and quotes after the first non-whitespace character are preserved. Leading and trailing whitespace (space, tab, carriage return) is discarded, but interior whitespace within the line is preserved verbatim. A line ending with a backslash will be continued to the following one, with the newline itself discarded. A backslash\
followed by any character other than newline will preserve the following character, so that\\
will become the value\
.In the file, a
'
-quoted value after the=
can span multiple lines and contain any character verbatim other than single quote, like single-quoted text in a POSIX shell. No backslash-escape sequences are recognized. Leading and trailing whitespace outside of the single quotes is discarded.In the file, a
"
-quoted value after the=
can span multiple lines, and the same escape sequences are recognized as in double-quoted text of a POSIX shell. Backslash (\
) followed by any of"
\
`
$
will preserve that character. A backslash followed by newline is a line continuation, and the newline itself is discarded. A backslash followed by any other character is ignored; both the backslash and the following character are preserved verbatim. Leading and trailing whitespace outside of the double quotes is discarded.The argument passed should be an absolute filename or wildcard expression, optionally prefixed with
-
, which indicates that if the file does not exist, it will not be read and no error or warning message is logged. This option may be specified more than once in which case all specified files are read. If the empty string is assigned to this option, the list of file to read is reset, all prior assignments have no effect.The files listed with this directive will be read shortly before the process is executed (more specifically, after all processes from a previous unit state terminated. This means you can generate these files in one unit state, and read it with this option in the next. The files are read from the file system of the service manager, before any file system changes like bind mounts take place).
Settings from these files override settings made with
Environment=
. If the same variable is set twice from these files, the files will be read in the order they are specified and the later setting will override the earlier setting.
You can find examples at playground, in documentation and the most detailed in tests.
- Go doc, hide
README.md
behind.github
and customize the root documentation page. - Consider docker compose's parser API. Mimic it's interface?
- Export environment in docker compose format?
parse_env_file_internal
—systemd
implementation- Useful constants: [1], [2]