Golang获取系统环境变量及godotenv库使用(Golang obtains system environment variables and godotenv library usage)

1、Golang 获取系统环境变量

Golang 要获取系统的环境变量需要导入”os”包,通过os包中的Getenv方法来获取。

func Getenv(key string) string {

}

示例代码:

package main
 
import (
  "fmt"
  "os"
)
 
func main() {
  fmt.Println(os.Getenv("GOPATH"))
} 

2、通过godotenv库配置环境变量 

2.1 简介

微服务应用提倡将配置存储在环境变量中。任何从开发环境切换到生产环境时需要修改的东西都从代码抽取到环境变量里。但是在实际开发中,如果同一台机器运行多个项目,设置环境变量容易冲突,不实用。godotenv库从文件中读取配置, 然后存储到程序的环境变量中。在代码中可以使用读取非常方便。源于一个 Ruby 的开源项目dotenv。

.env
godotenv

2.2 快速使用

第三方库需要先安装:

go get github.com/joho/godotenv

后使用:

package main
 
import (
  "fmt"
  "log"
  "os"
 
  "github.com/joho/godotenv"
)
 
func main() {
  err := godotenv.Load()
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("age: ", os.Getenv("age"))
}

然后在可执行程序相同目录下,添加一个文件

.env
name = zmc
age = 20

运行程序,输出:

name:  zmc
age:  20

可见,使用非常方便。默认情况下,读取项目根目录下的文件,文件中使用的格式,每行一个键值对。调用即可加载,可直接调用读取。

godotenv
.env
key = value
godotenv.Load()
os.Getenv("key")

2.3 高级特性

自动加载

如果连方法都不想自己调用,可以导入,配置会自动读取:

Load
github.com/joho/godotenv/autoload
package main
 
import (
  "fmt"
  "os"
 
  _ "github.com/joho/godotenv/autoload"
)
 
func main() {
  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("age: ", os.Getenv("age"))
}

注意,由于代码中没有显式用到库,需要使用空导入,即导入时包名前添加一个。

godotenv
_

看包的源码,其实就是库帮你调用了方法:

autoload
Load
// src/github.com/joho/godotenv/autoload/autoload.go
package autoload
 
/*
    You can just read the .env file on import just by doing
 
        import _ "github.com/joho/godotenv/autoload"
 
    And bob's your mother's brother
*/
 
import"github.com/joho/godotenv"
 
func init() {
  godotenv.Load()
}

加载自定义文件

默认情况下,加载的是项目根目录下的文件。当然我们可以加载任意名称的文件,文件也不必以为后缀:

.env
.env
package main
 
import (
  "fmt"
  "log"
  "os"
 
  "github.com/joho/godotenv"
)
 
func main() {
  err := godotenv.Load("common", "dev.env", "production.env")
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("version: ", os.Getenv("version"))
  fmt.Println("database: ", os.Getenv("database"))
}

文件内容:

common
name = awesome web
version = 0.0.1

dev.env文件内容
database = sqlite

production.env文件内容
database = mysql

运行结果:

name:  awesome web
version:  0.0.1
database:  sqlite

注意:接收多个文件名作为参数,如果不传入文件名,默认读取文件的内容。如果多个文件中存在同一个键,那么先出现的优先,后出现的不生效。所以,上面输出的是sqlite。

Load
.env
database

注释

文件中可以添加注释,注释以开始,直到该行结束。

.env
#
# app name
name = awesome web
# current version
version = 0.0.1

YAML

文件还可以使用 YAML 格式:

.env
name: awesome web
version: 0.0.1

不存入环境变量(重要)

允许不将文件内容存入环境变量,使用返回一个,可直接使用:

godotenv
.env
godotenv.Read()
map[string]string
package main
 
import (
  "fmt"
  "log"
 
  "github.com/joho/godotenv"
)
 
func main() {
  var myEnv map[string]string
  myEnv, err := godotenv.Read()
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])
}

输出:

name:  awesome web
version:  0.0.1

直接操作,简单直接!

map

数据源

除了读取文件,还可以从中读取配置:

string、io.Reader
io.Reader
package main
 
import (
  "fmt"
  "log"
 
  "github.com/joho/godotenv"
)
 
func main() {
  content := `
name: awesome web
version: 0.0.1
  `
  myEnv, err := godotenv.Unmarshal(content)
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])
}

输出:

name:  awesome web
version:  0.0.1

只要实现了接口,就能作为数据源。可以从文件(),网络(),等多种来源读取:

io.Reader
os.File
net.Conn
bytes.Buffer
package main
 
import (
    "bytes"
    "fmt"
    "log"
    "os"
 
    "github.com/joho/godotenv"
)
 
func main() {
  file, _ := os.OpenFile(".env", os.O_RDONLY, 0666)
  myEnv, err := godotenv.Parse(file)
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])
 
  buf := &bytes.Buffer{}
  buf.WriteString("name: awesome web @buffer")
  buf.Write([]byte{'\n'})
  buf.WriteString("version: 0.0.1")
  myEnv, err = godotenv.Parse(buf)
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])
}

输出:

name:  awesome web
version:  0.0.1
name:  awesome web @buffer
version:  0.0.1  

注意,从字符串读取和从读取使用的方法是不同的。前者为,后者是。

io.Reader
Unmarshal
Parse

生成文件

.env

可以通过程序生成一个文件的内容,可以直接写入到文件中:

.env
package main
 
import (
  "bytes"
  "log"
 
  "github.com/joho/godotenv"
)
 
func main() {
  buf := &bytes.Buffer{}
  buf.WriteString("name = awesome web")
  buf.WriteByte('\n')
  buf.WriteString("version = 0.0.1")
 
  env, err := godotenv.Parse(buf)
  if err != nil {
    log.Fatal(err)
  }
 
  err = godotenv.Write(env, "./.env")
  if err != nil {
    log.Fatal(err)
  }
}

查看生成的文件:

.env
name="awesome web"
version="0.0.1"

还可以返回一个字符串:

package main
 
import (
  "bytes"
  "fmt"
  "log"
 
  "github.com/joho/godotenv"
)
 
func main() {
  buf := &bytes.Buffer{}
  buf.WriteString("name = awesome web")
  buf.WriteByte('\n')
  buf.WriteString("version = 0.0.1")
 
  env, err := godotenv.Parse(buf)
  if err != nil {
    log.Fatal(err)
  }
 
  content, err := godotenv.Marshal(env)
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println(content)
}

输出:

name="awesome web"
version="0.0.1"

命令行模式

还提供了一个命令行的模式:

godotenv
 godotenv -f ./.env command args

前面在安装时,就已经安装在目录下了,我习惯把加入系统,所以命令可以直接使用。

go get
godotenv
godotenv
$GOPATH/bin
$GOPATH/bin
PATH
godotenv

命令行模式就是读取指定文件(如果不通过指定,则使用文件),设置环境变量,然后运行后面的程序。

-f
.env

我们简单写一个程序验证一下:

package main
 
import (
  "fmt"
  "os"
)
 
func main() {
  fmt.Println(os.Getenv("name"))
  fmt.Println(os.Getenv("version"))
}

使用运行一下:

godotenv
 godotenv -f ./.env go run main.go

输出:

awesome web
0.0.1

多个环境

实践中,一般会根据环境变量的值加载不同的文件:

APP_ENV
package main
 
import (
    "fmt"
    "log"
    "os"
 
    "github.com/joho/godotenv"
)
 
func main() {
    env := os.Getenv("GODAILYLIB_ENV")
    if env == "" {
        env = "development"
    }
 
    err := godotenv.Load(".env." + env)
    if err != nil {
        log.Fatal(err)
    }
 
    err = godotenv.Load()
    if err != nil {
        log.Fatal(err)
    }
 
    fmt.Println("name: ", os.Getenv("name"))
    fmt.Println("version: ", os.Getenv("version"))
    fmt.Println("database: ", os.Getenv("database"))
}

我们先读取环境变量,然后读取对应的 + env,最后读取默认的文件。

GODAILYLIB_ENV
.env.
.env

前面也提到过,先读取到的优先。我们可以在默认的文件中配置基础信息和一些默认的值, 如果在开发/测试/生产环境需要修改,那么在对应的文件中再配置一次即可。

.env
.env.development/.env.test/.env.production

文件内容:

.env
name = awesome web
version = 0.0.1
database = file

.env.development文件内容:
database = sqlite3

.env.production文件内容:
database = mysql

运行程序:

# 默认是开发环境
$ go run main.go
name:  awesome web
version:  0.0.1
database:  sqlite3
 
# 设置为生产环境
$ GODAILYLIB_ENV=production go run main.go
name:  awesome web
version:  0.0.1
database:  mysql

2.4 一点源码

读取文件内容,为什么可以使用访问:

godotenv
os.Getenv
// src/github.com/joho/godotenv/godotenv.go
func loadFile(filename string, overload bool) error {
    envMap, err := readFile(filename)
    if err != nil {
        return err
    }
 
    currentEnv := map[string]bool{}
    rawEnv := os.Environ()
    for _, rawEnvLine := range rawEnv {
        key := strings.Split(rawEnvLine, "=")[0]
        currentEnv[key] = true
    }
 
    for key, value := range envMap {
        if !currentEnv[key] || overload {
            os.Setenv(key, value)
        }
    }
 
    returnnil
}

因为调用将键值对设置到环境变量中了。

godotenv
os.Setenv

参考:https://www.cnblogs.com/-wenli/p/12332436.html

————————

1. Golang get system environment variables

To obtain the environment variables of the system, golang needs to import the “OS” package and obtain them through the getenv method in the OS package.

func Getenv(key string) string {

}

Example code:

package main
 
import (
  "fmt"
  "os"
)
 
func main() {
  fmt.Println(os.Getenv("GOPATH"))
} 

2. Configuring environment variables through the godotenv Library

2.1 introduction

Microservice applications advocate storing configuration in environment variables. Anything that needs to be modified when switching from the development environment to the production environment is extracted from the code into the environment variables. However, in actual development, if the same machine runs multiple projects, setting environment variables is easy to conflict and is not practical. The godotenv library reads the configuration from the file and stores it in the program’s environment variables. It can be used in the code, which is very convenient to read. It comes from a ruby open source project dotenv.

.env
godotenv

2.2 quick use

The third-party library needs to be installed first:

go get github.com/joho/godotenv

Post use:

package main
 
import (
  "fmt"
  "log"
  "os"
 
  "github.com/joho/godotenv"
)
 
func main() {
  err := godotenv.Load()
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("age: ", os.Getenv("age"))
}

< strong > then add a file < / strong > in the same directory of the executable program:

.env
name = zmc
age = 20

Run the program, output:

name:  zmc
age:  20

It can be seen that it is very convenient to use< Strong > by default, the file in the project root directory is read. The format used in the file is one key value pair per line. Call to load and directly call to read

godotenv
.env
key = value
godotenv.Load()
os.Getenv("key")

2.3 advanced features

< strong > auto load < / strong >

If you don’t even want to call the method yourself, you can import it and the configuration will be read automatically:

Load
github.com/joho/godotenv/autoload
package main
 
import (
  "fmt"
  "os"
 
  _ "github.com/joho/godotenv/autoload"
)
 
func main() {
  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("age: ", os.Getenv("age"))
}

Note that since the library is not explicitly used in the code, you need to use empty import, that is, add one before the package name when importing.

godotenv
_

Look at the source code of the package. In fact, the library calls the method for you:

autoload
Load
// src/github.com/joho/godotenv/autoload/autoload.go
package autoload
 
/*
    You can just read the .env file on import just by doing
 
        import _ "github.com/joho/godotenv/autoload"
 
    And bob's your mother's brother
*/
 
import"github.com/joho/godotenv"
 
func init() {
  godotenv.Load()
}

< strong > Load customization file < / strong >

By default, files in the root directory of the project are loaded. Of course, we can load files with any name, and the files do not have to be suffixed:

.env
.env
package main
 
import (
  "fmt"
  "log"
  "os"
 
  "github.com/joho/godotenv"
)
 
func main() {
  err := godotenv.Load("common", "dev.env", "production.env")
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("version: ", os.Getenv("version"))
  fmt.Println("database: ", os.Getenv("database"))
}

Document content:

common
name = awesome web
version = 0.0.1

dev.env文件内容
database = sqlite

production.env文件内容
database = mysql

Operation results:

name:  awesome web
version:  0.0.1
database:  sqlite

Note: < strong > multiple file names are received as parameters. If no file name is passed in, the contents of the file are read by default. If the same key exists in multiple files, the first one will take precedence and the last one will not take effect therefore, SQLite is output above.

Load
.env
database

注释

You can add comments to the file, starting with comments until the end of the line.

.env
#
# app name
name = awesome web
# current version
version = 0.0.1

YAML

Files can also use yaml format:

.env
name: awesome web
version: 0.0.1

< strong > do not save environment variables (important) < / strong >

It is allowed not to save the file content into the environment variable. If < strong > is used, a < / strong > is returned, which can be used directly:

godotenv
.env
godotenv.Read()
map[string]string
package main
 
import (
  "fmt"
  "log"
 
  "github.com/joho/godotenv"
)
 
func main() {
  var myEnv map[string]string
  myEnv, err := godotenv.Read()
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])
}

Output:

name:  awesome web
version:  0.0.1

Direct operation, simple and direct!

map

< strong > data source < / strong >

In addition to reading the file, you can also read the configuration from it:

string、io.Reader
io.Reader
package main
 
import (
  "fmt"
  "log"
 
  "github.com/joho/godotenv"
)
 
func main() {
  content := `
name: awesome web
version: 0.0.1
  `
  myEnv, err := godotenv.Unmarshal(content)
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])
}

Output:

name:  awesome web
version:  0.0.1

As long as the interface is implemented, it can be used as a data source. It can be read from file (), network (), and other sources:

io.Reader
os.File
net.Conn
bytes.Buffer
package main
 
import (
    "bytes"
    "fmt"
    "log"
    "os"
 
    "github.com/joho/godotenv"
)
 
func main() {
  file, _ := os.OpenFile(".env", os.O_RDONLY, 0666)
  myEnv, err := godotenv.Parse(file)
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])
 
  buf := &bytes.Buffer{}
  buf.WriteString("name: awesome web @buffer")
  buf.Write([]byte{'\n'})
  buf.WriteString("version: 0.0.1")
  myEnv, err = godotenv.Parse(buf)
  if err != nil {
    log.Fatal(err)
  }
 
  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])
}

Output:

name:  awesome web
version:  0.0.1
name:  awesome web @buffer
version:  0.0.1  

Note that the method used to read from a string is different from that used to read from a string. The former is and the latter is.

io.Reader
Unmarshal
Parse

< strong > generate file < / strong >

.env

The contents of a file can be generated through the program and can be written directly to the file:

.env
package main
 
import (
  "bytes"
  "log"
 
  "github.com/joho/godotenv"
)
 
func main() {
  buf := &bytes.Buffer{}
  buf.WriteString("name = awesome web")
  buf.WriteByte('\n')
  buf.WriteString("version = 0.0.1")
 
  env, err := godotenv.Parse(buf)
  if err != nil {
    log.Fatal(err)
  }
 
  err = godotenv.Write(env, "./.env")
  if err != nil {
    log.Fatal(err)
  }
}

View generated files:

.env
name="awesome web"
version="0.0.1"

You can also return a string:

package main
 
import (
  "bytes"
  "fmt"
  "log"
 
  "github.com/joho/godotenv"
)
 
func main() {
  buf := &bytes.Buffer{}
  buf.WriteString("name = awesome web")
  buf.WriteByte('\n')
  buf.WriteString("version = 0.0.1")
 
  env, err := godotenv.Parse(buf)
  if err != nil {
    log.Fatal(err)
  }
 
  content, err := godotenv.Marshal(env)
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println(content)
}

Output:

name="awesome web"
version="0.0.1"

< strong > command line mode < / strong >

A command line mode is also provided:

godotenv
 godotenv -f ./.env command args

In the previous installation, it has been installed in the directory. I am used to adding it to the system, so the command can be used directly.

go get
godotenv
godotenv
$GOPATH/bin
$GOPATH/bin
PATH
godotenv

Command line mode is to read the specified file (if not specified, use the file), set the environment variable, and then run the following program.

-f
.env

Let’s simply write a program to verify:

package main
 
import (
  "fmt"
  "os"
)
 
func main() {
  fmt.Println(os.Getenv("name"))
  fmt.Println(os.Getenv("version"))
}

Use to run the following:

godotenv
 godotenv -f ./.env go run main.go

Output:

awesome web
0.0.1

< strong > multiple environments < / strong >

In practice, different files are usually loaded according to the value of environment variables:

APP_ENV
package main
 
import (
    "fmt"
    "log"
    "os"
 
    "github.com/joho/godotenv"
)
 
func main() {
    env := os.Getenv("GODAILYLIB_ENV")
    if env == "" {
        env = "development"
    }
 
    err := godotenv.Load(".env." + env)
    if err != nil {
        log.Fatal(err)
    }
 
    err = godotenv.Load()
    if err != nil {
        log.Fatal(err)
    }
 
    fmt.Println("name: ", os.Getenv("name"))
    fmt.Println("version: ", os.Getenv("version"))
    fmt.Println("database: ", os.Getenv("database"))
}

We first read the environment variable, then read the corresponding + Env, and finally read the default file.

GODAILYLIB_ENV
.env.
.env

As mentioned earlier, the first read is preferred. You can configure basic information and some default values in the default file. If you need to modify them in the development / test / production environment, you can configure them again in the corresponding file.

.env
.env.development/.env.test/.env.production

Document content:

.env
name = awesome web
version = 0.0.1
database = file

.env.development文件内容:
database = sqlite3

.env.production文件内容:
database = mysql

Running program:

# 默认是开发环境
$ go run main.go
name:  awesome web
version:  0.0.1
database:  sqlite3
 
# 设置为生产环境
$ GODAILYLIB_ENV=production go run main.go
name:  awesome web
version:  0.0.1
database:  mysql

2.4 one point source code

Read the contents of the file, why can I use access:

godotenv
os.Getenv
// src/github.com/joho/godotenv/godotenv.go
func loadFile(filename string, overload bool) error {
    envMap, err := readFile(filename)
    if err != nil {
        return err
    }
 
    currentEnv := map[string]bool{}
    rawEnv := os.Environ()
    for _, rawEnvLine := range rawEnv {
        key := strings.Split(rawEnvLine, "=")[0]
        currentEnv[key] = true
    }
 
    for key, value := range envMap {
        if !currentEnv[key] || overload {
            os.Setenv(key, value)
        }
    }
 
    returnnil
}

Because the call sets the key value pair to the environment variable.

godotenv
os.Setenv

参考:https://www.cnblogs.com/-wenli/p/12332436.html