Decifrando "syntax error near unexpected token"
O que você vai aprender
- Como ler a mensagem
syntax error near unexpected token - Por que a posição do token e a causa real frequentemente divergem
- Como depurar por token:
fi/then/done/(/newline/end of file
Resumo rápido
Em syntax error near unexpected token 'X', o X é onde o bash desistiu do parsing, e o erro real geralmente está antes dele.
- Classifique pelo token (
fi/then/done= fluxo de controle,(= parênteses / caracteres especiais,newline/end of file= algo não fechado) - Leia a linha do erro e a linha imediatamente anterior (suspeite do que vem antes do token, não do token em si)
- Verifique a sintaxe apenas com
bash -n script.sh(encontre a linha sem executar) - Inspecione terminações de linha e caracteres ocultos com
cat -A
Premissas
- Shell: bash (o padrão no Ubuntu / Debian)
- Alvo: um script
.shque você escreveu, ou um comando digitado em um shell interativo /bin/sh(dash) exibe uma redação diferente (coberto abaixo)
O que é syntax error near unexpected token?
Conclusão: Enquanto o bash analisa seu comando, ele encontrou uma palavra (token) que não pode aparecer legalmente naquela posição. Ele para na etapa de verificação de sintaxe, antes de executar qualquer coisa.
Antes de executar um comando, o bash primeiro o analisa para verificar se as palavras estão dispostas de acordo com sua gramática. No momento em que decide "esta palavra não pode estar aqui legalmente", ele exibe esse erro e não executa nada.
$ echo hello world) bash: syntax error near unexpected token `)'
Aqui ) é o unexpected token. O bash leu echo hello world normalmente, mas um ) apareceu sem um ( correspondente, então sinalizou um erro de sintaxe.
Enquanto command not found significa "o comando não foi localizado (mas a gramática estava correta)", syntax error significa "isso nem é uma declaração válida". Nada foi executado.
Por que a posição do token difere da causa real?
Conclusão: O bash lê da esquerda para a direita e para no primeiro ponto em que não consegue mais interpretar corretamente. O token reportado é esse ponto de parada; a causa (algo não fechado ou ausente) geralmente está antes dele.
Esta é 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 relatório aponta para fi na linha 3, mas a causa real é o then ausente na linha 1. O bash estava esperando then após if [ ... ], recebeu echo em vez disso, depois fi, e só então decidiu que não conseguia mais interpretar a entrada. Portanto, ele reporta o ponto onde parou (fi).
Dica de leitura
Quando o token é uma palavra de fechamento/terminação () / } / fi / done / esac / end of file), suspeite de um problema com o lado de abertura correspondente. Não corrija a posição do token; revise a estrutura antes dele.
Quando o token é fi / then / done
Conclusão: Um separador ausente (
;ou nova linha), umthen/doausente, ou um bloco não fechado. Verifique seif-fiefor-doneestão emparelhados.
Quando a estrutura de if / for / while / case está quebrada, essas palavras-chave se tornam o unexpected token.
Sem separador antes do then
Um ; ou nova linha é necessário entre a condição 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 correspondência
# 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 após a lista in (aqui echo na linha 2) como uma violação de gramática; ele nunca alcança done.
Quando você cola apenas algumas linhas, é fácil 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 é ( ou um símbolo
Conclusão: Um caractere especial do shell sem aspas como
()&;|<>. Coloque símbolos em nomes de arquivo ou strings entre aspas.
( e ) são caracteres especiais usados para subshells e definições de função, então escrevê-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 símbolo que você 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, então a aspas nunca fecha e você recebe um erro de sintaxe. Verifique com cat -A, ou redigite as aspas manualmente.
Quando o token é newline ou end of file
Conclusão: Uma aspas, parêntese, here-document ou bloco de controle não fechado. O bash leu até o final do arquivo sem encontrar o fechamento e parou no terminador.
unexpected token 'newline' ou unexpected end of file é um sinal de que algo aberto nunca foi fechado.
Uma aspas não fechada
$ echo "hello >
A " aberta nunca é fechada, então o bash trata a próxima linha como continuação 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 incompatível
# 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 rótulo exato começando no início da linha. Indente-o e ele não é reconhecido: o bash exibe um warning: here-document ... delimited by end-of-file (wanted 'EOF') e trata tudo até o final do arquivo como o corpo (isso é um aviso, não um erro de sintaxe, e a linha EOF é impressa como está). Para indentar com tabs, use <<-EOF (remove apenas tabs iniciais, não espaços).
Você está executando com sh?
Conclusão: Executar sintaxe exclusiva do bash (
[[ ]], arrays, substituição de processo) comsh script.shcausa um erro de sintaxe. Execute com bash, ou defina o shebang como#!/bin/bash.
No Ubuntu, /bin/sh é o dash, que não entende extensões do bash. Um script que funciona no bash falhará 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 redação diferente, mas a causa é a mesma: sintaxe que o POSIX sh não 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 você não está acidentalmente prefixando sh. Veja Corrigindo "bad interpreter" para detalhes.
Quando terminações de linha CRLF são a causa
Conclusão: 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 terminações de linha CRLF (\r\n), deixando um \r invisível 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 é 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 "terminações de linha: LF" e "codificação: UTF-8" para prevenir recorrência. Adicionar *.sh text eol=lf ao .gitattributes também funciona bem.