「syntax error near unexpected token」の読み解き方
この記事で解決できること
syntax error near unexpected tokenの メッセージの読み方 が分かる- token が指す位置と 本当の原因がずれる理由 を理解できる
fi/then/done/(/newline/end of fileなど token 別に原因を切り分け できる
結論(読み解きの型)
syntax error near unexpected token 'X' の X は「bash がそこで構文として行き詰まった地点」であり、ミスは多くの場合その手前にある。
- token 名を見て分類する(
fi/then/doneなら制御構文、(なら括弧・特殊文字、newline/end of fileなら閉じ忘れ) - エラー行とその直前を読む(token の位置ではなく手前を疑う)
bash -n script.shで構文だけ検査(実行せず行番号を特定)- 改行コード・不可視文字を
cat -Aで確認
前提(対象環境)
- シェル: bash(Ubuntu / Debian 系の既定)
- 対象: 自分で書いた
.shスクリプト、または対話シェルで打ったコマンド /bin/sh(dash)はメッセージの文言が異なる(後述)
syntax error near unexpected token とは何か?
結論: bash がコマンドを解析(パース)する段階で、文法上そこに来てはいけない単語(token)に出くわした、というエラー。実行前の「構文チェック」で止まっている状態。
bash はコマンドを実行する前に、まず文法どおりに並んでいるかを解析する。その途中で「ここにこの単語が来るのは文法的におかしい」と判断すると、実行せずにこのエラーを出す。
$ echo hello world) bash: syntax error near unexpected token `)'
ここで ) が unexpected token(予期しない単語)として報告されている。echo hello world までは正しく読めたが、対応する ( が無いまま ) が現れたため、bash は文法エラーと判断した。
command not found が「コマンドは見つからない(が文法は正しい)」エラーなのに対し、syntax error は「そもそも文として成立していない」エラーである。実行は一切行われていない。
なぜ token の位置と本当の原因がずれるのか?
結論: bash は左から順に読み進め、「これ以上正しく解釈できない」最初の地点で停止する。報告される token はその停止地点であり、原因(閉じ忘れ・付け忘れ)はその手前にあることが多い。
これがこのエラー最大のハマりどころである。例を見る。
if [ "$x" -gt 0 ] echo "big" fi
$ bash script.sh script.sh: line 3: syntax error near unexpected token `fi'
報告されたのは 3 行目の fi だが、本当の原因は 1 行目の then 忘れである。bash は if [ ... ] の後に then が来るはずだと待っているのに、echo が来て、さらに fi が来て、ようやく「もう正しく解釈できない」と判断した。だから止まった地点(fi)が報告される。
読み方のコツ
token が「閉じ・終端」を表す単語() / } / fi / done / esac / end of file)のときは、対応する開き側に問題があると疑う。token の位置を直すのではなく、手前の構造を見直す。
token が fi / then / done のときは?
結論: 制御構文の区切り(
;や改行)の付け忘れ、then/doの欠落、ブロックの閉じ忘れが原因。if〜fi、for〜doneの対応を確認する。
if / for / while / case の構造が崩れていると、これらの予約語が unexpected token になる。
then の前に区切りが無い
if の条件と then の間には ; か改行が必要。
# NG: ] と then の間に区切りが無い if [ "$x" = "1" ] then echo ok fi
$ bash script.sh script.sh: line 3: syntax error near unexpected token `fi'
# OK: ; で区切る(または then を次の行へ) if [ "$x" = "1" ]; then echo ok fi
done / fi の閉じ忘れ・余り
# NG: do を付け忘れている for f in *.txt echo "$f" done
$ bash script.sh script.sh: line 3: syntax error near unexpected token `done'
for ... in ... の後には do が必要。do が無いまま done に到達するとこのエラーになる。
コピペで一部の行だけ貼り付けたとき、if だけ・do だけが欠けて構造が片肺になりやすい。ブロックは開きと閉じをセットで確認する。
token が ( や記号のときは?
結論: クォートしていない
()&;|<>などのシェル特殊文字が原因。ファイル名や文字列に含まれる記号は引用符で囲む。
( ) はサブシェルや関数定義に使う特殊文字なので、ファイル名や引数に裸で書くと文法エラーになる。
# NG: ファイル名の () が特殊文字として解釈される
$ cp report(final).txt /backup/
bash: syntax error near unexpected token `('対処はクォートまたはエスケープ。
# OK: クォートで囲む $ cp 'report(final).txt' /backup/ # OK: バックスラッシュでエスケープ $ cp report\(final\).txt /backup/
同じことが &(バックグラウンド実行)や ;(コマンド区切り)でも起きる。
# NG: & がバックグラウンド指定として解釈される $ echo Tom & Jerry
文字列として渡したい記号は必ず引用符で囲む。
貼り付け時の落とし穴
Web やドキュメントからコピーすると、" が全角の " "(スマートクォート)に化けていることがある。見た目は似ていても bash は別物として扱い、引用が閉じられず syntax error になる。cat -A で確認するか、手で打ち直す。
token が newline や end of file のときは?
結論: クォート・括弧・ヒアドキュメント・制御ブロックの閉じ忘れが原因。bash がファイル末尾まで読んでも「閉じ」が見つからず終端で停止している。
unexpected token 'newline' や unexpected end of file は「開いたものが閉じられていない」サイン。
クォートの閉じ忘れ
$ echo "hello >
開いた " が閉じられていないため、bash は次の行を文字列の続きと見なして入力を待ち続ける(> プロンプト)。スクリプトなら末尾で停止する。
echo "hello echo "world"
$ bash script.sh script.sh: line 3: unexpected EOF while looking for matching `"' script.sh: line 4: syntax error: unexpected end of file
ヒアドキュメントの終端ラベル不一致
# NG: 終端ラベルが本文 EOF と一致していない(行頭でない / スペース混入) cat <<EOF hello EOF
ヒアドキュメントの終端は行頭から始まる完全一致のラベルが必要。インデントすると終端と認識されず、ファイル末尾で unexpected end of file になる(<<-EOF ならタブのみ許容)。
sh で実行していないか?
結論: bash 専用構文(
[[ ]]・配列・プロセス置換)をsh script.shで実行すると構文エラーになる。bash で実行するか shebang を#!/bin/bashにする。
Ubuntu の /bin/sh は dash であり、bash の拡張構文を解釈できない。bash なら通るスクリプトでも sh で動かすと止まる。
# bash 専用の配列・[[ ]] を含むスクリプト arr=(a b c) [[ -n "$1" ]] && echo "$1"
# NG: sh(dash)で実行
$ sh script.sh
script.sh: 1: Syntax error: "(" unexpecteddash のメッセージは Syntax error: "(" unexpected で文言が異なるが、原因は同じ「POSIX sh では未対応の構文」。対処は次のいずれか。
# 1. bash で明示的に実行する $ bash script.sh # 2. shebang を bash にして直接実行する $ head -1 script.sh #!/bin/bash $ ./script.sh
./script.sh は shebang のインタプリタで動くが、sh script.sh は shebang を無視して dash で動く。意図せず sh を付けて実行していないか確認する。詳しくは「bad interpreter」エラーの直し方を参照。
CRLF 改行が原因のときは?
結論: Windows で編集したスクリプトは行末に
\r(CR)が混入し、構文エラーや謎の挙動を起こす。cat -Aで^Mを確認しdos2unixで変換する。
Windows のエディタで保存したスクリプトは改行が CRLF(\r\n)になり、各行末に見えない \r が付く。これが予約語や引用符にくっつき、構文エラーや command not found を誘発する。
# 行末の ^M が CR の混入を示す $ cat -A script.sh #!/bin/bash^M$ if [ "$x" = "1" ]; then^M$ echo ok^M$ fi^M$
$ が行末、^M が CR。これを LF に変換する。
# dos2unix があれば最短 $ dos2unix script.sh # 無ければ sed / tr で除去 $ sed -i 's/\r$//' script.sh
エディタ側で「改行コード: LF」「文字コード: UTF-8」に設定して保存すれば再発を防げる。.gitattributes に *.sh text eol=lf を書く運用も有効。