Strongbash

Различия

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

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
Последняя версия Следующая версия справа и слева
соглашения_кода:strongbash [08.04.2018 17:30]
admin ↷ Имя страницы соглашения_кода:strongbash изменено на соглашения_кода:strongbash_коменты
соглашения_кода:strongbash [02.12.2018 11:43]
admin Approved(admin 2018/12/02 11:44)
Строка 1: Строка 1:
 +===== Соглашение по строгому bash =====
 +TODO_OSV возможно выборочно проверять номера из https://​github.com/​koalaman/​shellcheck
 +
 +===== 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>
 +#!/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
 +</​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
 +}
 +
 +### [ "$( 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'​ }
 +</​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 и USAGE, INFO, EXAMPLE\\
 +В конце каждого файла должны быть echo SUCCESS и exit 0 :\\ **
 +Для продвинутых разработчиков лучше использовать в начале скрипт:​ include ::​carbon.sys
 +<hidden Почему...>​
 +  - exit 0 для того чтобы избежать случайный код возврата.
 +  - USAGE, INFO, 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
 +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
 +</​code>​
 +<color #​22b14c>​Хорошо сделать функцию</​color>​ <​nowiki>​
 +__exit()</​nowiki><​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 "​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
 +</​code>​
 +</​hidden>​
 +
 +<hidden Решение проблем...>​
 +<color #​22b14c>​Хорошо для тихих файлов:</​color>​
 +<code bash>
 +# 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
 +</​code>​
 +
 +
 +<color #​22b14c>​Или для тихих файлов с carbon.sys:</​color>​
 +<code bash>
 +__SILENT=TRUE
 +source ::​carbon.sys
 +### --help Info: Автоматически создавать и коммитить все файлы в каталоге
 +### --help Usage: auto_git.sh [install] /​var/​www/​html
 +### --help Example: auto_git.sh /​var/​www/​html
 +exit 0
 +</​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 =====
 +**Не рекомендуется использовать переменные,​ особенно глобальные,​ для временных файлов,​ если эта переменная используется менее 5 раз.**\\
 +<​del>​$TMP_FILE=$( mktemp /​tmp/​tmp_file.XXXX )</​del>​
 +<hidden Почему...>​
 +  - Это усложняет чтение кода и понимание кода, файл сам по себе есть переменная и временная ссылка на него редко оправдана.\\
 +  - Плохо если приходится искать далеко вверху по коду, что это за переменная,​ и что там в этом файле.\\
 +  - mktemp это тоже плохо так как бессмысленно,​ если оч надо <code bash>
 +tt=/​tmp/​NAME_$((RANDOM)).$$
 +</​code>​\\
 +  - Имя временного файла должно содержать в себе имя исполняемой программы и ее PID обязательно для разбора оставшихся после падения файлов.\\
 +  - Если файлов останется от падения слишком много, то скрипт не сможет работать тк пиды заполнятся и это нормально мы об этом узнаем. Либо можно сделать rm -f перед работой
 +</​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>​
 +### todo http://​opencarbon.ru/​соглашения_кода:​strongbash_коменты
 +===== 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"​
 +или
 +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
 +</​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||**\\
 +тк 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, ее код возврата не будет проанализирован**\\
 +<hidden Что делать>​
 +Вы можете:​\\
 +1. Или использовать промежуточную переменную ret=$( $cmd )\\
 +2. Или использовать строкой выше set -o pipefail cmd set +o pipefail (кроме использования в if $cmd | cmd2)\\
 +4. Или если Вы считаете код достаточно надежным для конкретного случая,​ добавьте || true в конце\\
 +5. Или комментарий строкой выше # skip strongbash034\\
 +</​hidden>​
 +<hidden Исключения>​
 +Разрешены команды:​\\
 +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
 +</​hidden>​
 +**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 =====
 +Все аргументы нужно сохранять в глобальные переменные вида <code bash>
 +ARG_MYNAME=${1//​--myname=/​}
 +</​code>​\\
 +или sys:​parse_arg "​$@"​
 +
 +===== strongbash037 todo =====
 +Запрещено использовать echo Без кавычек
 +<​del>​echo $tmp</​del>​
 +
 + 
 +===== TODO добавить в инструкцию как программировать на баше =====
 +А пока читать коменты в http://​opencarbon.ru/​правила_разработки:​как_надо_делать:​разбор_crab_mysqldump2git
 +
 +**TODO** Описать виды функций __funct funct и виды переменных ARG CONF GLOBAL local и namespace сабшелы\\
 +**TODO**
 +В файл примера
 +
 +Добавить в описание правил что 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~~
 +
 +/*Не удаляйте эту строку и ниже!*/
 +{(rater>​id=1|name=Прочитал_соглашения_кода:​strongbash|type=vote|trace=user|tracedetails=1)}
 +
 +
 +~~OWNERAPPROVE~~