Erro de sintaxe do bash quando "else" segue uma cláusula vazia "then"

36

Por que o script a seguir não seria executado, mas forneceria um erro de sintaxe de else:

LOGS3_DIR=~/logs
if [ -d "$LOGS3_DIR" ]; then
 cd
 cd "$LOGS3_DIR"
 echo "$LOGS3_DIR"
 for filename in `find "." -mtime 1 -type f`
  do
  if lsof "$filename" > /dev/null
  then
    # file is open
  else
    echo "deleting $filename"
    rm "$filename"
  fi
 done
fi
Usuário Iniciante
fonte

Respostas:

23

Não use substituição de comando na saída defind . Aqui, tudo pode ser feito com find:

find . -mtime 1 -type f ! -exec lsof -t {} \; -exec rm -f {} \; > /dev/null

Com algumas findimplementações (incluindo FreeBSD de findonde vem e GNU find), você pode usar em -deletevez de -exec rm....

A razão pela qual você está recebendo um erro é que não há comando entre thene elsealguns shells (começando com o shell Bourne de onde vem essa sintaxe) exigem pelo menos um (e um comentário não é um comando). Note que é completamente arbitrário e não há razão para que essas conchas fariam isso. yashe zshnão tem essa limitação (if false; then else echo x; fi e até if false; then else fifunciona bem com eles).

Como já foi dito, você pode usar um comando noop como :(ou for nothing in; do nothing; done) ou reverter a lógica com a !palavra - chave (disponível nos shells POSIX, mas não no shell Bourne (você verá que o uso :disso era comum nesse shell)). mkshe por yashacaso o suporte if false; then () else echo x; fi(eu não confiaria nisso, pois isso pode mudar em versões futuras).

Outra abordagem é com:

lsof... || {
  cmd1
  cmd2
}

embora uma diferença é o status geral de saída que será a de lsofse lsoffalhar.

Stéphane Chazelas
fonte
17
Embora essa seja uma maneira muito melhor de fazer o que o Usuário do @Novice está tentando, ela não responde à pergunta.
precisa
Embora -execfrequentemente seja útil, xargsalgumas vezes é necessário um loop de shell. Nesse caso, um while read nameloop é a opção preferida (no bash com o GNU find, você pode usar a opção -0 para ambos; portably, você deve desistir da nova linha).
Jan Hudec
@JanHudec, existem maneiras portáteis. -print0é -exec printf '%s\0' {} +(mas, de maneira portável, você não pode lidar com essa saída, exceto se quiser considerar perl), e com find .//.e algum pós-processamento, você pode escapar das novas linhas para xargs. Note que não é um while read, é while IFS= read -r.
Stéphane Chazelas
@ Chris, adicionei uma resposta à pergunta real desde que a resposta acabou sendo aceita.
Stéphane Chazelas
91

Parece que você deseja fazer um no-op se o arquivo estiver aberto, então você deve adicionar um :comando que é nulo em bash:

if lsof "$filename" > /dev/null; then
  # file is open
  :
else
  printf 'deleting %s\n' "$filename"
  rm -- "$filename"
fi

Se você não usar :, bashnão poderá analisar seu código e mostrará um erro como bash: syntax error near unexpected token 'else'.

cuonglm
fonte
nunca é novo :e é o primeiro comando listado em bash-builtins.
bolov
26

Outra alternativa: inverta sua lógica.

if ! lsof "$filename" >/dev/null;then
    echo "deleting $filename"
    rm "$filename"
fi
Joseph R.
fonte
17

TL; DR

Nenhuma das outras respostas realmente responde à sua pergunta original sobre por que o comando fornece um erro de sintaxe. Isso é causado por um comando ausente entre então e mais .

Um comando ausente

Seu código original é assim:

if lsof "$filename" > /dev/null
then
  # file is open
else
  echo "deleting $filename"
  rm "$filename"
fi

O problema é que você tem um comentário entre então e outra coisa , mas o comentário não é tratado como um comando. Em resumo, você pode reescrever o problema que possui (estruturalmente falando) da seguinte maneira:

$ if true; then else echo; fi
bash: syntax error near unexpected token `else'

Corrija sua sintaxe com um Bourne interno

Você pode corrigir esse problema, colocando comandos reais antes de outra pessoa , mas um comentário por si só não vai fazer. A seção se-então não pode estar vazia; se você quiser um espaço reservado, use os dois pontos incorporados . Por exemplo:

$ if true; then :; else echo; fi

Simplesmente colocar :na seção entre então e outra coisa corrigirá o erro de sintaxe que você está enfrentando.

CodeGnome
fonte
11
A resposta do Gnouc, que também é a mais votada, já está abordando a pergunta original.
Jlliagre
Responda apenas para solucionar o erro de sintaxe. FWIW, você pode reproduzir um erro semelhante com um único ponto e vírgula no início de uma linha. Isso dará uma dica forte. $ ; -bash: syntax error near unexpected token ';'
Matthew Hannigan