コンテンツにスキップ

環境変数

概要

環境変数のパース機能が提供されています。 これらの機能は、YMAL や JSON 等の設定ファイルにおける環境変数の埋め込みなどに利用できます。

環境変数に関する機能はzosパッケージにより提供されています。

環境変数名をもとに値を取得する単純なユースケースの場合は、Go 言語標準のosパッケージを利用してください。

機能

1. 環境変数解決機能

環境変数解決機能は環境変数を値に解決します。 以下のフォーマットの環境変数を解決できます。 これらのルールは基本的にShell Parameter Expansionをベースにしています。 parameterは[0-9a-zA-Z_]+の文字列です。 wordは$を含まない文字列[^\$]*です。

ほとんどの場合、本機能は#2-環境変数置換の機能を通して利用されることを想定しています。

  1. ${parameter} : 以下の置換ルール表を参照してください。
  2. ${parameter:-word} : 以下の置換ルール表を参照してください。
  3. ${parameter-word} : 以下の置換ルール表を参照してください。
  4. ${parameter:=word} : 以下の置換ルール表を参照してください。
  5. ${parameter=word} : 以下の置換ルール表を参照してください。
  6. ${parameter:?word} : 以下の置換ルール表を参照してください。
  7. ${parameter?word} : 以下の置換ルール表を参照してください。
  8. ${parameter:+word} : 以下の置換ルール表を参照してください。
  9. ${parameter+word} : 以下の置換ルール表を参照してください。
  10. ${parameter:offset} : offsetより前の文字を削除します。
  11. ${parameter:offset:length} : offsetより前と、offset + lengthより後の文字を削除します。
  12. ${!prefix*} : prefixを持つパラメータ名を空白区切りで連結します(${!prefix*}と同じ動作)。
  13. ${!prefix@} : 現在は #12 と同じ動作です。
  14. ${#parameter} : 値の長さを返します。
  15. ${parameter#word} : 現在は #16 と同じ動作です。
  16. ${parameter##word} : word にマッチするプレフィックスを削除します。パターンが指定された場合は最長一致で削除します。
  17. ${parameter%word} : 現在は #18 と同じ動作です。
  18. ${parameter%%word} : word にマッチするサフィックスを削除します。パターンが指定された場合は最長一致で削除します。
  19. ${parameter/pattern/string} : 最初に pattern にマッチした部分を string に置換します。
  20. ${parameter//pattern/string} : pattern にマッチしたすべての部分を string に置換します。
  21. ${parameter/#pattern/string} : pattern に一致した場合、プレフィックスを string に置換します。
  22. ${parameter/%pattern/string} : pattern に一致した場合、サフィックスを string に置換します。
  23. ${parameter^pattern} : pattern に一致した場合、最初の文字を大文字に変換します。
  24. ${parameter^^pattern} : pattern に一致したすべての文字を大文字に変換します。
  25. ${parameter,pattern} : pattern に一致した場合、最初の文字を小文字に変換します。
  26. ${parameter,,pattern} : pattern に一致したすべての文字を小文字に変換します。
  27. ${parameter@operator} : operator を使って値を処理します。
# expression parameter Set and Not Null parameter Set but Null parameter Unset
01 ${parameter} substitute parameter substitute null substitute null
02 ${parameter:-word} substitute parameter substitute word substitute word
03 ${parameter-word} substitute parameter substitute null substitute word
04 ${parameter:=word} substitute parameter substitute word assign word
05 ${parameter=word} substitute parameter substitute null assign word
06 ${parameter:?word} substitute parameter error error
07 ${parameter?word} substitute parameter substitute null error
08 ${parameter:+word} substitute word substitute null substitute null
09 ${parameter+word} substitute word substitute word substitute null

Cron 式では以下のエイリアスを利用できます。 CRON_TZを除き、各エイリアスはその他の Cron 式と組み合わせて利用することはできません。

エイリアス名 利用例
CRON_TZ TZ CRON_TZ=UTC 0 0 * * *
@monthly 0 0 1 * * TZ=UTC @monthly
@weekly 0 0 * * 0 TZ=UTC @weekly
@daily 0 0 * * * TZ=UTC @daily
@hourly 0 * * * * TZ=UTC @hourly
@sunday 0 0 * * 0 TZ=UTC @sunday
@monday 0 0 * * 1 TZ=UTC @monday
@tuesday 0 0 * * 2 TZ=UTC @tuesday
@wednesday 0 0 * * 3 TZ=UTC @wednesday
@thursday 0 0 * * 4 TZ=UTC @thursday
@friday 0 0 * * 5 TZ=UTC @friday
@saturday 0 0 * * 6 TZ=UTC @saturday

patternに指定可能な表記(文字)は以下の通りです。

pattern:
  c       : matches to the character ('$' is not allowed).
  [a-z]   : matches specified character range.
  .*      : matches any length of characters.
  .?      : matches zero or single characters.

operatorとして利用可能な文字は以下の通りです。

operator:
  U       : convert all characters to upper case using [strings.ToUpper]
  u       : convert the first character to upper case using [strings.ToUpper]
  L       : convert all characters to lower case using [strings.ToLower]
  l       : convert the first character to lower case using [strings.ToLower]

2. 環境変数置換

テキストデータ(Goにおける[]byteデータ)内に含まれる環境変数を解決し、値に置換します。 環境変数の置換に利用できる2つの関数が用意されています。

EnvSubstはネスト構造のない環境変数のみ解決できます。 EnvSubst2はネスト構造(1階層まで)のある環境変数を置換できます。 つまり、EnvSubst2を利用すると${FOO:-${BAR}}のような環境変数を置換できます。

func EnvSubst(b []byte) (subst []byte, err error)

func EnvSubst2(b []byte) (subst []byte, err error)

3. 環境変数パース機能

環境変数ファイルパース機能は環境変数の定義から環境変数をパースします。 この機能はParseEnv関数を通して利用します。 環境変数の定義フォーマットは基本的にbashのそれに似せて作られています。

パース関数のインターフェースは以下の通りで、パースした環境変数のキーと値が返却されます。 この機能では、環境変数はマップデータとして返却されるのみであり、OSの環境変数としては設定されません。

func ParseEnv(b []byte) (map[string]string, error)

単一行の変数定義の例を示します。 >>の右側にパースされた値を示しています。

FOO=BAR          >> BAR
FOO="BAR"        >> BAR
FOO='BAR'        >> BAR
FOO='B"R'        >> B"R
FOO="B'R"        >> B'R
export FOO=BAR   >> BAR

以下のようにクオーテーション(シングルクォーテーションまたはダブルクオーテーション)を利用することで、複数行の値をパースできます。 以下の例の場合、改行(\n)はパース時に削除されます。

FOO="
BAR
BAZ
"

#はコメント行を表します。 行末にコメントを記載する際は#の前に半角スペースを必ずおいてください。

# comment
FOO=BAR # comment

\により文字をエスケープできます。 以下のエスケープパターンをご参照ください。 >>の右側にパースされた値を示しています。 \nをクォーテーション内部で利用すると改行文字LFとして扱われます。

FOO=B\"R      >> B"R
FOO=B\'R      >> B'A
FOO="B\"R"    >> B"R
FOO=B\R       >> BR
FOO="B\nR"    >> B<LF>R

4. 環境変数ファイル読込機能

環境変数ファイル読込機能は環境変数を定義ファイル(例えば.envファイル)から読み込みます。 この機能はLoadEnv関数を通して利用します。 環境変数のパースには3. 環境変数パース機能が利用されます。 したがって定義ファイルのフォーマットはそちらを参照してください。

読込関数のインターフェースは以下の通りです。 この機能では、環境変数は返却されず、代わりにOSの環境変数として設定されます。

func LoadEnv(files ...string) error

セキュリティに関する特記事項

セキュリティに関する特記事項はありません。

性能に関する特記事項

性能に関する特記事項はありません。

実装例・使い方

環境変数の値解決

以下のコードでいくつかの環境変数を解決してみます。

package main

import (
    "fmt"
    "os"

    "github.com/aileron-projects/go/zos"
)

func main() {
    os.Setenv("ABC", "abcdefg")
    os.Setenv("FOO", "foo")
    os.Setenv("BAR", "BAR")
    os.Setenv("ARR_X", "xxx")
    os.Setenv("ARR_Y", "yyy")

    must := func(b []byte, err error) string {
        if err != nil {
            panic(err)
        }
        return string(b)
    }
    fmt.Println("${FOO} ------------", must(zos.ResolveEnv([]byte("${FOO}"))))
    fmt.Println("${BAZ:-default} ---", must(zos.ResolveEnv([]byte("${BAZ:-default}"))))
    fmt.Println("${BAZ-default}  ---", must(zos.ResolveEnv([]byte("${BAZ-default}"))))
    fmt.Println("${BAZ:=default} ---", must(zos.ResolveEnv([]byte("${BAZ:=default}"))))
    fmt.Println("${BAZ=default}  ---", must(zos.ResolveEnv([]byte("${BAZ=default}"))))
    fmt.Println("${BAZ:?default} ---", must(zos.ResolveEnv([]byte("${BAZ:?default}"))))
    fmt.Println("${BAZ?default}  ---", must(zos.ResolveEnv([]byte("${BAZ?default}"))))
    fmt.Println("${BAZ:+default} ---", must(zos.ResolveEnv([]byte("${BAZ:+default}"))))
    fmt.Println("${BAZ+default}  ---", must(zos.ResolveEnv([]byte("${BAZ+default}"))))
    fmt.Println("${ABC:3} ----------", must(zos.ResolveEnv([]byte("${ABC:3}"))))
    fmt.Println("${ABC:3:3} --------", must(zos.ResolveEnv([]byte("${ABC:3:3}"))))
    fmt.Println("${!ARR*} ----------", must(zos.ResolveEnv([]byte("${!ARR*}"))))
    fmt.Println("${!ARR@} ----------", must(zos.ResolveEnv([]byte("${!ARR@}"))))
    fmt.Println("${#FOO} ----------", must(zos.ResolveEnv([]byte("${#FOO}"))))
    fmt.Println("${FOO#[a-z]} -----", must(zos.ResolveEnv([]byte("${FOO#[a-z]}"))))
    fmt.Println("${FOO##[a-z]} ----", must(zos.ResolveEnv([]byte("${FOO##[a-z]}"))))
    fmt.Println("${FOO%[a-z]} -----", must(zos.ResolveEnv([]byte("${FOO%[a-z]}"))))
    fmt.Println("${FOO%%[a-z]} ----", must(zos.ResolveEnv([]byte("${FOO%%[a-z]}"))))
    fmt.Println("${FOO/[a-z]/x} ---", must(zos.ResolveEnv([]byte("${FOO/[a-z]/x}"))))
    fmt.Println("${FOO//[a-z]/x} --", must(zos.ResolveEnv([]byte("${FOO//[a-z]/x}"))))
    fmt.Println("${FOO/#[a-z]/x} --", must(zos.ResolveEnv([]byte("${FOO/#[a-z]/x}"))))
    fmt.Println("${FOO/%[a-z]/x} --", must(zos.ResolveEnv([]byte("${FOO/%[a-z]/x}"))))
    fmt.Println("${FOO^[f]} -------", must(zos.ResolveEnv([]byte("${FOO^[f]}"))))
    fmt.Println("${FOO^^[o]} ------", must(zos.ResolveEnv([]byte("${FOO^^[o]}"))))
    fmt.Println("${BAR,[B]} -------", must(zos.ResolveEnv([]byte("${BAR,[B]}"))))
    fmt.Println("${BAR,,[A]} ------", must(zos.ResolveEnv([]byte("${BAR,,[A]}"))))
    fmt.Println("${FOO@U} ---------", must(zos.ResolveEnv([]byte("${FOO@U}"))))
}

上記コードはにより以下の結果が出力されます。

${FOO} ------------ foo
${BAZ:-default} --- default
${BAZ-default}  --- default
${BAZ:=default} --- default
${BAZ=default}  --- default
${BAZ:?default} --- default
${BAZ?default}  --- default
${BAZ:+default} --- default
${BAZ+default}  --- default
${ABC:3} ---------- defg
${ABC:3:3} -------- def
${!ARR*} ---------- ARR_X ARR_Y
${!ARR@} ---------- ARR_X ARR_Y
${#FOO} ---------- 3
${FOO#[a-z]} ----- oo
${FOO##[a-z]} ---- oo
${FOO%[a-z]} ----- fo
${FOO%%[a-z]} ---- fo
${FOO/[a-z]/x} --- xoo
${FOO//[a-z]/x} -- xxx
${FOO/#[a-z]/x} -- xoo
${FOO/%[a-z]/x} -- fox
${FOO^[f]} ------- Foo
${FOO^^[o]} ------ fOO
${BAR,[B]} ------- bAR
${BAR,,[A]} ------ BaR
${FOO@U} --------- FOO

環境変数の置換

ParseEnvによる環境変数のパース例を示します。 パースされた値はマップデータとして返却されます。

package main

import (
    "github.com/aileron-projects/go/zos"
)

func main() {
    if err := zos.LoadEnv("env.txt"); err != nil {
        panic(err)
    }
}

上記を実行すると以下の値が得られます。 ターミナルへの出力のため、改行など一部エスケープされている点に注意してください。 また、見やすさのために出力をフォーマットしています。

{
  "BAR": "bar",
  "BAZ": "baz",
  "FOO": "foo",
  "MULTILINE_A": "onetwo",
  "MULTILINE_B": "one\ntwo",
  "PASSWORD": "bar",
  "QUOTE_DOUBLE": "double quoted. ' can be used.",
  "QUOTE_DOUBLE_ESCAPE": "double quotation \"escaped\".",
  "QUOTE_SINGLE": "single quoted. \" can be used.",
  "QUOTE_SINGLE_ESCAPE": "single quotation 'escaped'.",
  "SECRET_URL": "http://foo:bar@example.com",
  "URL": "http://example.com",
  "USERNAME": "foo"
}

参考資料