Почему мне нужно поместить «do» в той же строке, что и «for»?

28

1. Резюме

Я не понимаю, зачем мне правило E010 bashate .


2. Детали

Я использую Bashate для Linting .shфайлов. Правило E010:

делать не на той же линии, что и для

for bashate:

  • Правильный:

    #!/bin/bash
    for f in bash/*.sh; do
        sashacommand "$f"
    done
  • Ошибка:

    #!/bin/bash
    for f in bash/*.sh
        do sashacommand "$f"
    done

Есть ли веские аргументы, зачем мне forи doв одну строчку?


3. Не полезно

Я не могу найти ответ на свой вопрос в:

  1. Google
  2. Статьи о лучших практиках кодирования ( пример )
  3. Bashate документация . Я нахожу только :

    Набор правил, которые помогают поддерживать последовательность в блоках управления. Они игнорируются на длинных очередях, которые имеют продолжение, потому что развертывание, которое является «интересным»

Саша Черных
источник
3
Отступы do, безусловно, немного необычны, но for ... \ndo\n<indent>body...чрезвычайно распространены, и я бы даже оценил больше, чем for ... ; do. Это также жалуется на это?
Майкл Гомер
20
bashateэто проверка стиля Стили по своей природе субъективны. Не используйте его, если вам не нравится стиль, который он навязывает. Вместо этого вы должны рассмотреть синтаксис / проверку ошибок как shellcheck .
Мей
@ meuh, спасибо, я уже использую ShellCheck. мой вопрос: E010- только стиль правило или в некоторых случаях мне нужно для и сделать в одной и той же линии , не только по причинам стиля.
Саша Черных
10
Там никогда не синтаксическая обязанность иметь для и делать на ту же строку в оболочке.
Мей
1
@ Саша Черных Это не само по себе отступление, а отступ do. Это как отступы открывающей фигурной скобки ifи добавить код на ту же строку в языке , как C. Другого примера, если питон позволил это, было бы поставив :в ifотступе на следующую строке и добавление кода на ту же строку. Это будет отличать его от дополнительных линий в теле.
JoL

Ответы:

60

Синтаксически следующие два фрагмента кода являются правильными и эквивалентными:

for f in bash/*.sh; do
    sashacommand "$f"
done
for f in bash/*.sh
    do sashacommand "$f"
done

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

for f in *
    do cmd1
    cmd2
done

... но помечать это как "ошибку" - ИМХО чье-то личное мнение, а не объективная истина.

В общем, почти любой ;может быть заменен на новую строку. И ;новая, и новая строка являются командными терминаторами. doэто ключевое слово, которое означает «здесь следует то, что нужно сделать (в этом forцикле)».

for f in *; do ...; done

такой же как

for f in *
do
   ...
done

и в качестве

for f in *; do
   ...
done

Причиной использования одного над другим является удобочитаемость и местные / личные стили стиля.


Личное мнение:

В заголовках цикла, которые очень длинные , я думаю, что может иметь смысл поставить doновую строку, как в

for i in animals people houses thoughts basketballs bees
do
    ...
done

или

for i in        \
    animals     \
    people      \
    houses      \
    thoughts    \
    basketballs \
    bees
do
    ...
done

То же самое относится к thenв ifзаявлении.

Но опять же, это сводится к личным предпочтениям стиля или к любому стилю кодирования, который использует ваша команда / проект.

Кусалананда
источник
Вы говорите: «В заголовках цикла, которые очень длинные, может иметь смысл поставить новую строку». Не могли бы вы указать причину этого? Учитывая, что за заголовком должен следовать do, я не вижу причины, по которой его размещение на следующей строке могло бы улучшить читаемость.
Конрад Рудольф
3
@KonradRudolph Мой терминал имеет ширину 80 символов. Если список будет перенесен, я сделаю его набором непрерывных строк (как в моем последнем примере), если он будет почти перенесен, я поставлю do(или then) на отдельной строке (как в примере со второго по последний). Все это связано с личными предпочтениями. Мне не нравятся слишком длинные строки, отчасти потому, что мой терминал "всего" шириной 80 символов, а отчасти потому, что я думаю, что он выглядит неопрятно. Мне также трудно разбирать очень длинные строки на наличие ошибок.
Кусалананда
1
Кажется ненужным постоянно указывать, что это мнение. Bashate - это руководство по стилю, поэтому оно основано на мнении.
Бармар
4
@Barmar, обратите внимание, что в данном вопросе используются такие фразы, как «need to» и «rule», а readme-вызов bashate вызывает в doотдельной строке «error». Это довольно жесткие фразы, так что это действительно кажется , стоит того , чтобы указать на то , что, скорее, наоборот, так называемое «правило» на самом деле просто субъективное мнение.
ilkkachu
2
@mtraceur Правильно, я не подвергаю сомнению личные предпочтения. Но имейте в виду, что ограничение на читаемость столбца, которое вы цитируете, не относится к коду. Это относится только к прозе. Движение глаз принципиально отличается для чтения кода, поэтому данные этих исследований здесь не применимы. А что касается использования исторических причин, которые больше не применяются в качестве обоснования, мы также использовали последние 8.3 имен файлов.
Конрад Рудольф
28

Обратите внимание на то, что написано в верхней части страницы:

bashate: эквивалент pep8 для скриптов bash

PEP8 - это руководство по стилю для кода Python. В то время как Python люди часто кажется, берут свои проблемы стиля очень серьезно [править] , это как раз то, руководство. Мы могли бы придумать плюсы как для размещения doстроки в одной строке for, так и для собственной линии.

Это та же дискуссия , где ставить {в коде C при запуске ifили whileблок (или такой).


Несколько строк ниже в документации, есть еще одно интересное правило:

E020: объявление функции не в формате ^function name {$

Я предполагаю, что суть в том, что автор этого руководства по стилю считает, что использование functionключевого слова необходимо, чтобы читатель распознал объявление функции. Тем не менее, function fooэто нестандартный способ определения функции: стандартный способ foo(). Единственное различие между ними (в Bash) состоит в том, что другое сложнее портировать на стандартную оболочку, как /bin/shв Debian или Ubuntu.

Итак, я бы предложил взять все эти руководства по стилю с небольшим количеством соли или три, и решить для себя, какой стиль лучше. Хорошая проверка стиля позволяет отключить некоторые проверки (bashate должен bashate -i E010,E011игнорировать эти две проверки.) Отличная проверка стиля позволит вам изменить тесты, например, для проверки противоположности E020, здесь.

ilkkachu
источник
12

TL; DR: Вам не нужно. Это просто мнение какого-то чувака.

Как и многие другие, я писал forподобные циклы десятилетиями:

for F in arg1 arg2 arg*
do
    somecommand "$F"
done

Этот макет подчеркивает тот факт, что do ... doneтело цикла окружено, как фигурные скобки в C-подобных языках, и позволяет символам новой строки разделять компоненты конструкции цикла.

Конечно, существуют религиозные войны о том, где размещать фигурные скобки, так что вы могли бы сказать, что жюри все еще отсутствует в этом. ИМХО, это ясно, как это было разработано для работы; Борн увлекался подобранными разделителями, ср. if ... fi, case ... esacИ потребность в точку с запятой в короткой версии показывает , что вы делаете это меньше , чем первоначально разработан. Ничего плохого в этом нет; технологические достижения и многие вещи, которые когда-то казались хорошей идеей, теперь не одобряются goto. Но пока все сообщество сценариев оболочки не примет предпочтения bashateавторов, вам не нужно ничего делать.

Alexis
источник
3
То, fiчто соответствует ifи esacт. Д., Взято из Алгола 68. Единственная причина, по которой forцикл doне заканчивается od, od- это имя существующей стандартной утилиты. См en.wikipedia.org/wiki/ALGOL_68C#Algol_68C_and_Unix
Kusalananda
1
Верно, блоки с разделителями по ключевым словам также использовались в других языках семейства Алгол, включая Паскаль (который использует begin ... endи т. Д.).
Алексис
2
Разве не слышал рассказ о том od, что это смешно ... (Хотя, почему бы тогда не закончить цикл " rof?")
Алексис
2
@alexis Ну, как сказал Кусалананда, это исходит из Алгола 68, это ключевой момент. Стивен Борн, создатель оригинальной оболочки Bourne, находился под сильным влиянием этого языка, и именно поэтому он придерживался этого синтаксиса. Даже когда он писал на C. Смотрите исходный код оболочки Bourne : minnie.tuhs.org//cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/mac.h Да, и путь, BEGINи ENDтам тоже определены.
Сергей Колодяжный
1
Кстати, весь этот стиль форматирования также происходит от Algol 68. Его FORи DOбудет также по отдельным линиям, по соглашению, за исключением , когда весь цикл будет легко помещается на одной строке.
reinierpost
3

Я удивлен, что никто еще не упомянул этот допустимый и переносимый синтаксис:

set alfa bravo charlie
for q do
  echo "$q"
done

Обратите внимание, что forи doнаходятся на одной строке, без точки с запятой. Результат:

alfa
bravo
charlie
Стивен Пенни
источник
Я одобряю этот ответ для включения упоминания об этом непонятном (но в некоторых случаях критическом для чистого и переносимого кода оболочки), однако стоит явно объяснить людям, что это сглаживает позиционные параметры, а также я вынужден упомянуть, что единственная «по-настоящему переносимая (tm)» форма - это та, где for qи doнаходятся в отдельных строках (форма в этом примере не поддерживалась пару десятилетий назад в исходной оболочке Almquish ( ash): in-ulm.de/~mascheck/various/ bourne_args / # endnote )
mtraceur
Хотя пример в этом ответе работает для меня, применение его к примеру в вопросе OP не работает.
Scribblemacher
3

Несколько хороших ответов здесь. Всегда берите гидов по стилю с долей соли, и ИМХО, и опыт, будьте очень осторожны в отношении любого сообщества, которое изобилует чрезмерной догмой, когда дело касается стиля. Похоже, они верят, что когда они НАКОНЕЦ получат последнюю точку, которую я поставил, и эту последнюю букву Т, то программное обеспечение будет работать. Иногда для устранения ошибок требуется более чем идеальный стиль ...

Когда я начал изучать оболочку, большинство сценариев и текстов использовали встроенный формат точки с запятой

for i in "$@"; do
    stuff
done

но поскольку я был неопытным, мне было немного трудно определить; и делать - оба необходимы для подтверждения синтаксической правильности. Поэтому я предпочел сделать на следующей строке все сам по себе. Я больше не делаю это (каламбур)

Кроме того, команда while является отличным кандидатом на небольшую хитрость:

while prep1; prep2; condition; do
    stuff
done

но когда команды подготовки немного громоздки и / или условие сложное, его лучше отформатировать как

while
    prep1
    prep2
    condition
do
    stuff
done

и затем добавление do как "; do" является явно ошибочным.

Вы можете даже стать более интенсивным, чем это:

while
    if con1; then
      cond2
    elif cond3; then
      cond4
    elif cond5; then
      cond6
    else
      cond7
    fi
do
    stuff
done

потому что каждое утверждение является выражением. Обратите внимание, как оба стиля используются оппортунистически. Держите это в чистоте, и это вполне читабельно. Уплотните или перекосите его во имя стиля или «экономии места», и это станет ужасным беспорядком.

Так! Учитывая довольно барочные стили сценариев оболочки в целом, все, что направлено на повышение ясности и легкий визуальный доступ к значимым символам, всегда хорошо.

Форма, где «do» написана в строке с командой, приятна, когда у вас есть небольшая команда - я не нахожу «do» визуально скрытым, и внутренняя команда действительно не скрыта:

for i in whatever
  do stuff
done

Я считаю, что на это довольно приятно смотреть, и у него есть аналоги с while, if и case:

while condition
  do stuff
end

if condition
  then stuff1
  else stuff2
fi

case $blah in
  a) stuff1;;
  b) stuff2;;
  c) stuff3;;
  *) stuffInfinity;;
esac

Ясность и общая аккуратность - это все, что просит от вас Кодд *.

* Эдгар Ф. Кодд, отец теории реляционных баз данных.


источник
1
Никогда не видел whileс ifусловием раньше. Круто!
Джо