Помилка синтаксису Bash, коли "else" слідує за порожнім пунктом "тоді"


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цикл є кращим варіантом (у поєднанні з GNU find, ви можете використовувати параметр -0 для обох; портативно ви повинні відмовитися від нового рядка).
Ян Худек

@JanHudec, Є способи портативно. -print0є -exec printf '%s\0' {} +(але портативно ви не можете мати справу з цим висновком, за винятком випадків, якщо ви хочете врахувати perl), і з find .//.деякою післяобробкою ви можете уникнути нових рядків для xargs. Зауважте, що це не так while read, це while IFS= read -r.
Стефан Шазелас

@Chris, я додав відповідь на власне питання з моменту прийняття відповіді.
Стефан Шазелас

91

Здається, що ви хочете зробити неопераційний, якщо файл відкритий, тому вам слід додати 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'.


ніколи не нова, :і це перша команда, перелічена в bash-buildins.
болов

26

Ще одна альтернатива: поверніть свою логіку.

if ! lsof "$filename" >/dev/null;then
    echo "deleting $filename"
    rm "$filename"
fi

17

TL; DR

Жоден з інших відповідей насправді не стосується вашого початкового питання про те, чому команда видає синтаксичну помилку. Це викликано відсутністю команди між тим часом та іншим .

Відсутня команда

Ваш оригінальний код виглядає приблизно так:

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'

Виправте свій синтаксис за допомогою вбудованого Борна

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

$ if true; then :; else echo; fi

Просто розміщення :в розділі між тим і іншим виправить помилку синтаксису, яку ви відчуваєте.


1
Відповідь Gnouc, яка також є найбільш популярною, вже стосується оригінального питання.
jlliagre

Відповідь лише на адресу синтаксичної помилки. FWIW, ви можете відтворити подібну помилку з підошвою з двокрапкою на початку рядка. Це дасть сильний натяк. $ ; -bash: syntax error near unexpected token ';'
Меттью Ганніган
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.