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