Содержание

Соглашение по строгому bash

TODO_OSV возможно выборочно проверять номера из https://github.com/koalaman/shellcheck

strongbash001

Обязательно должен быть выполнен crab_indent.

Почему…

Почему…

  1. indent делает код более читаемым
  2. использование indent позволяет обнаруживать ошибки

Пример…

Пример…

Конструкция if-then располагаются в одной строке.
Хорошо:

if [ "$EUID" -ne "0" ]; then
      echo "Please run as root"
fi

Фигурная скобка «{« для определния тела функции должна быть в одной строке с функцией.
После переноса строки добавляем <Tab>.
Хорошо:

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 в условиях и функциях

Проблема, если мы вызываем функцию и анализируем ее код возврата
в ней перестает работать 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

Непривычность set -u

Проблема 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

Почему…

Почему…

  1. exit 0 для того чтобы избежать случайный код возврата.
  2. USAGE, INFO, EXAMPLE использования важен, чтобы программа могла жить в будущем и не была выброшена.
  3. echo START echo SUCCESS делает удобным использование и чтение консоли и логов.
  4. 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 )

Почему…

Почему…

  1. Это усложняет чтение кода и понимание кода, файл сам по себе есть переменная и временная ссылка на него редко оправдана.
  2. Плохо если приходится искать далеко вверху по коду, что это за переменная, и что там в этом файле.
  3. mktemp это тоже плохо так как бессмысленно, если оч надо
    tt=/tmp/NAME_$((RANDOM)).$$


  4. Имя временного файла должно содержать в себе имя исполняемой программы и ее PID обязательно для разбора оставшихся после падения файлов.
  5. Если файлов останется от падения слишком много, то скрипт не сможет работать тк пиды заполнятся и это нормально мы об этом узнаем. Либо можно сделать 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 раз меньше, чем на питоне при этом надежней и написана за гораздо меньшее время.

Прочитал соглашения кода strongbash
Yes(1) No(0) Clear

Yes:
,

No:

~~OWNERAPPROVE~~