===== Соглашение по строгому bash ===== TODO_OSV возможно выборочно проверять номера из https://github.com/koalaman/shellcheck ===== strongbash001 ===== **Обязательно должен быть выполнен crab_indent**. - indent делает код более читаемым - использование indent позволяет обнаруживать ошибки Конструкция **if-then** располагаются в одной строке.\\ Хорошо: if [ "$EUID" -ne "0" ]; then echo "Please run as root" fi Фигурная скобка **"{"** для определния тела функции должна быть в одной строке с функцией.\\ После переноса строки добавляем ****.\\ Хорошо: myfunc1() { commands... } Объявление функции без ключевого слова **function**.\\ Ключевое слово function было введено в ksh.\\ POSIX стандартизирует только синтаксис funcname().\\ Код в скобках с отступами.\\ Хорошо: myfunc1() { echo "Hello world" } Плохо: function myfunc1 { echo "Hello world" } Инструкция **case ** без оступов:\\ Хорошо: case "$variable" in "$condition1" ) command... ;; "$condition2" ) command... ;; esac \\ Хорошо: #!/bin/bash set -eu 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 ===== strongbash002 ===== **Должен быть установлен set -eu или include crab_sys.sh** \\ **Категорически нельзя ставить set -o pipefail на весь файл см strongbash034_6** ВНИМАНИЕ set -eu это самое главное правило, оно дает режим strong.\\ Позволяет отлавливать и исключать неявное и непредсказуемое поведение программы, а также быстрей отлаживать.\\ ОЧЕНЬ БОЛЬШОЕ ВНИМАНИЕ set -eu используется для того, чтобы отловить неизвестные **будущие** ошибки программы и не дать ей выполняться далее.\\ set -eu **не должен** использоваться для реализации какой либо логики основного алгоритма, другими словами программа с set -eu и без set -eu должна работать одинаково в основной логике.\\ 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.\\ Плохо делать ассерты вида:\\ [ $# = 3 ] Хорошо явно отлавливать и писать текстом пользователю о **заранее** известных ошибках:\\ [ $# != 3 ] && { echo 'Укажите 3 параметра'; exit 1; } Плохо **пытаться** использовать возможности set -e для формирования кода возврата функций: set -e func1(){ git log > /tmp/myqqq.txt grep qqq /tmp/myqqq.txt # ^^^ не надо надеяться на то, что здесь упадет и функция вернет не ноль, здесь не остановится выполнение функции. # такой подход технически невозможен ни с ни без set -e return 0 } ### это работать не будет func1 && echo "qqq найден" Плохо использовать стандартный кода возврата функций тк это отключает set -e: set -e func1(){ set -e ### здесь писать бесполезно опция работать не будет тк мы берем код возврата функции git log > /tmp/myqqq.txt ### если здесь ошибка, то мы ее не поймаем и не упадем тк set -e отключится grep qqq /tmp/myqqq.txt && return 0 || return $? } ### это будет работать, но потенциальные ошибки функции не приведут к падению скрипта и код становится менее надежным func1 && echo "qqq найден" Хорошо: 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 найден" Для паранои: 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 найден" Проблема, если мы вызываем функцию и анализируем ее код возврата\\ в ней перестает работать set -e :\\ 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 Решение, можно использовать echo TRUE вместо return 0 :\\ 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 } ### [ "$( check_is_oversized /tmp34/11.log )" = TRUE ] --- так нельзя тк любое условие полностью выключает set -e 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' } \\ Проблема set -e не работает в условиях:\\ # если команда ip упадет мы об этом не узнаем( хотя иногда это может быть и неважно для логики). if ip r g 1 | grep 192.168.1.1; then echo "Шлюз указан верно" fi Решение, можно использовать промежуточную переменную: ip_ret="$( ip r g 1 )" if echo "$ip_ret" | grep 192.168.1.1; then echo "Шлюз указан верно" fi Решение2, можно использовать промежуточную переменную: ip_ret="$( ip r g 1 )" || true if [ -z "$ip_ret" ] then echo "Не можем получить шлюз" fi Проблема set -u иногда непривычен для входящих параметров:\\ src=$1 # если $1 не передан, то программа упадет Решение, можно использовать default значения переменных: echo ${1:-defaulttxt} [ "${1:---help}" = '--help' ] && { echo 'Example: $0 src dst'; exit 0; } Или учитывая правило skill -1 сделать так:\\ [ "${1:-}" = '' -o "${1:-}" = '--help' ] && { echo 'Example: $0 src dst'; exit 0; } или my_src="${1:-}" ===== strongbash003 ===== **Нельзя использовать || true в условных выражениях.**\\ Это миф что без них не работает и все падает при set -e.\\ Все отлично работает, если return 0 в конце каждой функции и exit 0 в конце каждого файла. Как не надо делать: funct1(){ [ a = b ] && echo Привет || true } Как надо делать: funct1(){ [ a = b ] && echo Привет return 0 } ===== strongbash004 ===== **Нельзя использовать else true в условных выражениях**\\ см strongbash003 ===== strongbash005 ===== **В конце каждой функции должен быть return 0 или return $ret или exit 0 или exit $ret**\\ Это обязательно, чтобы не передать случайно неявное и нежелаемое значение последнего if, вместо явного $?. Плохо: prepare_text(){ local text="${1:-}" [ "$text" = "" ] && echo "Привет" ### "вот здесь мы отловили код возврата и не должны упасть, но мы упадем сразу после выхода из функции" ### тк вернется последний код возврата }; arg1=$( prepare_text "555" ) echo $arg1 Хорошо: prepare_text(){ local text="${1:-}" [ "$text" = "" ] && echo "Привет" return 0 ### мы всегда явно задаем, что мы хотим вернуть либо return 0 либо return $ret }; arg1=$( prepare_text "555" ) echo $arg1 ===== strongbash006 ===== **В начале каждого файла должен быть echo START и USAGE, INFO, EXAMPLE\\ В конце каждого файла должны быть echo SUCCESS и exit 0 :\\ ** Для продвинутых разработчиков лучше использовать в начале скрипт: include crab_sys.sh - exit 0 для того чтобы избежать случайный код возврата. - USAGE, INFO, EXAMPLE использования важен, чтобы программа могла жить в будущем и не была выброшена. - echo START echo SUCCESS делает удобным использование и чтение консоли и логов. - include crab_sys.sh позволит автоматически деалать echo START echo SUCCESS echo FAIL sys::usage "$@" # --help и показывает callstack при падении Хорошо: #!/bin/bash echo "$0 $@ [$$] START" >&2 if [ "${1:---help}" = '--help' ]; then echo "Usage: $0 src dst" echo "Example: $0 /tmp/12 /tmp/14.gz" echo "Info: Копирование со сжатием" exit 0 fi cmd1 cmd2 cmd3 echo "$0 $@ [$$] SUCCESS" >&2 exit 0 Хорошо сделать функцию __exit(), если много exit 0 в программе #!/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 Хорошо для продвинутых и для карбон софт: #!/bin/bash source crab_sys.sh sys::usage "$@" ### --help Info: Автоматически создавать и коммитить все файлы в каталоге ### --help Usage: auto_git.sh [install] /var/www/html ### --help Example: auto_git.sh /var/www/html ### Пример sys::arg_parse "txt1" "txt2" "--named1=a1" "--named2=a2" "--forced" ### Пример echo "${ARGV[1]}==txt1 ${ARGV[2]}==txt2 ${ARG_NAMED1}==a1 ${ARGV_NAMED2}==a2 ${ARG_FORCED}==TRUE" sys::arg_parse "$@" cmd1 cmd2 cmd3 exit 0 Хорошо для тихих файлов: # echo "$0 START">&2 ### --help Info: Автоматически создавать и коммитить все файлы в каталоге ### --help Usage: auto_git.sh [install] /var/www/html ### --help Example: auto_git.sh /var/www/html # echo "$0 SUCCESS">&2 exit 0 Или для тихих файлов с crab_sys.sh: __SILENT=TRUE source crab_sys.sh ### --help Info: Автоматически создавать и коммитить все файлы в каталоге ### --help Usage: auto_git.sh [install] /var/www/html ### --help Example: auto_git.sh /var/www/html exit 0 ===== strongbash007 ===== **В конце каждого файла должен быть exit 0 или для библиотек: # exit 0.** Мы должны явно задавать все легальные выходы из программы аналогично как с функциями.\\ exit 0 в отличии от exit $ret дополнительно защитит от будущих ошибок при рефакторинге кода.\\ Хорошо #!/bin/bash echo "$0 $@ [$$] START" >&2 cmd1 [ $sz -gt 110 ] && { echo "Ошибка sz=$sz"; exit 2; } cmd2 cmd3 echo "$0 $@ [$$] SUCCESS" >&2 exit 0 Плохо exit $ret тк в будущем при рефакторинге может появиться неоднозначности #!/bin/bash echo "$0 $@ [$$] START" >&2 cmd1 [ $sz -gt 110 ] && { echo "Ошибка sz=$sz"; exit 2; } cmd2 cmd3 || ret=$? echo "$0 $@ [$$] SUCCESS" >&2 exit $ret Хорошо. Если Ваш файл должен возвращать разные коды легально, то нужно это сделать явно : #!/bin/bash echo "$0 $@ [$$] START" >&2 cmd1 ret=1 ret=4 ret=2 echo "$0 $@ [$$] SUCCESS" >&2 [ $ret != 0 ] && exit $ret exit 0 ===== strongbash008 ===== **Нужно использовать shebang #!/bin/bash и даже в include**\\ **Не нужно использовать shebang переносимости #!/usr/bin/env bash** 1. Позволит проверять include на crab_syntax\\ 2. #!/usr/bin/env не нужно использовать для /bin/bash тк 99,999 ОС linux bash лежит в /bin/bash и только если Вы хотите использовать код на не linux, тогда возможно потребуется /usr/bin/env\\ Плохо #!/usr/bin/env bash Хорошо #!/bin/bash ===== strongbash009 ===== **Нельзя использовать команду let.** Так как она дает ошибку при переходе через 0, let i=1-1 упадет скрипт. Плохо: let c=a-b Хорошо:\\ c=$((a-b)) ===== strongbash010 warning ===== **Не рекомендуется использовать переменные, особенно глобальные, для временных файлов, если эта переменная используется менее 5 раз.**\\ $TMP_FILE=$( mktemp /tmp/tmp_file.XXXX ) - Это усложняет чтение кода и понимание кода, файл сам по себе есть переменная и временная ссылка на него редко оправдана.\\ - Плохо если приходится искать далеко вверху по коду, что это за переменная, и что там в этом файле.\\ - mktemp это тоже плохо так как бессмысленно, если оч надо tt=/tmp/NAME_$((RANDOM)).$$ \\ - Имя временного файла должно содержать в себе имя исполняемой программы и ее PID обязательно для разбора оставшихся после падения файлов.\\ - Если файлов останется от падения слишком много, то скрипт не сможет работать тк пиды заполнятся и это нормально мы об этом узнаем. Либо можно сделать rm -f перед работой Плохо тк используется мало и глобальная переменная: $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 Плохо тк используется далеко от объявления и глобальная переменная: (Иногда это нормально для временных каталогов) $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 Хорошо локальное использование: ls /var/lib > /tmp/myname_tmpls.$$ while read f; do echo $f done < /tmp/myname_tmpls.$$ rm -f /tmp/myname_tmpls.$$ Хорошо объявили и сразу используем много раз: $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 ### todo http://opencarbon.ru/соглашения_кода:strongbash_коменты ===== strongbash011 ===== **Максимальный indent вложенность 5**\\ Большая вложенность всегда приводит к большой вложенности мысли при чтении кода, это неудобно и приводит к ошибкам.\\ Используйте ранний return или сделайте подфункции, или case и тп.\\ Не используйте большие if then else иначе мозг будет занят 5-ю уровнями вложенности.\\ ===== strongbash012 ===== **Команды | и || и && при переносе длинных строк** \\ **требуется вынести на следующую строку и поставить tab.** Это повышает читаемость кода и делает явным, что был перенос и это продолжение предыдущей строки. Хорошо: cat 123 | grep 123 | sed | cat | grep \ | while read t; do echo 123; done \ ===== strongbash013 ===== **Ставьте пробел после #**\\ **Можно используйте без пробелов #} и #{ для исправления ошибок crab_indent.** ===== strongbash014 ===== **Максимальный размер функции 64 строки, а лучше 32.** Каждая функция это смысловая единица которая должна помещаться в пару предложений человеческого языка, это правильное нативное удобное для мозга деления на смыслы.\\ Выделите подфункции, часто вида: __fname(){ } При этом не нужно создавать миллиард функций на каждое словосочетание тк любая функция эта новая сущность, а плодить сущности плохо.\\ И если функция из 4 строк вызывается один раз, то врядли она нужна.\\ ===== strongbash015 ===== **Слишком большой линейный файл больше 64 строк, выделите подфункции.** Если в файле больше 64 строк, значит однозначно есть команды которые делают похожие вещи или группу вещей и правильней их объединить в функции.\\ ===== strongbash016 ===== **Уберите конечные space, tab.**\\ **В конце файла должен стоять один enter.**\\ ===== strongbash017 ===== **Длина строки должна быть не более 100 символов. Используйте переносы '\'** Человеческий мозг нормально воспринимает до 7-10 слов в одну ширину строки, если больше, то нужно делать перенос и желательно в точке новой подкоманды.\\ Не нужно надеяться на встроенные псевдопереносы IDE средств, это совсем другое.\\ Хорошо: echo "aaaaaaaaaaaaaaaaaaaaaa"\ "bbbbbbbbbbbbbbbbbbb" или txt="aaaaaaaaaaaaaaaaaaaaaa"\ "bbbbbbbbbbbbbbbbbbb" или echo "aaaaaaaaaaaaaaaaaaaaaa\ bbbbbbbbbbbbbbbbbbb" или txt=\ "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 ===== strongbash018 ===== **Поставьте пробел перед символом перенаправления '>'** echo "text" > file.txt ===== strongbash019 ===== **Добавьте описание и пример использования программы echo 'Info: Usage: Example: myname src dst'** Хорошо: 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 Хорошо при include crab_sys.sh sys::usage "$@" и ### --help Info: Автоматически создавать и коммитить все файлы в каталоге ### --help Usage: auto_git.sh [install] /var/www/html ### --help Example: auto_git.sh /var/www/html sys::arg_parse "$@" ===== strongbash020 ===== **Запрещены функции Log в утилитах**\\ Запрещены любые LOG $(date) в утилитах, это можно только в демонах и в кроне 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 # не утверждено как именно ===== strongbash021 ===== **1. Запрещено удалять tmp в trap EXIT.**\\ **2. Не рекомендуется использовать trap EXIT.**\\ 1. Создание временного файла это своего рода with file и его удаление это end with, чтоб было понтяно где он более не нужен аля область работы с файлом. И если файл остался в тмп, то это признак что есть ошибка в логике. 2. Использование trap EXIT плохо тк он скрывает ошибки программ и никто о них не узнает, ломает честную логику алгоритма, это вредный хак. Должен быть явный вызов error_exit() и тп. Если нужно удалить lock можно использовать trap TERM INT HUP, если lock остался после сбоя значит он и должен остаться, чтобы получить ошибки и разобраться со сбоем.\\ ===== strongbash022 ===== **1. Утилиты должны удалять за собой временные файлы**\\ **2. Запрещено в утилитах передавать результаты через промежуточные файлы. Только errno stdout stderr.** ** В исключительных случаях имя файла должно быть в argv или конфиг в argv.**\\ Непонятно как проверять, что результаты идут через промежуточные файлы, пока проверяем, что чистится /tmp/\\ ===== strongbash023 ===== **1. Нужно всегда использовать read -r f1 f2 **\\ **2. А для чтения целой строки while IFS='' read -r line; do**\\ 1. read -r иначе все esc последовательности обработаются, это вряд ли то что Вы хотели.\\ 2. IFS='' иначе начальные пробелы и табы потеряются и строка будет не та, что в оригинале.\\ Как не надо делать: read f1 f2 < /tmp/myfile while read line; do echo "$f" done < /tmp/myfile Как надо делать: read -r f1 f2 < /tmp/myfile while IFS='' read -r line; do echo "$f" done < /tmp/myfile ===== strongbash024 ===== ** 1. Запрещено rm -rf "/$dir" "./$dir" "$dir*" "$dir/*" "$dir/" **\\ ** 2. rm -rf всегда с –one-file-system**\\ 1. Это приводит к удалению незадуманных файлов если переменная пустая\\ 2. При работе с контейнерами и виртуалками, очень велик риск удалить их rootfs, а используются они сейчас повсеместно.\\ Как не надо делать: rm -rf "/$dir" rm -rf "./$dir" rm -rf "$dir*" rm -rf "$dir/*" rm -rf "$dir/" Как надо делать: rm -rf -one-file-system "$dir" rm -rf -one-file-system "/var/cache/mydir/*" ===== strongbash025 ===== **"do" и "then" НЕ делаем c новой строки**\\ В большинстве языков принято в одной строке поэтому большинству так привычней.\\ Как не надо делать: if true; then echo true fi while read t; do echo $t done Как надо делать: if true; then echo true fi while read t; do echo $t done ===== strongbash026 ===== **Нельзя использовать голые скобки для assert и тп [ a = b ]**\\ Это противоречит явности кода и человекочитаемости ошибок.\\ Это использование set -e для основной логики, что запрещено.\\ \\ Как не надо делать: [ "$fname" = "" ] Как надо делать: [ "$fname" = "" ] && { echo 'Укажите имя файла'; exit 1; } Как надо делать: # Или не проверять все подряд, тк все на свете проверить анрил, само упадет по set -e ===== strongbash027 ===== нельзя использовать конструкции вида [ a = b ] || cmd1 и cmd1 || cmd2 кроме утвержденного сахара\\ используйте: [ a != b ] && cmd1 и if ! cmd1; then cmd2 fi ===== strongbash028 ===== **Нельзя использовать cmd1 || cmd2 || cmd3** лучше проверять окружение и вызвать сразу нужное If centos if ubintu if dir \\ **Пример такой сахар разрешен, как разрешать отдельные ошибки команд не заглушая остальные**\\ Если бы не существовало команды 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; } **НО правильнее** if ! git status | grep -qm1 'nothing to commit'; then git commit -m 'qqqqqqqqqqq' fi ===== strongbash029 ===== **Если мы берем stdout от функции мы обязаны прописать в первой строке Функции set -e тк он снимается** ===== strongbash030 ===== **Нельзя вызывать функцию внутри if fname и fname&& и fname||**\\ тк set -e перестанет работать, перевыставить не поможет ===== strongbash031 ===== **нельзя использовать -EOF, используйте обычный EOF** ===== strongbash032 ===== **usage скрипта и его описание должно располагаться в начале скрипта**\\ Чтобы при открытии файла сразу понять, что он делает. ===== strongbash033 ===== **1. Глобальная переменная должна быть в верхнем регистре, если используется в функциях.**\\ **2. Все локальные переменные в функциях должны быть объявлены как local.**\\ **2.1 Переменные наследуемые из env и тп можно объявить как declare в начале файла.**\\ **3. Локальные переменные должны быть в нижнем регистре.**\\ **4. Стараться не переопределять глобальные переменные в функциях, возможно станет правилом** ===== strongbash034 ===== **1. Нельзя при включенном set -o pipefail использовать grep -q grep -m 1 head tail**\\ это приводит к падению левой части pipe.\\ **2. set -o pipefail не работает в if $cmd; then и не нужно выставлять**\\ нет смысла выставлять перед if\\ **3. ipret=$(ip r g |grep src) Команда используется внутри pipe, ее код возврата не будет проанализирован**\\ Вы можете:\\ 1. Или использовать промежуточную переменную ret=$( $cmd )\\ 2. Или использовать строкой выше set -o pipefail cmd set +o pipefail (кроме использования в if $cmd | cmd2)\\ 4. Или если Вы считаете код достаточно надежным для конкретного случая, добавьте || true в конце\\ 5. Или комментарий строкой выше # skip strongbash034\\ Разрешены команды:\\ grep sed tr awk tail head echo cat cut printf sort uniq wc uname date locate which tac dos2unix unix2dos tee iconv free lsmod dmidecode hostname netstat ps top egrep fgrep while ls find **4. Необходимо включить set -o pipefail для ( code ) | cmd**\\ Иначе code будет работать без set -e\\ **5. Все set -o pipefail нужно отключать после pipe set +o pipefail**\\ **6. Нельзя устанавливать set -o pipefail на весь файл**\\ ===== strongbash035 ===== ** Нельзя использовать присвоение внутри if a=$(cmd); then **\\ ===== strongbash036 todo ===== Все аргументы нужно сохранять в глобальные переменные вида ARG_MYNAME=${1//--myname=/} \\ или sys:parse_arg "$@" ===== strongbash037 todo ===== Запрещено использовать echo Без кавычек echo $tmp ===== strongbash038 todo ===== Нужно всегда использовать переменные и аргументы в кавычках. Нельзя: if [ $1 = info ]; then echo $1 fi Нужно: if [ "$1" = 'info' ]; then echo "$1" fi ===== TODO добавить в инструкцию как программировать на баше ===== А пока читать коменты в http://opencarbon.ru/правила_разработки:как_надо_делать:разбор_crab_mysqldump2git **TODO** Описать виды функций __funct funct и виды переменных ARG CONF GLOBAL local и namespace сабшелы\\ **TODO** В файл примера Добавить в описание правил что grep -qm1 может уронить левую часть если это долгая команда и отгрепается первая строка пример ( set -euEo pipefail; for((i=0;i<10;i++)); do echo q; sleep 1; done | grep -q q ); echo $? Добавить в описание для всего есть подходящие инструменты, bash отличный скриптовый язык, позволяющий делать мощные программы написав немного кода по вызову готовых утилит. И при этом этот код легко сопровождать и разбирать потом без программиста, админам и саппорту. Есть куча кейсов где программы на баше в 10 раз меньше, чем на питоне при этом надежней и написана за гораздо меньшее время. ~~DISCUSSION~~ /*Не удаляйте эту строку и ниже!*/ {(rater>id=1|name=Прочитал_соглашения_кода:strongbash|type=vote|trace=user|tracedetails=1)} ~~OWNERAPPROVE~~