Strongbash Коменты
Различия
Здесь показаны различия между двумя версиями данной страницы.
Предыдущая версия справа и слева Предыдущая версия Следующая версия | Предыдущая версия | ||
корзина:strongbash_коменты [10.03.2018 15:59] admin [strongbash022 TODO] |
корзина:strongbash_коменты [20.05.2019 15:18] (текущий) |
||
---|---|---|---|
Строка 1: | Строка 1: | ||
- | ===== Соглашение по строгому bash ===== | + | старые не обработанные коменты |
- | + | ||
- | ===== strongbash001 ===== | + | |
- | **Обязательно должен быть выполнен crab_indent**. | + | |
- | <hidden Почему...> | + | |
- | - indent делает код более читаемым | + | |
- | - использование indent позволяет обнаруживать ошибки | + | |
- | </hidden> | + | |
- | <hidden Пример...> | + | |
- | Конструкция **if-then** располагаются в одной строке.\\ | + | |
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | if [ "$EUID" -ne "0" ]; then | + | |
- | echo "Please run as root" | + | |
- | fi | + | |
- | </code> | + | |
- | + | ||
- | + | ||
- | Фигурная скобка **"{"** для определния тела функции должна быть в одной строке с функцией.\\ | + | |
- | После переноса строки добавляем **<Tab>**.\\ | + | |
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | myfunc1() { | + | |
- | commands... | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | Объявление функции без ключевого слова **function**.\\ | + | |
- | Ключевое слово function было введено в ksh.\\ | + | |
- | POSIX стандартизирует только синтаксис funcname().\\ | + | |
- | Код в скобках с отступами.\\ | + | |
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | myfunc1() { | + | |
- | echo "Hello world" | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | <color #ed1c24>Плохо:</color> | + | |
- | <code bash> | + | |
- | function myfunc1 { | + | |
- | echo "Hello world" | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | Инструкция **case ** без оступов:\\ | + | |
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | case "$variable" in | + | |
- | "$condition1" ) | + | |
- | command... | + | |
- | ;; | + | |
- | + | ||
- | "$condition2" ) | + | |
- | command... | + | |
- | ;; | + | |
- | + | ||
- | esac | + | |
- | </code>\\ | + | |
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | #!/usr/bin/env bash | + | |
- | + | ||
- | set -euEo pipefail | + | |
- | + | ||
- | func1(){ | + | |
- | grep 'zzz' /tmp/temp1.tmp | cat \ | + | |
- | | grep zza \ | + | |
- | && echo zzz || echo aaa | + | |
- | return 0 | + | |
- | } | + | |
- | + | ||
- | if func1; then | + | |
- | echo true | + | |
- | else | + | |
- | echo false | + | |
- | fi | + | |
- | + | ||
- | ( | + | |
- | cd /tmp/ | + | |
- | ls | + | |
- | ) \ | + | |
- | | ( | + | |
- | while read t; do | + | |
- | echo $t | + | |
- | done | + | |
- | ) | + | |
- | + | ||
- | case "${2:-bash}" in | + | |
- | bash) | + | |
- | echo bash | + | |
- | ;; | + | |
- | *) | + | |
- | echo "not supported yet. plz add new style" | + | |
- | ;; | + | |
- | esac | + | |
- | + | ||
- | exit 0 | + | |
- | </code> | + | |
- | </hidden> | + | |
- | + | ||
- | ===== strongbash002 ===== | + | |
- | **Должен быть установлен set -euEo pipefail или include ::carbon.sys** \\ | + | |
- | <WRAP center round important>ВНИМАНИЕ set -eu это самое главное правило, оно дает режим strong.\\ | + | |
- | Позволяет отлавливать и исключать неявное и непредсказуемое поведение программы, а также быстрей отлаживать.\\ | + | |
- | </WRAP> | + | |
- | <WRAP center round important> | + | |
- | ОЧЕНЬ БОЛЬШОЕ ВНИМАНИЕ set -eu используется для того, чтобы отловить неизвестные **будущие** ошибки программы и не дать ей выполняться далее.\\ | + | |
- | set -eu **не должен** использоваться для реализации какой либо логики основного алгоритма.\\ | + | |
- | </WRAP> | + | |
- | <hidden Что включает...> | + | |
- | set -e падать если есть необработанные ошибки, а точнее необработанный код возврата.\\ | + | |
- | set -u падать если используется неинициализированная переменная.\\ | + | |
- | set -o pipefail падать в ERR если в одной из пайп команд произошла ошибка.\\ | + | |
- | set -E наследовать trap в функциях.\\ | + | |
- | </hidden> | + | |
- | <hidden Как не надо делать...> | + | |
- | Todo вынести в отдельное правило\\ | + | |
- | <color #ed1c24>Плохо делать ассерты вида:</color>\\ | + | |
- | <code bash> | + | |
- | [ $# = 3 ] | + | |
- | </code> | + | |
- | <color #22b14c>Хорошо явно отлавливать и писать текстом пользователю о **заранее** известных ошибках:</color>\\ | + | |
- | <code bash> | + | |
- | [ $# != 3 ] && { echo 'Укажите 3 параметра'; exit 1; } | + | |
- | </code> | + | |
- | <color #ed1c24>Плохо **пытаться** использовать возможности set -e для формирования кода возврата функций:</color> | + | |
- | <code bash> | + | |
- | set -e | + | |
- | func1(){ | + | |
- | grep qqq /tmp/myqqq.txt | + | |
- | # ^^^ не надо надеяться на то, что здесь упадет и функция вернет не ноль, здесь не остановится выполнение функции. | + | |
- | # такой подход технически невозможен ни с ни без set -e | + | |
- | return 0 | + | |
- | } | + | |
- | ### это работать не будет | + | |
- | func1 && echo "qqq найден" | + | |
- | </code> | + | |
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | func1(){ | + | |
- | set -e ### требуется указать еще раз чтоб работало ret=$( func1 ) | + | |
- | grep qqq /tmp/myqqq.txt && echo TRUE || echo FALSE | + | |
- | return 0 | + | |
- | } | + | |
- | ret="$(func1)" | + | |
- | [ "$ret" = "TRUE" ] && echo "qqq найден" | + | |
- | </code> | + | |
- | </hidden> | + | |
- | <hidden Проблемы и решения set -euEo pipefail...> | + | |
- | <WRAP center round info> | + | |
- | set -euEo pipefail дает огромную пользу для предсказуемости работы и отладки, | + | |
- | но есть небольшие побочки, которые легко решаются.\\ | + | |
- | </WRAP> | + | |
- | <color #ed1c24>Проблема pipefail иногда мешает делать grep:</color>\\ | + | |
- | <code bash> | + | |
- | set -euEo pipefail | + | |
- | # grep myssh Упадет если нет слова myssh, хотя мы может этого не хотели, а хотели пустой файл | + | |
- | ps aux | grep -v grep | grep myssh >/tmp/tmp1; echo ret=$?; | + | |
- | </code> | + | |
- | <color #22b14c>Решение, добавить || true :</color> | + | |
- | <code bash> | + | |
- | set -euEo pipefail | + | |
- | ps aux | grep -v grep | grep myssh >/tmp/tmp1 || true; echo ret=$?; | + | |
- | </code> | + | |
- | \\ | + | |
- | <color #ed1c24>Проблема, если мы вызываем функцию и анализируем ее код возврата\\ | + | |
- | в ней перестает работать set -e :</color>\\ | + | |
- | <code bash> | + | |
- | set -e | + | |
- | check_is_oversized(){ | + | |
- | # set -e здесь бесполезно добавлять | + | |
- | [ `stat -c %s $1` -gt 1000 ] && return 1 # если программа stat упадет? то ошибки не будет тк set -e здесь выключен уже | + | |
- | # в итоге вернется return 0 даже если stat упал | + | |
- | return 0 | + | |
- | } | + | |
- | check_is_oversized /tmp34/11.log && echo "файл слишком большой" ### команда '&&' отключит обработку ошибок функции check_is_oversized | + | |
- | </code> | + | |
- | <color #22b14c>Решение, можно использовать echo TRUE вместо return 0 :</color>\\ | + | |
- | <code bash> | + | |
- | set -e | + | |
- | check_is_oversized(){ | + | |
- | set -e ### обязательно еще раз тк он снимется при ret=$( check_is_oversized ) | + | |
- | [ `stat -c %s $1` -gt 1000 ] && echo TRUE | + | |
- | echo FALSE | + | |
- | return 0 | + | |
- | } | + | |
- | ### обязательно через промежуточную переменную иначе тоже set -e потеряется | + | |
- | ### не будет работать [ "$( check_is_oversized /tmp34/11.log )" = TRUE ] | + | |
- | ret=$( check_is_oversized /tmp34/11.log ) | + | |
- | [ "$ret" = TRUE ] && echo "файл слишком большой" | + | |
- | ### Из минусов такого подхода это запуск сабшела и небольшой оверхед, | + | |
- | ### поэтому при массовых(10000) вызовах функции приходится ее использовать с потерей контроля ошибок и обычным return | + | |
- | ### или можно использовать глобальную переменную ERRNO= или глобальную FNAME_RET= | + | |
- | ### или работать процедурно через eval: funct_search txt1 txt2 rettxt1 funct_search () { eval "$3"='$value' } | + | |
- | </code>\\ | + | |
- | <color #ed1c24>Плохо использовать</color> | + | |
- | Такую конструкцию для запуска вложенных функций-команд с отловом кода возврата и без потери set -e, тк не удовлетворяет правилу skill-1 и pl7 | + | |
- | <code bash> | + | |
- | ( set +e; (set -e; fun1; ); ret=$?; ); | + | |
- | </code> | + | |
- | \\ | + | |
- | <color #ed1c24>Проблема set -u иногда непривычен для входящих параметров:</color>\\ | + | |
- | <code bash> | + | |
- | src=$1 # если $1 не передан, то программа упадет | + | |
- | </code> | + | |
- | <color #22b14c>Решение, можно использовать default значения переменных:</color> | + | |
- | <code bash> | + | |
- | echo ${1:-defaulttxt} | + | |
- | </code> | + | |
- | <code bash> | + | |
- | [ "${1:---help}" = '--help' ] && { echo 'Example: $0 src dst'; exit 0; } | + | |
- | </code> | + | |
- | Или учитывая правило skill -1 надо делать так:\\ | + | |
- | <code bash> | + | |
- | [ "${1:-}" = '' -o "${1:-}" = '--help' ] && { echo 'Example: $0 src dst'; exit 0; } | + | |
- | или | + | |
- | my_src="${1:-}" | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash003 ===== | + | |
- | + | ||
- | **Нельзя использовать || true в условных выражениях.**\\ | + | |
- | + | ||
- | <hidden Почему...>Это миф что без них не работает и все падает при set -e.\\ | + | |
- | Все отлично работает, если return 0 в конце каждой функции и exit 0 в конце каждого файла.</hidden> | + | |
- | <hidden Пример...> | + | |
- | <color #ed1c24>Как не надо делать:</color> | + | |
- | <code bash> | + | |
- | funct1(){ | + | |
- | [ a = b ] && echo Привет || true | + | |
- | } | + | |
- | </code> | + | |
- | <color #22b14c>Как надо делать:</color> | + | |
- | <code bash> | + | |
- | funct1(){ | + | |
- | [ a = b ] && echo Привет | + | |
- | return 0 | + | |
- | } | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash004 ===== | + | |
- | + | ||
- | **Нельзя использовать else true в условных выражениях**\\ | + | |
- | см strongbash003 | + | |
- | + | ||
- | ===== strongbash005 ===== | + | |
- | **В конце каждой функции должен быть return 0 или return $ret или exit 0 или exit $ret**\\ | + | |
- | <hidden Почему...>Это обязательно, чтобы не передать случайно неявное и нежелаемое значение последнего if, вместо явного.</hidden> | + | |
- | <hidden Пример...> | + | |
- | <color #ed1c24>Плохо:</color> | + | |
- | <code bash> | + | |
- | prepare_text(){ | + | |
- | local text="${1:-}" | + | |
- | [ "$text" = "" ] && echo "Привет" | + | |
- | ### "вот здесь мы отловили код возврата и не должны упасть, но мы упадем сразу после выхода из функции" | + | |
- | ### тк вернется последний код возврата | + | |
- | }; | + | |
- | arg1=$( prepare_text "555" ) | + | |
- | echo $arg1 | + | |
- | </code> | + | |
- | + | ||
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | prepare_text(){ | + | |
- | local text="${1:-}" | + | |
- | [ "$text" = "" ] && echo "Привет" | + | |
- | return 0 ### мы всегда явно задаем, что мы хотим вернуть либо return 0 либо return $ret | + | |
- | }; | + | |
- | arg1=$( prepare_text "555" ) | + | |
- | echo $arg1 | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash006 ===== | + | |
- | **В начале каждого файла должен быть echo START и EXAMPLE\\ | + | |
- | В конце каждого файла должны быть echo SUCCESS и exit 0 :\\ ** | + | |
- | Для продвинутых разработчиков лучше использовать в начале скрипт: include ::carbon.sys | + | |
- | <hidden Почему...> | + | |
- | - exit 0 для того чтобы избежать случайный код возврата. | + | |
- | - EXAMPLE использования важен, чтобы программа могла жить в будущем и не была выборошена. | + | |
- | - echo START echo SUCCESS делает удобным использование и чтение логов. | + | |
- | - include ::carbon.sys позволит автоматически деалать echo START echo SUCCESS echo FAIL sys::usage "$@" ### --help EXAMPLE и показывает callstack при падении | + | |
- | </hidden> | + | |
- | <hidden Пример...> | + | |
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | #!/bin/bash | + | |
- | echo "$0 $@ [$$] START" >&2 | + | |
- | [ "${1:---help}" = '--help' ] && { echo Example: $0 src dst; exit 0; } | + | |
- | cmd1 | + | |
- | cmd2 | + | |
- | cmd3 | + | |
- | echo "$0 $@ [$$] SUCCESS" >&2 | + | |
- | exit 0 | + | |
- | </code> | + | |
- | <color #22b14c>Хорошо если много exit 0 в программе</color> | + | |
- | <code bash> | + | |
- | #!/bin/bash | + | |
- | echo "$0 $@ [$$] START" >&2 | + | |
- | [ "${1:---help}" = '--help' ] && { echo Example: $0 src dst; exit 0; } | + | |
- | ARGV="$@" | + | |
- | __exit(){ | + | |
- | local ret=$1 | + | |
- | [ "$ret" = 0 ] && echo "$0 $ARGV [$$] SUCCESS" >&2 \ | + | |
- | || echo "$0 $ARGV [$$] FAILED" >&2 | + | |
- | exit $ret | + | |
- | } | + | |
- | + | ||
- | cmd1 | + | |
- | cmd2 || __exit 0 | + | |
- | cmd3 | + | |
- | echo "$0 $@ [$$] SUCCESS" >&2 | + | |
- | exit 0 | + | |
- | </code> | + | |
- | + | ||
- | <color #22b14c>Хорошо для продвинутых и для карбон софт:</color> | + | |
- | <code bash> | + | |
- | #!/bin/bash | + | |
- | source ::carbon.sys | + | |
- | sys::usage "$@" | + | |
- | ### --help Example: vm_backup src node2 | + | |
- | cmd1 | + | |
- | cmd2 | + | |
- | cmd3 | + | |
- | exit 0 | + | |
- | </code> | + | |
- | </hidden> | + | |
- | + | ||
- | <hidden Решение проблем...> | + | |
- | <color #22b14c>Хорошо для тихих файлов:</color> | + | |
- | <code bash> | + | |
- | # echo "$0 START">&2 | + | |
- | # echo "$0 SUCCESS">&2 | + | |
- | </code> | + | |
- | + | ||
- | + | ||
- | <color #22b14c>Или для тихих файлов с carbon.sys:</color> | + | |
- | <code bash> | + | |
- | __SILENT=TRUE | + | |
- | source ::carbon.sys | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash007 ===== | + | |
- | + | ||
- | **В конце каждого файла должен быть exit 0 или для библиотек: # exit 0.** | + | |
- | <hidden Почему...> | + | |
- | Мы должны явно задавать все легальные выходы из программы аналогично как с функциями.\\ | + | |
- | exit 0 в отличии от exit $ret дополнительно защитит от будущих ошибок при рефакторинге кода.\\ | + | |
- | </hidden> | + | |
- | <hidden Пример...> | + | |
- | <color #22b14c>Хорошо</color> | + | |
- | <code bash> | + | |
- | #!/bin/bash | + | |
- | echo "$0 $@ [$$] START" >&2 | + | |
- | cmd1 | + | |
- | [ $sz -gt 110 ] && { echo "Ошибка sz=$sz"; exit 2; } | + | |
- | cmd2 | + | |
- | cmd3 | + | |
- | echo "$0 $@ [$$] SUCCESS" >&2 | + | |
- | exit 0 | + | |
- | </code> | + | |
- | <color #ed1c24>Плохо exit $ret тк в будущем при рефакторинге может появиться неоднозначности</color> | + | |
- | <code bash> | + | |
- | #!/bin/bash | + | |
- | echo "$0 $@ [$$] START" >&2 | + | |
- | cmd1 | + | |
- | [ $sz -gt 110 ] && { echo "Ошибка sz=$sz"; exit 2; } | + | |
- | cmd2 | + | |
- | cmd3 || ret=$? | + | |
- | echo "$0 $@ [$$] SUCCESS" >&2 | + | |
- | exit $ret | + | |
- | </code> | + | |
- | + | ||
- | <color #22b14c>Хорошо. Если Ваш файл должен возвращать разные коды легально, то нужно это сделать явно : | + | |
- | </color> | + | |
- | <code bash> | + | |
- | #!/bin/bash | + | |
- | echo "$0 $@ [$$] START" >&2 | + | |
- | cmd1 | + | |
- | ret=1 | + | |
- | ret=4 | + | |
- | ret=2 | + | |
- | echo "$0 $@ [$$] SUCCESS" >&2 | + | |
- | [ $ret != 0 ] && exit $ret | + | |
- | exit 0 | + | |
- | </code> | + | |
- | </hidden> | + | |
- | + | ||
- | ===== strongbash008 ===== | + | |
- | + | ||
- | **Нужно использовать shebang #!/bin/bash и даже в include**\\ | + | |
- | **Не нужно использовать shebang переносимости <del>#!/usr/bin/env bash</del>** | + | |
- | <hidden Почему...> | + | |
- | 1. Позволит проверять include на crab_syntax\\ | + | |
- | 2. #!/usr/bin/env не нужно использовать для /bin/bash тк 99,999 ОС linux bash лежит в /bin/bash и только если Вы хотите использовать код на не linux, тогда возможно потребуется /usr/bin/env\\ | + | |
- | </hidden> | + | |
- | <hidden Пример...> | + | |
- | <color #ed1c24>Плохо</color> | + | |
- | <code bash> | + | |
- | #!/usr/bin/env bash | + | |
- | </code> | + | |
- | <color #22b14c>Хорошо</color> | + | |
- | <code bash> | + | |
- | #!/bin/bash | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash009 ===== | + | |
- | + | ||
- | **Нельзя использовать команду let.** | + | |
- | <hidden Почему...> | + | |
- | Так как она дает ошибку при переходе через 0, let i=1-1 упадет скрипт. | + | |
- | </hidden> | + | |
- | <hidden Пример...> | + | |
- | Плохо: | + | |
- | <code bash> | + | |
- | let c=a-b | + | |
- | </code> | + | |
- | Хорошо:\\ | + | |
- | <code bash> | + | |
- | c=$((a-b)) | + | |
- | </code> | + | |
- | </hidden> | + | |
- | + | ||
- | ===== strongbash010 warning TODO ===== | + | |
- | **Не рекомендуется использовать переменные, особенно глобальные, для временных файлов, если эта переменная используется менее 5 раз.**\\ | + | |
- | <del>$TMP_FILE=$( mktemp /tmp/tmp_file.XXXX )</del> | + | |
- | <hidden Почему...> | + | |
- | - Это усложняет чтение кода и понимание кода, файл сам по себе есть переменная и временная ссылка на него редко оправдана.\\ | + | |
- | - Плохо если приходится искать далеко вверху по коду, что это за переменная, и что там в этом файле.\\ | + | |
- | - mktmp это тоже плохо так как бессмысленно.\\ | + | |
- | - Имя временного файла должно содержать в себе имя исполняемой программы и ее PID обязательно.\\ | + | |
- | </hidden> | + | |
- | + | ||
- | <hidden Пример...> | + | |
- | <color #ed1c24>Плохо тк используется мало и глобальная переменная:</color> | + | |
- | <code bash> | + | |
- | $TMP_FILE_CAT_FROM=/tmp/${binname}_tmpls.$$ или $TMP_FILE_CAT_FROM=$( mktemp /tmp/tmpls.XXXX ) | + | |
- | ls /var/lib > $TMP_FILE_CAT_FROM | + | |
- | while read f; do | + | |
- | echo $f | + | |
- | done < $TMP_FILE_CAT_FROM | + | |
- | rm -f $TMP_FILE_CAT_FROM | + | |
- | </code> | + | |
- | <color #ed1c24>Плохо тк используется далеко от объявления и глобальная переменная:</color> | + | |
- | <color #ed1c24>(Иногда это нормально для временных каталогов)</color> | + | |
- | <code bash> | + | |
- | $TMP_FILE_CAT_FROM=/tmp/${binname}_tmpls.$$ или $TMP_FILE_CAT_FROM=$( mktemp /tmp/tmpls.XXXX ) | + | |
- | код1 | + | |
- | код2 | + | |
- | код3 | + | |
- | код4 | + | |
- | код5 | + | |
- | код6 | + | |
- | ls /var/lib > $TMP_FILE_CAT_FROM | + | |
- | while read f; do | + | |
- | echo $f | + | |
- | done < $TMP_FILE_CAT_FROM | + | |
- | ls /var/lib > $TMP_FILE_CAT_FROM | + | |
- | while read f1; do | + | |
- | echo $f1 | + | |
- | done < $TMP_FILE_CAT_FROM | + | |
- | ls /var/lib > $TMP_FILE_CAT_FROM | + | |
- | while read f2; do | + | |
- | echo $f2 | + | |
- | done < $TMP_FILE_CAT_FROM | + | |
- | rm -f $TMP_FILE_CAT_FROM | + | |
- | </code> | + | |
- | + | ||
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | ls /var/lib > /tmp/myname_tmpls.$$ | + | |
- | while read f; do | + | |
- | echo $f | + | |
- | done < /tmp/myname_tmpls.$$ | + | |
- | rm -f /tmp/myname_tmpls.$$ | + | |
- | </code> | + | |
- | + | ||
- | + | ||
- | <color #22b14c>Хорошо объявили и сразу используем много раз:</color> | + | |
- | <code bash> | + | |
- | $tmpls=/tmp/${binname}_tmpls.$$ или $tmpls=/tmp/${binname}_tmpls.$$.$(RANDOM) или $tmpls=$( mktemp /tmp/${binname}_tmpls.$$.XXXX ) | + | |
- | ls /var/lib > $tmpls | + | |
- | while read f; do | + | |
- | echo $f | + | |
- | done < $tmpls | + | |
- | echo ls /var/lib2 > $tmpls | + | |
- | cat lilo.conf > $tmpls | + | |
- | if grep ttt $tmpls; then | + | |
- | echo ddd | + | |
- | fi | + | |
- | rm -f $tmpls | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash011 ===== | + | |
- | **Максимальный indent вложенность 5**\\ | + | |
- | <hidden Почему...> | + | |
- | Большая вложенность всегда приводит к большой вложенности мысли при чтении кода, это неудобно и приводит к ошибкам.\\ | + | |
- | Используйте ранний return или сделайте подфункции, или case и тп.\\ | + | |
- | Не используйте большие if then else и мозг будет занят 5-ю уровнями вложенности.\\ | + | |
- | </hidden> | + | |
- | + | ||
- | + | ||
- | ===== strongbash012 ===== | + | |
- | **Команды | и || и && при переносе длинных строк** \\ | + | |
- | **требуется вынести на следующую строку и поставить tab.** | + | |
- | <hidden Почему...> | + | |
- | Это повышает читаемость кода и делает явным, что был перенос и это продолжение предыдущей строки. | + | |
- | </hidden> | + | |
- | <hidden Пример...> | + | |
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | cat 123 | grep 123 | sed | cat | grep \ | + | |
- | | while read t; do echo 123; done \ | + | |
- | </code> | + | |
- | </hidden> | + | |
- | + | ||
- | ===== strongbash013 ===== | + | |
- | **Ставьте пробел после #**\\ | + | |
- | **Можно используйте без пробелов #} и #{ для исправления ошибок crab_indent.** | + | |
- | + | ||
- | ===== strongbash014 ===== | + | |
- | **Максимальный размер функции 64 строки, а лучше 32.** | + | |
- | <hidden Почему...> | + | |
- | Каждая функция это смысловая единица которая должна помещаться в пару предложений человеческого языка, это правильное нативное удобное для мозга деления на смыслы.\\ | + | |
- | </hidden> | + | |
- | <hidden Что делать если больше...> | + | |
- | Выделите подфункции, часто вида: | + | |
- | <code bash> | + | |
- | __fname(){ | + | |
- | } | + | |
- | </code> | + | |
- | При этом не нужно создавать миллиард функций на каждое словосочетание тк любая функция эта новая сущность, а плодить сущности плохо.\\ | + | |
- | И если функция из 4 строк вызывается один раз, то врядли она нужна.\\ | + | |
- | </hidden> | + | |
- | + | ||
- | ===== strongbash015 ===== | + | |
- | **Слишком большой линейный файл больше 64 строк, выделите подфункции.** | + | |
- | <hidden Почему...> | + | |
- | Если в файле больше 64 строк, значит однозначно есть команды которые делают похожие вещи и правильней их объединить в функции.\\ | + | |
- | </hidden> | + | |
- | ===== strongbash016 ===== | + | |
- | + | ||
- | **Уберите конечные space, tab.**\\ | + | |
- | **В конце файла должен стоять один enter.**\\ | + | |
- | + | ||
- | ===== strongbash017 ===== | + | |
- | + | ||
- | **Длина строки должна быть не более 100 символов. Используйте переносы '\'** | + | |
- | <hidden Почему...> | + | |
- | Человеческий мозг нормально воспринимает до 7-10 слов в одну ширину строки, если больше, то нужно делать перенос и желательно в точке новой подкоманды.\\ | + | |
- | Не нужно надеяться на встроенные псевдопереносы IDE средств, это совсем другое.\\ | + | |
- | </hidden> | + | |
- | <hidden Пример...> | + | |
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | echo "aaaaaaaaaaaaaaaaaaaaaa"\ | + | |
- | "bbbbbbbbbbbbbbbbbbb" | + | |
- | или | + | |
- | txt="aaaaaaaaaaaaaaaaaaaaaa"\ | + | |
- | "bbbbbbbbbbbbbbbbbbb" | + | |
- | или | + | |
- | echo "aaaaaaaaaaaaaaaaaaaaaa\ | + | |
- | bbbbbbbbbbbbbbbbbbb" | + | |
- | + | ||
- | iptables -t nat -I xge_pre -m addrtype ! --dst-type LOCAL \ | + | |
- | -m set ! --match-set xge_auth_list src \ | + | |
- | -m set ! --match-set xge_auth_list dst -j RETURN | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash018 ===== | + | |
- | + | ||
- | **Поставьте пробел перед символом перенаправления '>'** | + | |
- | <hidden Пример...> | + | |
- | <code bash> | + | |
- | echo "text" > file.txt | + | |
- | </code> | + | |
- | </hidden> | + | |
- | + | ||
- | ===== strongbash019 ===== | + | |
- | **Добавьте пример использования программы echo 'Example: myname src dst'** | + | |
- | <hidden Пример...> | + | |
- | <color #22b14c>Хорошо:</color> | + | |
- | <code bash> | + | |
- | [ "${1:---help}" = '--help' ] && { echo Example: myname src dst; exit 0; } | + | |
- | </code> | + | |
- | <color #22b14c>Хорошо при include ::carbon.sys</color> | + | |
- | <code bash> | + | |
- | sys::usage "$@" и | + | |
- | ### --help Example: myname src dst | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash020 TODO ===== | + | |
- | **Запрещены функции Log в утилитах**\\ | + | |
- | Запрещены любые LOG $(date) в утилитах, это можно только в демонах и в кроне | + | |
- | <code bash> | + | |
- | crab_logger {name_tool} &>>/var/log/name_tool.log | + | |
- | crab_exec -t timeout --daemon --log2 -u root --onlyonce --lock {name_tool} &>>/var/log/name_tool.log | + | |
- | # не утверждено как именно | + | |
- | </code> | + | |
- | + | ||
- | ===== strongbash021 TODO ===== | + | |
- | **Запрещено удалять tmp в trap EXIT.**\\ | + | |
- | создание временного файла это своего рода with file и его удаление это end with, чтоб было понтяно где он более не нужен аля область работы с файлом. И если файл остался в тмп то это признак, что есть ошибка в логике. | + | |
- | + | ||
- | ===== strongbash022 ===== | + | |
- | **1. Утилиты должны удалять за собой временные файлы**\\ | + | |
- | **2. Запрещено в утилитах передавать результаты через промежуточные файлы. Только errno stdout stderr.** | + | |
- | ** В исключительных случаях имя файла должно быть в argv или конфиг в argv.**\\ | + | |
- | Непонятно как проверять, что результаты идут через промежуточные файлы, пока проверяем, что чистится /tmp/\\ | + | |
- | + | ||
- | ===== strongbash023 ===== | + | |
- | **1. Нужно всегда использовать read <color #ed1c24>-r</color> f1 f2 **\\ | + | |
- | **2. А для чтения целой строки while <color #ed1c24>IFS=<nowiki>''</nowiki></color> read -r line; do**\\ | + | |
- | <hidden Почему...> | + | |
- | 1. read -r иначе все esc последовательности обработаются, это вряд ли Вы хотели.\\ | + | |
- | 2. IFS=<nowiki>''</nowiki> иначе начальные пробелы и табы потеряются и строка будет не та, что в оригинале.\\ | + | |
- | </hidden> | + | |
- | <hidden Пример...> | + | |
- | <color #ed1c24>Как не надо делать:</color> | + | |
- | <code bash> | + | |
- | read f1 f2 < /tmp/myfile | + | |
- | + | ||
- | while read line; do | + | |
- | echo "$f" | + | |
- | done < /tmp/myfile | + | |
- | </code> | + | |
- | <color #22b14c>Как надо делать:</color> | + | |
- | <code bash> | + | |
- | read -r f1 f2 < /tmp/myfile | + | |
- | while IFS='' read -r line; do | + | |
- | echo "$f" | + | |
- | done < /tmp/myfile | + | |
- | </code> | + | |
- | </hidden> | + | |
- | + | ||
- | ===== strongbash024 ===== | + | |
- | ** 1. Запрещено rm -rf "/$dir" "./$dir" "$dir*" "$dir/*" "$dir/" **\\ | + | |
- | ** 2. rm -rf всегда с –one-file-system**\\ | + | |
- | + | ||
- | <hidden Почему...> | + | |
- | 1. Это приводит к удалению незадуманных файлов если переменная пустая\\ | + | |
- | 2. При работе с контейнерами и виртуалками, очень велик риск удалить их rootfs, а используются они сейчас повсеместно.\\ | + | |
- | </hidden> | + | |
- | <hidden Пример...> | + | |
- | <color #ed1c24>Как не надо делать:</color> | + | |
- | <code bash> | + | |
- | rm -rf "/$dir" | + | |
- | rm -rf "./$dir" | + | |
- | rm -rf "$dir*" | + | |
- | rm -rf "$dir/*" | + | |
- | rm -rf "$dir/" | + | |
- | </code> | + | |
- | <color #22b14c>Как надо делать:</color> | + | |
- | <code bash> | + | |
- | rm -rf -one-file-system "$dir" | + | |
- | rm -rf -one-file-system "/var/cache/mydir/*" | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash025 ===== | + | |
- | **"do" и "then" НЕ делаем c новой строки**\\ | + | |
- | <hidden Почему...>В большинстве языков принято в одной строке поэтому большинству так привычней.\\ | + | |
- | </hidden> | + | |
- | <hidden Пример...> | + | |
- | <color #ed1c24>Как не надо делать:</color> | + | |
- | <code bash> | + | |
- | if true; | + | |
- | then | + | |
- | echo true | + | |
- | fi | + | |
- | while read t; | + | |
- | do | + | |
- | echo $t | + | |
- | done | + | |
- | </code> | + | |
- | <color #22b14c>Как надо делать:</color> | + | |
- | <code bash> | + | |
- | if true; then | + | |
- | echo true | + | |
- | fi | + | |
- | while read t; do | + | |
- | echo $t | + | |
- | done | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash026 ===== | + | |
- | **Нельзя использовать голые скобки для assert и тп [ a = b ]**\\ | + | |
- | + | ||
- | <hidden Почему...>Это противоречит явности кода и человекочитаемости ошибок.\\ | + | |
- | Это использование set -e для основной логики, что запрещено.\\ | + | |
- | </hidden>\\ | + | |
- | <hidden Пример...> | + | |
- | <color #ed1c24>Как не надо делать:</color> | + | |
- | <code bash> | + | |
- | [ "$fname" = "" ] | + | |
- | </code> | + | |
- | <color #22b14c>Как надо делать:</color> | + | |
- | <code bash> | + | |
- | [ "$fname" = "" ] && { echo 'Укажите имя файла'; exit 1; } | + | |
- | </code> | + | |
- | <color #22b14c>Как надо делать:</color> | + | |
- | <code bash> | + | |
- | # Или не проверять все подряд, тк все на свете проверить анрил, само упадет по set -e | + | |
- | </code> | + | |
- | </hidden> | + | |
- | ===== strongbash027 TODO ===== | + | |
- | нельзя использовать конструкции вида [ a = b ] || cmd1 кроме утвержденного сахара\\ | + | |
- | + | ||
- | ===== strongbash028 only warning TODO ===== | + | |
- | **Стараться не использовать cmd1 || cmd2 || cmd3** лучше проверять окружение и вызвать сразу нужное If centos if ubintu if dir \\ | + | |
- | **Пример такой сахар разрешен, как разрешать отдельные ошибки команд не заглушая остальные**\\ | + | |
- | <code bash> | + | |
- | Если бы не существовало команды git status | + | |
- | error=$( git commit -m 'qqqqqqqqqqq' 2>&1 ) \ | + | |
- | || echo "$error" | grep -qm1 'nothing to commit' \ | + | |
- | || { echo "$error"; exit 1; } | + | |
- | или | + | |
- | error=$( git commit -m 'qqqqqqqqqqq' 2>&1 ) \ | + | |
- | || [[ $error != *"nothing to commit"* ]] && { echo "$error"; exit 1; } | + | |
- | </code> | + | |
- | **НО правильнее** | + | |
- | <code bash> | + | |
- | if ! git status | grep -qm1 'nothing to commit'; then | + | |
- | git commit -m 'qqqqqqqqqqq' | + | |
- | fi | + | |
- | </code> | + | |
- | + | ||
- | ===== strongbash029 ===== | + | |
- | + | ||
- | **Если мы берем stdout от функции мы обязаны прописать в первой строке set -e тк он снимается** | + | |
- | ===== strongbash030 ===== | + | |
- | **Нельзя вызывать функцию внутри if fname и fname&& и fname||** | + | |
- | + | ||
- | ===== TODO добавить в инструкцию как программировать на баше ===== | + | |
- | **TODO** Описать виды функций __funct funct и виды переменных ARG CONF GLOBAL local и namespace сабшелы\\ | + | |
- | **TODO** | + | |
- | В файл примера | + | |
- | <code bash> | + | |
- | iptables -t nat -I xge_pre -m addrtype ! --dst-type LOCAL \ | + | |
- | -m set ! --match-set xge_auth_list src \ | + | |
- | -m set ! --match-set xge_auth_list dst -j RETURN | + | |
- | </code> | + | |
- | + | ||
- | Добавить в описание правил что grep -qm1 может уронить левую часть если это долгая команда и отгрепается первая строка пример | + | |
- | <code bash> | + | |
- | ( set -euEo pipefail; for((i=0;i<10;i++)); do echo q; sleep 1; done | grep -q q ); echo $? | + | |
- | </code> | + | |
- | + | ||
- | Добавить в описание | + | |
- | для всего есть подходящие инструменты, bash отличный скриптовый язык, позволяющий делать мощные программы написав немного кода по вызову готовых утилит. | + | |
- | И при этом этот код легко сопровождать и разбирать потом без программиста, админам и саппорту. | + | |
- | Есть куча кейсов где программы на баше в 10 раз меньше, чем на питоне при этом надежней и написана за гораздо меньшее время. | + | |
- | + | ||
- | ~~DISCUSSION~~ | + |