Strongbash Коменты
Различия
Здесь показаны различия между двумя версиями данной страницы.
корзина:strongbash_коменты [08.04.2018 16:38] admin [strongbash002] |
корзина: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 -eu или include ::carbon.sys** \\ | ||
- | **Категорически нельзя ставить <del>set -o pipefail</del> на весь файл см strongbash034_6** | ||
- | <WRAP center round important>ВНИМАНИЕ set -eu это самое главное правило, оно дает режим strong.\\ | ||
- | Позволяет отлавливать и исключать неявное и непредсказуемое поведение программы, а также быстрей отлаживать.\\ | ||
- | </WRAP> | ||
- | <WRAP center round important> | ||
- | ОЧЕНЬ БОЛЬШОЕ ВНИМАНИЕ set -eu используется для того, чтобы отловить неизвестные **будущие** ошибки программы и не дать ей выполняться далее.\\ | ||
- | set -eu **не должен** использоваться для реализации какой либо логики основного алгоритма, другими словами программа с set -eu и без set -eu должна работать одинаково в основной логике.\\ | ||
- | </WRAP> | ||
- | <hidden Что включает...> | ||
- | set -e падать если есть необработанные ошибки, а точнее необработанный код возврата.\\ | ||
- | set -u падать если используется неинициализированная переменная.\\ | ||
- | set -o pipefail падать в ERR если в одной из пайп команд произошла ошибка, но это приводит к ошибкам при работе grep -q grep -m1 head -n1 tail и подобные команды посылают sigpipe в левую половину, что приводит падению левой части pipe и нарушению логики скрипта. Менять привычные конструкции нерационально, поэтому set -o pipefail нужно использовать только в отдельных участках программы и выключать после использования set +o pipefail.\\ | ||
- | set -E наследовать trap ERR в функциях, что важно для debug.\\ | ||
- | </hidden> | ||
- | <hidden Как не надо делать...> | ||
- | <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(){ | ||
- | git log > /tmp/myqqq.txt | ||
- | grep qqq /tmp/myqqq.txt | ||
- | # ^^^ не надо надеяться на то, что здесь упадет и функция вернет не ноль, здесь не остановится выполнение функции. | ||
- | # такой подход технически невозможен ни с ни без set -e | ||
- | return 0 | ||
- | } | ||
- | ### это работать не будет | ||
- | func1 && echo "qqq найден" | ||
- | </code> | ||
- | |||
- | <color #ed1c24>Плохо использовать стандартный кода возврата функций тк это отключает set -e:</color> | ||
- | <code bash> | ||
- | set -e | ||
- | func1(){ | ||
- | set -e ### здесь писать бесполезно опция работать не будет тк мы берем код возврата функции | ||
- | git log > /tmp/myqqq.txt ### если здесь ошибка, то мы ее не поймаем и не упадем тк set -e отключится | ||
- | grep qqq /tmp/myqqq.txt && return 0 || return $? | ||
- | } | ||
- | ### это будет работать, но потенциальные ошибки функции не приведут к падению скрипта и код становится менее надежным | ||
- | func1 && echo "qqq найден" | ||
- | </code> | ||
- | |||
- | <color #22b14c>Хорошо:</color> | ||
- | <code bash> | ||
- | func1(){ | ||
- | set -e ### требуется указать еще раз чтоб работало ret=$( func1 ) | ||
- | git log > /tmp/myqqq.txt ### если здесь ошибка, то мы упадем ура | ||
- | grep qqq /tmp/myqqq.txt && echo TRUE || echo FALSE ### следует понимать, что ошибки grep не уронят скрипт, а приведут к echo FALSE | ||
- | return 0 | ||
- | } | ||
- | ret="$(func1)" | ||
- | [ "$ret" = "TRUE" ] && echo "qqq найден" | ||
- | </code> | ||
- | <color #22b14c>Для паранои:</color> | ||
- | <code bash> | ||
- | func1(){ | ||
- | set -e ### требуется указать еще раз чтоб работало ret=$( func1 ) | ||
- | git log > /tmp/myqqq.txt ### если здесь ошибка, то мы упадем | ||
- | ret="$(grep qqq /tmp/myqqq.txt)" ### этим мы отловим ошибки grep b и упадем | ||
- | [ -n "$ret" ] && echo TRUE || echo FALSE | ||
- | return 0 | ||
- | } | ||
- | ret="$(func1)" | ||
- | [ "$ret" = "TRUE" ] && echo "qqq найден" | ||
- | </code> | ||
- | |||
- | </hidden> | ||
- | <hidden Проблемы и решения set -e в условиях и функциях> | ||
- | <color #ed1c24>Проблема, если мы вызываем функцию и анализируем ее код возврата\\ | ||
- | в ней перестает работать set -e :</color>\\ | ||
- | <code bash> | ||
- | set -e | ||
- | check_is_oversized(){ | ||
- | set -e ### здесь бесполезно добавлять тк берется код возврата функции | ||
- | size="$( stat -c %s $1 )" | ||
- | [ "$size" -gt 1000 ] && return 0 # если программа stat упадет? то ошибки не будет тк set -e здесь выключен уже | ||
- | # в итоге вернется return 1 даже если stat упал | ||
- | return 1 | ||
- | } | ||
- | 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 ) | ||
- | size="$( stat -c %s $1 )" ### если файла нет мы упадем ура | ||
- | [ "$size" -gt 1000 ] && echo TRUE || echo FALSE | ||
- | return 0 | ||
- | } | ||
- | |||
- | ### так нельзя тк любое условие выключает set -e [ "$( check_is_oversized /tmp34/11.log )" = TRUE ] | ||
- | |||
- | ret=$( check_is_oversized /tmp34/11.log ) ### обязательно через промежуточную переменную иначе тоже set -e потеряется | ||
- | [ "$ret" = TRUE ] && echo "файл слишком большой" | ||
- | |||
- | ### Из минусов такого подхода это запуск сабшела и небольшой оверхед, | ||
- | ### поэтому при массовых(10000) вызовах функции приходится ее использовать с потерей контроля ошибок и обычным return | ||
- | ### или можно использовать глобальную переменную ERRNO= или глобальную FNAME_RET= | ||
- | ### или работать процедурно через eval: funct_search txt1 txt2 rettxt1 funct_search () { eval "$3"='$value' } | ||
- | </code>\\ | ||
- | <color #ed1c24>Проблема set -e не работает в условиях:</color>\\ | ||
- | <code bash> | ||
- | # если команда ip упадет мы об этом не узнаем( хотя иногда это может быть и неважно для логики). | ||
- | if ip r g 1 | grep 192.168.1.1; then | ||
- | echo "Шлюз указан верно" | ||
- | fi | ||
- | </code> | ||
- | <color #22b14c>Решение, можно использовать промежуточную переменную:</color> | ||
- | <code bash> | ||
- | ip_ret="$( ip r g 1 )" | ||
- | if echo "$ip_ret" | grep 192.168.1.1; then | ||
- | echo "Шлюз указан верно" | ||
- | fi | ||
- | </code> | ||
- | <color #22b14c>Решение2, можно использовать промежуточную переменную:</color> | ||
- | <code bash> | ||
- | ip_ret="$( ip r g 1 )" || true | ||
- | if [ -z "$ip_ret" ] then | ||
- | echo "Не можем получить шлюз" | ||
- | fi | ||
- | </code> | ||
- | </hidden> | ||
- | <hidden Непривычность set -u > | ||
- | <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 и показывает 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 Info: Автоматически создавать и коммитить все файлы в каталоге | ||
- | ### --help Usage: auto_git.sh [install] /var/www/html | ||
- | ### --help Example: auto_git.sh /var/www/html | ||
- | sys::arg_parse "$@" | ||
- | 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 'Info: Usage: Example: myname src dst'** | ||
- | <hidden Пример...> | ||
- | <color #22b14c>Хорошо:</color> | ||
- | <code bash> | ||
- | if [ "${1:---help}" = '--help' ]; then | ||
- | echo 'Info: Автоматически создавать и коммитить все файлы в каталоге' | ||
- | echo 'Usage: auto_git.sh [install] /var/www/html' | ||
- | echo 'Example: auto_git.sh /var/www/html' | ||
- | exit 0 | ||
- | fi | ||
- | </code> | ||
- | <color #22b14c>Хорошо при include ::carbon.sys</color> | ||
- | <code bash> | ||
- | sys::usage "$@" и | ||
- | ### --help Info: Автоматически создавать и коммитить все файлы в каталоге | ||
- | ### --help Usage: auto_git.sh [install] /var/www/html | ||
- | ### --help Example: auto_git.sh /var/www/html | ||
- | sys::arg_parse "$@" | ||
- | </code> | ||
- | </hidden> | ||
- | ===== strongbash020 ===== | ||
- | **Запрещены функции 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 ===== | ||
- | **1. Запрещено удалять tmp в trap EXIT.**\\ | ||
- | **2. Не рекомендуется использовать trap EXIT.**\\ | ||
- | <hidden Почему...> | ||
- | 1. Создание временного файла это своего рода with file и его удаление это end with, чтоб было понтяно где он более не нужен аля область работы с файлом. И если файл остался в тмп, то это признак что есть ошибка в логике. | ||
- | |||
- | 2. Использование trap EXIT тк он скрывает ошибки программ и никто о них не узнает, ломает честную логику алгоритма, это вредный хак. Должен быть явный вызов error_exit() и тп. | ||
- | Если нужно удалить lock можно использовать trap TERM INT HUP, если lock остался после сбоя значит он и должен остаться, чтобы получить ошибки и разобраться со сбоем.\\ | ||
- | </hidden> | ||
- | ===== 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 ===== | ||
- | нельзя использовать конструкции вида [ a = b ] || cmd1 и cmd1 || cmd2 кроме утвержденного сахара\\ | ||
- | используйте: | ||
- | <code bash> | ||
- | [ a != b ] && cmd1 и | ||
- | if ! cmd1; then | ||
- | cmd2 | ||
- | fi | ||
- | </code> | ||
- | ===== strongbash028 ===== | ||
- | **Нельзя использовать 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||** | ||
- | |||
- | ===== strongbash031 ===== | ||
- | **нельзя использовать -EOF, используйте обычный EOF** | ||
- | |||
- | ===== strongbash03x todo ===== | ||
- | **usage скрипта и его описание должно располагаться в начале скрипта** | ||
- | |||
- | ===== strongbash032 todo ===== | ||
- | **1. все локальные переменные должны быть объявлены через local и быть малыми буквами\\ | ||
- | 2. все глобальные переменные используемые внутри функций должны быть большими буквами | ||
- | ** | ||
- | |||
- | ===== 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~~ |