Decifrando "syntax error near unexpected token"
O que voce vai aprender
- Como ler a mensagem
syntax error near unexpected token - Por que a posicao do token e a causa real frequentemente divergem
- Como depurar por token:
fi/then/done/(/newline/end of file
Resumo rapido
Em syntax error near unexpected token 'X', o X e onde o bash desistiu do parsing, e o erro real geralmente esta antes dele.
- Classifique pelo token (
fi/then/done= fluxo de controle,(= parenteses / caracteres especiais,newline/end of file= algo nao fechado) - Leia a linha do erro e a linha imediatamente anterior (suspeite do que vem antes do token, nao do token em si)
- Verifique a sintaxe apenas com
bash -n script.sh(encontre a linha sem executar) - Inspecione terminacoes de linha e caracteres ocultos com
cat -A
Premissas
- Shell: bash (o padrao no Ubuntu / Debian)
- Alvo: um script
.shque voce escreveu, ou um comando digitado em um shell interativo /bin/sh(dash) exibe uma redacao diferente (coberto abaixo)
O que e syntax error near unexpected token?
Conclusao: Enquanto o bash analisa seu comando, ele encontrou uma palavra (token) que nao pode aparecer legalmente naquela posicao. Ele para na etapa de verificacao de sintaxe, antes de executar qualquer coisa.
Antes de executar um comando, o bash primeiro o analisa para verificar se as palavras estao dispostas de acordo com sua gramatica. No momento em que decide "esta palavra nao pode estar aqui legalmente", ele exibe esse erro e nao executa nada.
$ echo hello world) bash: syntax error near unexpected token `)'
Aqui ) e o unexpected token. O bash leu echo hello world normalmente, mas um ) apareceu sem um ( correspondente, entao sinalizou um erro de sintaxe.
Enquanto command not found significa "o comando nao foi localizado (mas a gramatica estava correta)", syntax error significa "isso nem e uma declaracao valida". Nada foi executado.
Por que a posicao do token difere da causa real?
Conclusao: O bash le da esquerda para a direita e para no primeiro ponto em que nao consegue mais interpretar corretamente. O token reportado e esse ponto de parada; a causa (algo nao fechado ou ausente) geralmente esta antes dele.
Esta e a maior armadilha deste erro. Considere:
if [ "$x" -gt 0 ] echo "big" fi
$ bash script.sh script.sh: line 3: syntax error near unexpected token `fi'
O relatorio aponta para fi na linha 3, mas a causa real e o then ausente na linha 1. O bash estava esperando then apos if [ ... ], recebeu echo em vez disso, depois fi, e so entao decidiu que nao conseguia mais interpretar a entrada. Portanto, ele reporta o ponto onde parou (fi).
Dica de leitura
Quando o token e uma palavra de fechamento/terminacao () / } / fi / done / esac / end of file), suspeite de um problema com o lado de abertura correspondente. Nao corrija a posicao do token; revise a estrutura antes dele.
Quando o token e fi / then / done
Conclusao: Um separador ausente (
;ou nova linha), umthen/doausente, ou um bloco nao fechado. Verifique seif-fiefor-doneestao emparelhados.
Quando a estrutura de if / for / while / case esta quebrada, essas palavras-chave se tornam o unexpected token.
Sem separador antes do then
Um ; ou nova linha e necessario entre a condicao e o then.
# NG: sem separador entre ] e then if [ "$x" = "1" ] then echo ok fi
$ bash script.sh script.sh: line 3: syntax error near unexpected token `fi'
# OK: separe com ; (ou coloque then na proxima linha) if [ "$x" = "1" ]; then echo ok fi
do ausente, ou done / fi sem correspondencia
# NG: o do esta ausente for f in *.txt echo "$f" done
$ bash script.sh script.sh: line 2: syntax error near unexpected token `echo' script.sh: line 2: ` echo "$f"'
for ... in ... deve ser seguido por do. Sem do, o bash para na linha apos a lista in (aqui echo na linha 2) como uma violacao de gramatica; ele nunca alcanca done.
Quando voce cola apenas algumas linhas, e facil deixar um if ou do solto e deixar a estrutura meio aberta. Sempre verifique a abertura e fechamento de um bloco como um par.
Quando o token e ( ou um simbolo
Conclusao: Um caractere especial do shell sem aspas como
()&;|<>. Coloque simbolos em nomes de arquivo ou strings entre aspas.
( e ) sao caracteres especiais usados para subshells e definicoes de funcao, entao escreve-los sem aspas em um nome de arquivo ou argumento causa um erro de sintaxe.
# NG: os () no nome do arquivo sao lidos como caracteres especiais
$ cp report(final).txt /backup/
bash: syntax error near unexpected token `('Corrija com aspas ou escape.
# OK: envolva em aspas $ cp 'report(final).txt' /backup/ # OK: escape com barra invertida $ cp report\(final\).txt /backup/
O mesmo acontece com & (background) ou ; (separador de comandos).
# NG: & e lido como operador de background $ echo Tom & Jerry
Sempre coloque entre aspas qualquer simbolo que voce pretende passar como texto literal.
Armadilha ao colar
Copiar da web ou de documentos pode transformar " em aspas curvas "smart quotes" ("`" "`"). Elas parecem similares, mas o bash as trata como caracteres diferentes, entao a aspas nunca fecha e voce recebe um erro de sintaxe. Verifique com cat -A, ou redigite as aspas manualmente.
Quando o token e newline ou end of file
Conclusao: Uma aspas, parentese, here-document ou bloco de controle nao fechado. O bash leu ate o final do arquivo sem encontrar o fechamento e parou no terminador.
unexpected token 'newline' ou unexpected end of file e um sinal de que algo aberto nunca foi fechado.
Uma aspas nao fechada
$ echo "hello >
A " aberta nunca e fechada, entao o bash trata a proxima linha como continuacao da string e continua esperando entrada (o prompt >). Em um script, ele para no final.
echo "hello echo "world"
$ bash script.sh script.sh: line 2: unexpected EOF while looking for matching `"'
Um terminador de here-document incompativel
# NG: o terminador nao corresponde (nao esta no inicio da linha / tem espacos iniciais) cat <<EOF hello EOF
Um terminador de here-document deve ser um rotulo exato comecando no inicio da linha. Indente-o e ele nao e reconhecido: o bash exibe um warning: here-document ... delimited by end-of-file (wanted 'EOF') e trata tudo ate o final do arquivo como o corpo (isso e um aviso, nao um erro de sintaxe, e a linha EOF e impressa como esta). Para indentar com tabs, use <<-EOF (remove apenas tabs iniciais, nao espacos).
Voce esta executando com sh?
Conclusao: Executar sintaxe exclusiva do bash (
[[ ]], arrays, substituicao de processo) comsh script.shcausa um erro de sintaxe. Execute com bash, ou defina o shebang como#!/bin/bash.
No Ubuntu, /bin/sh e o dash, que nao entende extensoes do bash. Um script que funciona no bash falhara quando executado com sh.
# Um script com arrays e [[ ]] exclusivos do bash arr=(a b c) [[ -n "$1" ]] && echo "$1"
# NG: executar com sh (dash)
$ sh script.sh
script.sh: 1: Syntax error: "(" unexpectedO dash exibe Syntax error: "(" unexpected com redacao diferente, mas a causa e a mesma: sintaxe que o POSIX sh nao suporta. Corrija de uma destas formas.
# 1. Execute explicitamente com bash $ bash script.sh # 2. Defina o shebang como bash e execute diretamente $ head -1 script.sh #!/bin/bash $ ./script.sh
./script.sh executa sob o interpretador no shebang, mas sh script.sh ignora o shebang e executa sob o dash. Verifique se voce nao esta acidentalmente prefixando sh. Veja Corrigindo "bad interpreter" para detalhes.
Quando terminacoes de linha CRLF sao a causa
Conclusao: Scripts editados no Windows carregam um
\r(CR) no final de cada linha, causando erros de sintaxe e comportamento estranho. Identifique^Mcomcat -Ae converta comdos2unix.
Um script salvo por um editor Windows usa terminacoes de linha CRLF (\r\n), deixando um \r invisivel no final de cada linha. Ele se gruda em palavras-chave e aspas e dispara erros de sintaxe ou command not found.
# o ^M no final das linhas mostra contaminacao por CR $ cat -A script.sh #!/bin/bash^M$ if [ "$x" = "1" ]; then^M$ echo ok^M$ fi^M$
$ marca o final da linha e ^M e o CR. Converta para LF.
# mais curto, se dos2unix estiver disponivel $ dos2unix script.sh # caso contrario, remova com sed / tr $ sed -i 's/\r$//' script.sh
Configure seu editor para salvar com "terminacoes de linha: LF" e "codificacao: UTF-8" para prevenir recorrencia. Adicionar *.sh text eol=lf ao .gitattributes tambem funciona bem.