Ошибка синтаксиса Bash, когда «else» следует за пустым предложением «then»

36

Почему следующий скрипт не выполняется, но выдает синтаксическую ошибку 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
Начинающий пользователь
источник

Ответы:

23

Не используйте подстановку команд на выходеfind . Здесь все можно сделать с помощью find:

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

С несколькими findреализациями (включая FreeBSD, findоткуда она взята и GNU find), вы можете использовать -deleteвместо -exec rm....

Причина, по которой вы получаете ошибку, заключается в том, что между thenи elseи некоторыми оболочками нет команды (начиная с оболочки Bourne, из которой исходит этот синтаксис), требуется по крайней мере одна (и комментарий не является командой). Обратите внимание, что это совершенно произвольно, и нет никаких причин, почему эти оболочки делают это. yashи zshне имеют этого ограничения ( if false; then else echo x; fiи даже if false; then else fiотлично работают с ними).

Как уже говорили другие, вы можете использовать команду noop, например :(или for nothing in; do nothing; done), или изменить логику с помощью !ключевого слова (доступно в оболочках POSIX, но не в оболочке Bourne (вы обнаружите, что использование :для этого было распространено в этой оболочке)). mkshи yashслучиться с поддержкой if false; then () else echo x; fi(я бы не стал полагаться на это, поскольку это может измениться в будущих версиях).

Другой подход с:

lsof... || {
  cmd1
  cmd2
}

хотя одно из различий заключается в общем состоянии выхода, которое будет в lsofслучае lsofотказа.

Стефан Шазелас
источник
17
Хотя это гораздо лучший способ сделать то, что пытается сделать пользователь @Novice, он не отвечает на вопрос вообще.
SeeJayBee
Хотя -execчасто это полезно, xargsиногда требуется цикл оболочки. В этом случае while read nameпредпочтительным вариантом является цикл (в bash с GNU find вы можете использовать опцию -0 для обоих; порой вам придется отказаться от перевода строки).
Ян Худек
@JanHudec, есть способы переносимые. -print0есть -exec printf '%s\0' {} +(но, к сожалению, вы не можете иметь дело с этим выводом, кроме случаев, когда вы хотите рассмотреть это perl), и с помощью find .//.некоторой постобработки вы можете избежать перехода на новую строку xargs. Обратите внимание, что это не while readтак while IFS= read -r.
Стефан Шазелас
@ Крис, я добавил ответ на фактический вопрос, так как ответ в конечном итоге был принят.
Стефан
91

Кажется, что вы хотите сделать no-op, если файл открыт, поэтому вы должны добавить a :, которая является пустой командой bash:

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

Если вы не используете :, bashне можете проанализировать ваш код, и будет отображаться ошибка, как bash: syntax error near unexpected token 'else'.

cuonglm
источник
никогда не новое :и это первая команда, перечисленная в bash-buildins.
Болов
26

Другой вариант: поменять логику.

if ! lsof "$filename" >/dev/null;then
    echo "deleting $filename"
    rm "$filename"
fi
Джозеф Р.
источник
17

TL; DR

Ни один из других ответов на самом деле не отвечает на ваш первоначальный вопрос о том, почему команда выдает синтаксическую ошибку. Это вызвано отсутствием команды между then и else .

Пропавшая команда

Ваш оригинальный код выглядит так:

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

Проблема заключается в том, что у вас есть комментарий между тогда и еще , но комментарий не рассматривается как команда. Короче говоря, вы можете переписать проблему (структурно говоря) следующим образом:

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

Исправьте свой синтаксис с помощью Bourne Builtin

Вы можете решить эту проблему путем размещения фактических команд , прежде чем еще , но комментарий самого по себе не будет делать. Раздел if-then не может быть пустым; если вы хотите заполнитель, вы можете использовать встроенное двоеточие . Например:

$ if true; then :; else echo; fi

Просто размещение :в секцию между затем и еще будет исправить ошибку синтаксиса вы испытываете.

CodeGnome
источник
1
Ответ Gnouc, который также является самым голосуемым, уже затрагивает первоначальный вопрос.
Jlliagre
Ответ только для устранения синтаксической ошибки. FWIW, вы можете воспроизвести похожую ошибку с единственной точкой с запятой в начале строки. Это даст сильный намек. $ ; -bash: syntax error near unexpected token ';'
Мэттью Ханниган