DEV Community

Linda_pp
Linda_pp

Posted on

TIL: Pitfall on getting all environment variables in Go

#go

Hello, this is my first post at DEV.TO 🐶

To get the list of all environment variables in Go, I found a function to do that in standard libraries

Environ returns a copy of strings representing the environment, in the form "key=value".

Using this function, I tried to write a function to get all environment variables key-value pairs in map[string]string.

Bad example 😣

I at first wrote following code.

kv := os.Environ()
m := make(map[string]string, len(kv))
for _, s := range kv {
    if idx := strings.IndexRune(s, '='); idx >= 0 {
        m[s[:idx]] = s[idx+1:]
    }
}

// `m` is what I wanted!

This example looked working fine at first, however later I found in some case it did not work only on Windows.

I found that the m contains empty string key "" and checked the output from os.Environ() line by line. And finally I found the cause.

=C:=C:\path\to\current\dir

What's this? But environment variable $=C: was actually existing on system. It was a hidden environment variable not listed in output of set command.

I dug Go standard library and found a comment (thank you @mattn for helping me to find out this!).

https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/syscall/env_windows.go#L58-L60

Environment variables can begin with =
so start looking for the separator = at j=1.
https://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx

The post in the comment, What are these strange =C: environment variables? explained what $=C: is.

Okay, so the command processor sets these things, but what are they? They are a leftover from the command processor's attempt to mimic the old MS-DOS way that drives and directories were handled. Whereas in Win32, there is only one current directory, in MS-DOS, there was one current directory for each drive.

So this is a hidden special environment variable on Windows and I need to consider it on getting all environment variables.

Good example 😆

Fortunately, fix was easy. = is at first character, and environment variable name is never empty. So we can simply ignore the first character.

kv := os.Environ()
m := make(map[string]string, len(kv))
for _, s := range kv {
    // On Windows, environment variable names may start with '=' (e.g. =C:).
    // To handle the variables properly, skip first character of KEY=VALUE line.
    for i := 1; i < len(s); i++ {
        if s[i] == '=' {
            m[s[:i]] = s[i+1:]
            break
        }
    }
}

// `m` is what I wanted!

Top comments (0)