在YML中使用环境变量并设置默认值。

huangapple go评论82阅读模式
英文:

Using env variables in YML with default value

问题

我有以下代码来读取包含环境变量的yml配置文件:

confContent, err := ioutil.ReadFile("config.yml")
if err != nil {
    panic(err)
}
// 扩展环境变量
confContent = []byte(os.ExpandEnv(string(confContent)))
conf := &SysConfig{}
if err := yaml.Unmarshal(confContent, conf); err != nil {
    panic(err)
}

config.yml

db:
  name: ${DB_NAME:qm}
  host: localhost

它可以工作,但是如果没有提供DB_NAME环境变量,我该如何让它读取默认值呢?

英文:

I have the following code to read config files from yml which includes ENV variables too:

confContent, err := ioutil.ReadFile("config.yml")
	if err != nil {
		panic(err)
	}
	// expand environment variables
	confContent = []byte(os.ExpandEnv(string(confContent)))
	conf := &SysConfig{}
	if err := yaml.Unmarshal(confContent, conf); err != nil {
		panic(err)
	}

config.yml

db:
  name: ${DB_NAME:qm}
  host: localhost

It is working but how can I get it to read default values if DB_NAME env is not given?

答案1

得分: 6

你可以使用Expand函数替换ExpandEnv中的映射器,并考虑默认值,代码如下:

package main

import (
	"fmt"
	"os"
	"strings"
)

func main() {
	mapper := func(placeholderName string) string {
		split := strings.Split(placeholderName, ":")
		defValue := ""
		if len(split) == 2 {
			placeholderName = split[0]
			defValue = split[1]
		}

		val, ok := os.LookupEnv(placeholderName)
		if !ok {
			return defValue
		}

		return val
	}

	os.Setenv("DAY_PART", "morning")

	fmt.Println(os.Expand("Good ${DAY_PART:test}, ${NAME:Gopher}", mapper))
}

这将输出:

Good morning, Gopher

这是根据os包文档中的Expand示例进行的修改。

英文:

You can replace the mapper on ExpandEnv using Expand and take into account default values like this:

package main

import (
	"fmt"
	"os"
	"strings"
)

func main() {
	mapper := func(placeholderName string) string {
		split := strings.Split(placeholderName, ":")
		defValue := ""
		if len(split) == 2 {
			placeholderName = split[0]
			defValue = split[1]
		}

		val, ok := os.LookupEnv(placeholderName)
		if !ok {
			return defValue
		}

		return val
	}

	os.Setenv("DAY_PART", "morning")

	fmt.Println(os.Expand("Good ${DAY_PART:test}, ${NAME:Gopher}", mapper))
}

this will render

Good morning, Gopher

This is based on the example from Expand from the os package documentation.

答案2

得分: 2

我建议你使用由spf13开发的强大的Viper包来读取配置文件,它可以优雅地解决你的问题,并且你可以使用它来加载其他类型的配置文件。

解决你的问题

  1. 获取该包
go get github.com/spf13/viper
  1. 配置文件

假设你有一个名为db.yaml的配置文件:

db:
  name: somedb
  host: localhost
  1. 代码示例

正如我们所看到的,一旦Viper加载了配置文件,我们可以通过键获取值。Yaml文件将被解析为一个嵌套结构,你可以将其解组为Golang结构体。我们应该使用viper.GetString("db.name")来获取值,你可以参考这个页面获取更多用法信息。

import (
	"fmt"

	"github.com/spf13/viper"
)

func InitConf() {
	viper.AutomaticEnv()     // 读取系统环境变量
	viper.SetConfigName("db")  // 要加载的配置文件名
	viper.AddConfigPath(".")    // 配置文件路径

	viper.SetDefault("db.name", "mysqldb") // 在这里设置默认的数据库名称

    // 读取配置文件并处理错误
	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			// 配置文件未找到;如果需要,可以忽略错误
		} else {
			// 配置文件找到,但产生了其他错误
		}
	}

	fmt.Printf("db.name: %s", viper.GetString("db.name"))
}

viper.AutomaticEnv()方法将自动读取系统环境变量,假设你有一个名为ENV_XXX的环境变量,那么你可以通过viper.GetString("ENV_XXX")获取它。如果环境变量的名称与配置文件的键相同,Viper将优先使用环境变量

  • 当我们在db.yaml中未设置name时,输出将为db.name: mysqldb
  • 当我们在db.yaml中设置了name值为somedb时,输出将为db.name: somedb

希望你会发现它有用!有关更多使用细节,请参阅Viper的README文件。

英文:

I recommend you use the awesome Viper package by spf13) to read conf files, it can solve your problem gracefully and you can use it to load many other type of conf files.

Solve your problem

  1. Get the package
    go get github.com/spf13/viper
  1. conf file

Supposed you have conf file named db.yaml:

db:
  name: somedb
  host: localhost

  1. code example

As we can see, once Viper loads the conf file, we can get values by key. A Yaml file will be parsed as a nested structure which you can unmarshal into a Golang struct, We should use viper.GetString("db.name") to get the value, you can refer to this page to get more usage info.

import (
	"fmt"

	"github.com/spf13/viper"
)

func InitConf() {
	viper.AutomaticEnv()     // read system env 
	viper.SetConfigName("db")  // conf file name to be load
	viper.AddConfigPath(".")    // conf file path

	viper.SetDefault("db.name", "mysqldb") // you can set default da name value here 

    // do read conf file and handle error
	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			// Config file not found; ignore error if desired
		} else {
			// Config file was found but another error was produced
		}
	}

	fmt.Printf("db.name: %s", viper.GetString("db.name"))
}

viper.AutomaticEnv() this method will read system env automatically, suppose you have an env named ENV_XXX, then you can get it by viper.GetString("ENV_XXX"). If the env name is the same as one of conf file key, viper uses the env first

  • when we do not set the name in db.yaml the output will be db.name: mysqldb
  • when we set the name value: somedb in db.yaml the output will be db.name: somedb

Hope you will find it useful! more details on usage see the Viper README file

答案3

得分: 0

我猜你的conf结构体可能是这样的:

type Db struct {
    Name string
    Host string
}

type Config struct {
    Db *Db
}

所以要获取Dbname的默认变量,你必须像这样进行检查:

if conf.Db == nil || conf.Db.Name == "" {
    conf.Db.Name = "test"
}

希望对你有帮助!

英文:

I guess you had a struct for your conf like this:

Db *struct{
		Name string
		Host string
	}

so for retrieve the default variable of Dbname, you must check it like this:

if conf.Db == nil || conf.Db.Name == "" {
        conf.Db.Name = "test"
    }

huangapple
  • 本文由 发表于 2021年9月25日 15:01:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/69323868.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定