アルアカ - Arcadia Academia

Arcadia Academiaは「エンジニアリングを楽しむ」を合言葉に日本のデジタル競争力を高めることをミッションとするテックコミュニティです。

bashでenvファイルをsourceすると括弧でsyntax errorになる時の直し方

Featured image of the post

「AWSのEventBridgeでcron式を.envファイルに書いたら、sourceした瞬間に構文エラーが出た」「値に括弧を含めただけなのに、環境変数の読み込みスクリプトが動かなくなった」「他の人が書いたenv読み込みシェルスクリプトが、自分の設定だけエラーで落ちる」——bashで.envのような設定ファイルをsourceしていると、こうした「括弧が絡む構文エラー」に一度は当たります。原因は単純で、bashが括弧をサブシェルの開始として解釈してしまうからです。この記事では、症状の再現から原因の切り分け、直し方までを手順で説明します。

💡
30秒で要点
・KEY=cron(0 12 * * ? *)のように括弧を含む値を.envに書いてsourceすると、bashが括弧をサブシェルの開始構文として解釈してエラーになる
・直し方は値全体をシングルクォートで囲むだけ(KEY='cron(0 12 * * ? *)')
・自分の.envが正しくても、読み込み側の共通スクリプトがクォートを想定していないと同じエラーが起きる
・bash -nで構文だけ先に確認すると、実行前にエラー箇所を特定できる

目次を開く

症状: .envをsourceすると構文エラーになる

例えば次のような.envがあるとします。

# .env
SCHEDULE=cron(0 12 * * ? *)

これを普通に読み込もうとすると、次のように失敗します。

$ source .env
bash: .env: line 1: syntax error near unexpected token `('
bash: .env: line 1: `SCHEDULE=cron(0 12 * * ? *)'

exportで1行ずつ読み込む書き方でも同じです。

$ export SCHEDULE=cron(0 12 * * ? *)
bash: syntax error near unexpected token `('

原因: 括弧は値の一部ではなく「サブシェルの開始」の記号

bashは行を評価するとき、(を「ここからサブシェルを開始する」という制御構文の記号として扱います。クォートで囲んでいないcron(...)は、bashから見ると次のように解釈されます。

  • cronというコマンドを実行しようとする
  • 続く(でサブシェルを開こうとする
  • 中身の0 12 * * ? *をコマンドとして解釈しようとする

その結果、構文として破綻してエラーになります。値の中身が何であるかは関係なく、クォートで囲まれていない括弧はすべてこの扱いになります。同じ理由で、スペースを含む値や$から始まる値も、クォートなしでは意図通りに読み込めません。

直し方: 値全体をシングルクォートで囲む

対処はシンプルで、値全体をクォートで囲むだけです。

# .env
SCHEDULE='cron(0 12 * * ? *)'
$ source .env
$ echo "$SCHEDULE"
cron(0 12 * * ? *)

シングルクォートは中身を一切展開しないため、括弧はもちろん$や*もそのまま文字列として扱われます。値の中に変数展開($HOMEなど)を含めたい場合だけ、ダブルクォートを使います。ダブルクォートは$とバッククォート以外の記号をそのまま通すため、括弧だけの値であれば同じように動きます。

# ダブルクォートでも括弧だけなら通る
SCHEDULE="cron(0 12 * * ? *)"

早見表: 症状ごとの原因と対処

症状 原因 対処
syntax error near unexpected token '(' 括弧がサブシェル開始として解釈された 値をシングルクォートで囲む
値にスペースが入ると2つの変数に分かれる クォートなしの値がスペースで分割された 値全体をクォートで囲む
$から始まる値が別の文字列に変わる クォートなしで変数展開された シングルクォートで展開を止める
一部の環境だけ動かない 読み込み側のスクリプトがクォートを想定していない 次章の対策を読み込みスクリプト側にも入れる

他人が使うenv読み込みスクリプトを直す場合

自分の.envをクォートで直しても、共通の読み込みスクリプト(set -a; source .env; set +aのような形)自体が値を分解・加工していると、同じエラーが別の場所で再発します。他のメンバーも使うスクリプトを書くときは、次のような形にして値を分解しないようにします。

while IFS='=' read -r key value; do
  export "$key=$value"
done < .env

readで受け取ったvalueをクォートせずに再展開する処理(evalや文字列連結)が途中に挟まっていないかを、まず疑ってください。

まとめ

括弧を含む値を.envに書いてsourceすると、bashが括弧をサブシェルの開始として解釈し構文エラーになります。対処は値全体をシングルクォートで囲むことだけです。同じ理由でスペースや$を含む値も壊れるため、.envの値は基本的にすべてクォートしておくと安全です。共通の読み込みスクリプトを使っている場合は、そちらがクォートを崩していないかも合わせて確認してください。

よくある質問(FAQ)

ダブルクォートではダめですか?

括弧だけの値であればダブルクォートでも問題なく動きます。ただし値の中に$が含まれていると変数展開が起きるため、展開させたくない場合はシングルクォートを使ってください。

変数展開を含む値の場合はどうすればいいですか?

$HOMEのように展開してほしい変数を値に含める場合はダブルクォートを使います。展開してほしい部分だけをダブルクォートにし、括弧などの記号部分は変えないようにすると意図通りに動きます。

.envではなくexportコマンドを直接打つ場合も同じですか?

同じです。export KEY=cron(0 12 * * ? *)のようにその場でコマンドとして打つ場合も、bashは同じ理由で構文エラーにします。exportの後もクォートで値を囲んでください。

エラーが出る前に気付く方法はありますか?

.envが単純なKEY=VALUEの羅列であれば、sourceする前にサブシェルで一度試すと安全です。

bash -c 'source .env' && echo OK

構文エラーがあればここで先に検出でき、本番のシェルを壊さずに確認できます。

プログラミング学習でお悩みですか?

現役エンジニアがあなたの学習をマンツーマンでサポートします。

  • 学習の進め方がわからない
  • ポートフォリオの作り方を知りたい
  • 現場で使える技術を学びたい
まずは30分の無料相談

相談は完全無料・オンラインで気軽に

あなたを爆速で成長させるメンタリングプログラムはこちら

メンタープログラムバナー

学習・開発のお悩みは現役エンジニアに相談

メンタープログラムの詳細を見る

エンジニアの基礎学習ゲーム

プログラミング学習支援

無料相談はこちら