Зачем писать весь сценарий bash в функциях?

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

Вы также должны помнить, что для управления Ovirt необходимо выбрать одну выделенную виртуальную машину.

Я ничего не могу сказать о Proxmox, потому что не работал с ним.

Почему вы не используете https://virt-manager.org/ . Это просто и быстро.

60
29.09.2016, 21:45
12 ответов

Я начал использовать тот же стиль программирования на bash после прочтения записи в блоге Кфира Лави «Защитное программирование на Bash» . Он приводит довольно много веских причин, но лично я считаю их наиболее важными:

  • процедуры становятся описательными: гораздо легче понять, что должна делать конкретная часть кода. Вместо стены кода вы видите: «О, функция find_log_errors читает этот файл журнала на предмет ошибок». Сравните это с поиском целого ряда строк awk / grep / sed, которые используют бог знает какой тип регулярного выражения в середине длинного скрипта - вы понятия не имеете, что он там делает, если нет комментариев.

  • вы можете отлаживать функции, заключив в set -x и set + x . Как только вы узнаете, что остальная часть кода работает нормально, вы можете использовать этот трюк, чтобы сосредоточиться на отладке только этой конкретной функции. Конечно, вы можете заключить части скрипта, но что, если это длинная часть? Проще сделать что-то вроде этого:

      set -x 
    parse_process_list 
    set + x 
     
  • использование печати с помощью cat << - EOF. . . EOF . Я использовал его несколько раз, чтобы сделать свой код более профессиональным. Кроме того, весьма удобна функция parse_args () с функцией getopts . Опять же, это помогает с удобочитаемостью, вместо того, чтобы запихивать все в сценарий как гигантскую стену текста. Их также удобно использовать повторно.

И очевидно, что это намного удобнее для тех, кто знает C, Java или Vala, но имеет ограниченный опыт работы с bash.Что касается эффективности, то здесь мало что можно сделать - сам по себе bash не самый эффективный язык, и люди предпочитают perl и python, когда дело касается скорости и эффективности. Тем не менее, вы можете nice функцию:

nice -10 resource_hungry_function

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

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

Некоторые из примеров, в которых я использовал этот стиль:

58
27.01.2020, 19:32

Некоторые важные трюизмы о программировании:

  • Ваша программа будет меняться, даже если ваш босс настаивает, что это не так.
  • Только код и ввод влияют на поведение программы.
  • Именование - это сложно.

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

При этом функции Bash имеют некоторые проблемы, не встречающиеся в большинстве языков:

  • Расстановка имен в Bash ужасна. Например, если забыть использовать ключевое слово local, это приведет к загрязнению глобального пространства имен.
  • Использование local foo="$(bar)" приводит к потере кода выхода bar.
  • Именованных параметров нет, поэтому нужно помнить, что "$@" означает в разных контекстах.

* Извините, если это обидно, но после использования комментариев в течение нескольких лет и разработки без них** в течение еще большего количества лет стало ясно, что лучше.

** Использование комментариев для лицензирования, документации API и тому подобного по-прежнему необходимо.

8
27.01.2020, 19:32

Причина, совершенно отличная от уже приведенных в других ответах: одна из причин, по которой этот метод иногда используется, когда единственный оператор определения функции на верхнем уровне - это вызов main , заключается в том, чтобы убедиться, что сценарий случайно не сделает что-нибудь неприятное, если сценарий усечен. Сценарий может быть усечен, если он передан из процесса A в процесс B (оболочка), а процесс A завершается по какой-либо причине до того, как он завершит запись всего сценария. Это особенно вероятно, если процесс A извлекает сценарий с удаленного ресурса. Хотя по соображениям безопасности это не очень хорошая идея, это то, что нужно сделать, и некоторые сценарии были изменены, чтобы предвидеть проблему.

11
27.01.2020, 19:32

Комментарии и интервалы не могут не приблизиться к удобочитаемости, которую могут выполнять функции, как я продемонстрирую. Без функций вы не увидите леса за деревьями - большие проблемы прячутся среди множества строк деталей. Другими словами, люди не могут одновременно сосредоточиться на мелких деталях и на общей картине. В коротком сценарии это может быть неочевидно; пока он остается коротким, он может быть достаточно читабельным. Программное обеспечение становится больше, а не меньше, и, конечно же, оно является частью всей программной системы вашей компании, которая, несомненно, намного больше, возможно, состоит из миллионов строк.

Подумайте, если бы я дал вам такие инструкции:

Place your hands on your desk.
Tense your arm muscles.
Extend your knee and hip joints.
Relax your arms.
Move your arms backwards.
Move your left leg backwards.
Move your right leg backwards.
(continue for 10,000 more lines)

К тому времени, когда вы прошли половину или даже 5%, вы бы забыли, какими были первые несколько шагов. Вы не могли обнаружить большинство проблем, потому что за деревьями не было видно леса. Сравните с функциями:

stand_up();
walk_to(break_room);
pour(coffee);
walk_to(office);

Это, безусловно, более понятно, сколько бы комментариев вы ни добавили в построчную последовательную версию. Это также делает намного более вероятным, что вы заметите, что вы забыли приготовить кофе и, вероятно, забыли sit_down () в конце. Когда вы думаете о деталях регулярных выражений grep и awk, вы не можете представить себе общую картину - «что, если кофе не приготовлен»?

Функции в первую очередь позволяют вам увидеть общую картину и заметить, что вы забыл приготовить кофе (или что кто-то может предпочесть чай). В другой раз, в другом настроении, вы беспокоитесь о детальной реализации.

Конечно, есть и другие преимущества, обсуждаемые в других ответах. Еще одно преимущество, которое четко не указано в других ответах, заключается в том, что функции обеспечивают гарантию, важную для предотвращения и исправления ошибок. Если вы обнаружите, что какая-то переменная $ foo в соответствующей функции walk_to () была неправильной, вы знаете, что вам нужно только посмотреть на остальные 6 строк этой функции, чтобы найти все, на что могла повлиять эта проблема, и все, что могло сделали это неправильно. Без (правильных) функций все и вся во всей системе может быть причиной того, что $ foo неверен, и $ foo может повлиять на все и вся. Следовательно, вы не можете безопасно исправить $ foo без повторного изучения каждой строчки программы. Если $ foo является локальным для функции, вы можете гарантировать безопасность и правильность любых изменений, проверяя только эту функцию.

7
27.01.2020, 19:32

В своем комментарии я упомянул три преимущества функций:

  1. Их легче тестировать и проверять на правильность.

  2. Функции можно легко повторно использовать (получить) в будущих сценариях

  3. Они нравятся вашему боссу.

И никогда не недооценивайте важность числа 3.

Я хотел бы затронуть еще одну проблему:

... так что возможность произвольно менять порядок выполнения - это не то, что мы вообще делал бы. Например, вы бы не захотели внезапно поместить declare_variables после walk_into_bar , это могло бы нарушить работу.

Чтобы получить преимущество разбиения кода на функции, нужно попытаться сделать функции как можно более независимыми. Если walk_into_bar требует переменной, которая больше нигде не используется, тогда эта переменная должна быть определена и сделана локальной для walk_into_bar . Процесс разделения кода на функции и минимизации их взаимозависимостей должен сделать код более понятным и простым.

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

40
27.01.2020, 19:32

Процесс требует последовательности. Большинство задач выполняются последовательно. Нет смысла возиться с порядком.

Но самое главное в программировании, включая создание сценариев, - это тестирование. Тестирование, тестирование, тестирование. Какие тестовые сценарии у вас есть в настоящее время для проверки правильности ваших сценариев?

Ваш босс пытается направить вас из сценариста в программиста. Это хорошее направление. Людям, которые придут после вас, вы понравитесь.

НО. Всегда помните о своих корнях, ориентированных на процесс. Если имеет смысл упорядочить функции в той последовательности, в которой они обычно выполняются, сделайте это, по крайней мере, в качестве первого прохода.

Позже вы увидите, что одни из ваших функций обрабатывают ввод, другие - вывод, третьи обрабатывают, третьи моделируют данные, а третьи манипулируют данными, поэтому может быть разумно сгруппировать похожие методы, возможно, даже убрать их. в отдельные файлы.

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

7
27.01.2020, 19:32

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

#!/bin/bash
# ex62.sh: Global and local variables inside a function.

func ()
{
  local loc_var=23       # Declared as local variable.
  echo                   # Uses the 'local' builtin.
  echo "\"loc_var\" in function = $loc_var"
  global_var=999         # Not declared as local.
                         # Therefore, defaults to global. 
  echo "\"global_var\" in function = $global_var"
}  

func

# Now, to see if local variable "loc_var" exists outside the function.

echo
echo "\"loc_var\" outside function = $loc_var"
                                      # $loc_var outside function = 
                                      # No, $loc_var not visible globally.
echo "\"global_var\" outside function = $global_var"
                                      # $global_var outside function = 999
                                      # $global_var is visible globally.
echo                      

exit 0
#  In contrast to C, a Bash variable declared inside a function
#+ is local ONLY if declared as such.

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

Повторное использование часто означает создание общей библиотеки функций и исходного кода этой библиотеки во все ваши сценарии. Это не поможет им работать быстрее, но поможет вам быстрее их писать.

18
27.01.2020, 19:32

Читаемость это одно. Но модульность - это не только это. ( Полумодуляризация , возможно, более правильна для функций.)

В функциях вы можете хранить некоторые переменные локальными, что увеличивает надежность , уменьшая вероятность того, что что-то испортится.

Еще одно преимущество функций - это повторное использование . После того, как функция закодирована, ее можно многократно применять в сценарии. Вы также можете перенести его на другой скрипт.

Ваш код сейчас может быть линейным, но в будущем вы можете войти в область многопоточности или многопоточности в мире Bash. Как только вы научитесь делать что-то в функциях, вы будете хорошо подготовлены для шага в параллель.

Еще одно замечание. Как отмечает Etsitpab Nioliv в комментарии ниже, перенаправить из функций легко как единое целое. Но есть еще один аспект перенаправления с функциями. А именно, перенаправления могут быть установлены по определению функции. Например: [

f () { echo something; } > log

] Теперь явные перенаправления для вызовов функций не требуются.

$ f

Это может сэкономить много повторений, что снова увеличивает надежность и помогает поддерживать порядок.

См. Также

71
27.01.2020, 19:32

Время - деньги

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

Я хочу сосредоточиться на одном ожидании: в рабочей среде «время - деньги» . Таким образом, отсутствие ошибок и производительность вашего кода оцениваются вместе с читабельностью , тестируемостью, ремонтопригодностью, рефакторингом, возможностью повторного использования ...

Запись в «модулях» код сокращает время чтения, необходимое не только самому кодеру , но даже время, используемое тестировщиками или начальником. Кроме того, обратите внимание, что время босса обычно оплачивается больше, чем время кодера, и что ваш босс оценит качество вашей работы.

Кроме того, написание в независимых «модулях» кода (даже сценария bash) позволит вам работать «параллельно» с другими компонентами вашей команды, сокращая общее время производства и использование в лучшем случае опыта сингла, чтобы просмотреть или переписать часть без побочных эффектов для других, переработать код, который вы только что написали «как есть» , для другой программы / сценария, чтобы создавать библиотеки (или библиотеки сниппетов), чтобы уменьшить общий размер и связанную с этим вероятность ошибок, отлаживать и тщательно тестировать каждую отдельную часть ... и, конечно, он организует логическую секцию вашей программы / скрипта и улучшит ее читаемость. Все, что сэкономит время и деньги.Недостатком является то, что вы должны придерживаться стандартов и комментировать свои функции (что, тем не менее, вы должны делать в рабочей среде).

Соблюдение стандарта замедлит вашу работу вначале, но впоследствии ускорит работу всех остальных (и вас тоже). Действительно, когда количество участников сотрудничества растет, это становится неизбежной необходимостью. Так, например, даже если я считаю, что глобальные переменные должны быть определены глобально , а не в функции, я могу понять стандарт, который инициализирует их в функции с именем declare_variables () всегда вызывается в первой строке main () one ...

И последнее, но не менее важное: не стоит недооценивать возможность в современных редакторах исходного кода отображать или скрывать выборочно отдельные процедуры ( Сворачивание кода ). Это сохранит компактность кода и позволит пользователю снова сэкономить время.

enter image description here

Здесь выше вы можете увидеть, как развернуто только функция walk_into_bar () . Даже если у других было по 1000 строк каждая, вы все равно могли держать под контролем весь код на одной странице. Обратите внимание, что он свернут даже в разделе, в котором вы переходите для объявления / инициализации переменных.

6
27.01.2020, 19:32

Помимо причин, приведенных в других ответах:

  1. Психология: Программист, чья производительность измеряется в строках кода, будет иметь стимул писать излишне многословный код. Чем больше руководство фокусируется на строках кода, тем больше стимул программиста расширять свой код за счет ненужной сложности. Это нежелательно, поскольку повышенная сложность может привести к увеличению стоимости обслуживания и увеличению усилий, необходимых для исправления ошибок.
2
27.01.2020, 19:32

Вы разбиваете код на функции по той же причине, по которой вы делали бы это для C / C ++, python, perl, ruby ​​или любого другого кода языка программирования. Более глубокая причина - это абстракция: вы инкапсулируете задачи более низкого уровня в примитивы (функции) более высокого уровня, чтобы вам не нужно было беспокоиться о том, как что-то делается. В то же время код становится более читаемым (и поддерживаемым), а логика программы - более понятной.

Однако, глядя на ваш код, я нахожу довольно странным наличие функции для объявления переменных; это действительно заставляет меня приподнять бровь.

16
27.01.2020, 19:32

Еще одна причина, которую часто упускают из виду, — разбор синтаксиса bash:

set -eu

echo "this shouldn't run"
{
echo "this shouldn't run either"

Этот скрипт явно содержит синтаксическую ошибку, и bash вообще не должен его запускать, верно? Неправильно.

~ $ bash t1.sh
this shouldn't run
t1.sh: line 7: syntax error: unexpected end of file

Если бы мы завернули код в функцию, этого бы не произошло:

set -eu

main() {
  echo "this shouldn't run"
  {
  echo "this shouldn't run either"
}

main
~ $ bash t1.sh
t1.sh: line 10: syntax error: unexpected end of file
4
27.01.2020, 19:32

Теги

Похожие вопросы