eval "$(shdotenv)"
はい、そういうことです。シェルスクリプトの標準機能だけでは一行で .env ファイルを読み込むのは無理です。書き方によっては可能な場合もありますが .env ファイルは各言語の実装ごとに細かい方言がありシェルスクリプトの文法とは細かい仕様が異なります。そこでシェルスクリプトに読み込むための shdotenv を作りました。.env ファイルを読み込んで外部コマンドを起動する CLI コマンドとしても使うことができるので、他の実装の dotenv コマンド等の代わりとして言語に依存せずに使うことが出来ます。
.env の仕様について
正式な仕様はありません。困ったことに各言語のライブラリがそれぞれそれっぽい文法で実装しています。(検索すると誰かが RFC を書こうとしようとした痕跡がありますが、広まってないようなのでリンクしません。)もちろんシェルスクリプトの文法とも異なります。以下はシェルスクリプトとは異なる点の一例です。
- キーと値の前後(
=の前後)の空白が除去されます。 - 値をクォートで括らなくても
|や&等がシェルの文法ではなくただの文字として扱われます。 - 値の中にクォートが含まれる場合、それはクォートを閉じる意味を持たず通常の文字として扱われます。
- 値の中の
\nは改行という意味を持ちます。実装によってはその他のエスケープ文字に対応してることもあります。 - 行末の
\による継続行のサポートはありません。
このような違いがあるため、一般的な .env ファイルはシェルスクリプトで正しく読み取れるとは限りません。
shdotenv の .env の仕様について
既存の .env が統一されてないので shdotenv でも新たに定義していますが、POSIX シェルスクリプトのサブセットとして定義しています。そのため shdotenv の標準の .env ファイルはシェルスクリプトの source コマンドでそのまま読み取ることが出来ます。source コマンドで読み取れるなら shdotenv は不要なのでは?と思うかもしれませんが、変数定義以外のシェルスクリプトコードが含まれているとエラーが出るようにしており安全に変数を読みとれるのと、すでに環境変数が定義されている場合は上書きしないように他の実装の仕様にあわせています。
さらに他の .env 形式を読み込むオプションもあるため、シェルスクリプトと他の言語とで相互運用が可能になっています。現時点で対応してる他の .env 形式は、ruby, node, python, php, go, docker です。
また Docker の --env-file による環境変数の読み取りが変数展開や複数行の値に対応していなかったので、それを可能にする contrib/dockerenv コマンドもおまけで作っています。
参考
- Ruby
- JavaScript
- Python
- Go
- PHP
- Rust