strongbash.1543769027.txt.gz | Хозяин: | Изменен: 20.05.2019 15:18 admin | Утвержден(admin 2018/12/02 11:44)
Обязательно должен быть выполнен crab_indent.
indent делает код более читаемым
использование 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
Должен быть установлен set -eu или include ::carbon.sys
Категорически нельзя ставить 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 иногда непривычен для входящих параметров:
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:-}"
Нельзя использовать || true в условных выражениях.
Это миф что без них не работает и все падает при set -e.
Все отлично работает, если return 0 в конце каждой функции и exit 0 в конце каждого файла.
Как не надо делать:
funct1(){
[ a = b ] && echo Привет || true
}
Как надо делать:
funct1(){
[ a = b ] && echo Привет
return 0
}
Нельзя использовать else true в условных выражениях
см strongbash003
В конце каждой функции должен быть 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
В начале каждого файла должен быть echo START и USAGE, INFO, EXAMPLE
В конце каждого файла должны быть echo SUCCESS и exit 0 :
Для продвинутых разработчиков лучше использовать в начале скрипт: include ::carbon.sys
exit 0 для того чтобы избежать случайный код возврата.
USAGE, INFO, EXAMPLE использования важен, чтобы программа могла жить в будущем и не была выброшена.
echo START echo SUCCESS делает удобным использование и чтение консоли и логов.
include ::carbon.sys позволит автоматически деалать 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 ::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
Хорошо для тихих файлов:
# 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
Или для тихих файлов с carbon.sys:
__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
В конце каждого файла должен быть 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
Нужно использовать 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
Нельзя использовать команду let.
Так как она дает ошибку при переходе через 0, let i=1-1 упадет скрипт.
Плохо:
let c=a-b
Хорошо:
c=$((a-b))
Не рекомендуется использовать переменные, особенно глобальные, для временных файлов, если эта переменная используется менее 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_коменты
Максимальный indent вложенность 5
Большая вложенность всегда приводит к большой вложенности мысли при чтении кода, это неудобно и приводит к ошибкам.
Используйте ранний return или сделайте подфункции, или case и тп.
Не используйте большие if then else иначе мозг будет занят 5-ю уровнями вложенности.
Команды | и || и && при переносе длинных строк
требуется вынести на следующую строку и поставить tab.
Это повышает читаемость кода и делает явным, что был перенос и это продолжение предыдущей строки.
Хорошо:
cat 123 | grep 123 | sed | cat | grep \
| while read t; do echo 123; done \
Ставьте пробел после #
Можно используйте без пробелов #} и #{ для исправления ошибок crab_indent.
Максимальный размер функции 64 строки, а лучше 32.
Каждая функция это смысловая единица которая должна помещаться в пару предложений человеческого языка, это правильное нативное удобное для мозга деления на смыслы.
Выделите подфункции, часто вида:
__fname(){
}
При этом не нужно создавать миллиард функций на каждое словосочетание тк любая функция эта новая сущность, а плодить сущности плохо.
И если функция из 4 строк вызывается один раз, то врядли она нужна.
Слишком большой линейный файл больше 64 строк, выделите подфункции.
Если в файле больше 64 строк, значит однозначно есть команды которые делают похожие вещи или группу вещей и правильней их объединить в функции.
Уберите конечные space, tab.
В конце файла должен стоять один enter.
Длина строки должна быть не более 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
Поставьте пробел перед символом перенаправления '>'
Добавьте описание и пример использования программы 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 ::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 "$@"
Запрещены функции 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
# не утверждено как именно
1. Запрещено удалять tmp в trap EXIT.
2. Не рекомендуется использовать trap EXIT.
1. Создание временного файла это своего рода with file и его удаление это end with, чтоб было понтяно где он более не нужен аля область работы с файлом. И если файл остался в тмп, то это признак что есть ошибка в логике.
2. Использование trap EXIT плохо тк он скрывает ошибки программ и никто о них не узнает, ломает честную логику алгоритма, это вредный хак. Должен быть явный вызов error_exit() и тп.
Если нужно удалить lock можно использовать trap TERM INT HUP, если lock остался после сбоя значит он и должен остаться, чтобы получить ошибки и разобраться со сбоем.
1. Утилиты должны удалять за собой временные файлы
2. Запрещено в утилитах передавать результаты через промежуточные файлы. Только errno stdout stderr.
В исключительных случаях имя файла должно быть в argv или конфиг в argv.
Непонятно как проверять, что результаты идут через промежуточные файлы, пока проверяем, что чистится /tmp/
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
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/*"
«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
Нельзя использовать голые скобки для assert и тп [ a = b ]
Это противоречит явности кода и человекочитаемости ошибок.
Это использование set -e для основной логики, что запрещено.
Как не надо делать:
[ "$fname" = "" ]
Как надо делать:
[ "$fname" = "" ] && { echo 'Укажите имя файла'; exit 1; }
Как надо делать:
# Или не проверять все подряд, тк все на свете проверить анрил, само упадет по set -e
нельзя использовать конструкции вида [ a = b ] || cmd1 и cmd1 || cmd2 кроме утвержденного сахара
используйте:
[ a != b ] && cmd1 и
if ! cmd1; then
cmd2
fi
Нельзя использовать 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
Если мы берем stdout от функции мы обязаны прописать в первой строке Функции set -e тк он снимается
Нельзя вызывать функцию внутри if fname и fname&& и fname||
тк set -e перестанет работать, перевыставить не поможет
нельзя использовать -EOF, используйте обычный EOF
usage скрипта и его описание должно располагаться в начале скрипта
Чтобы при открытии файла сразу понять, что он делает.
1. Глобальная переменная должна быть в верхнем регистре, если используется в функциях.
2. Все локальные переменные в функциях должны быть объявлены как local.
2.1 Переменные наследуемые из env и тп можно объявить как declare в начале файла.
3. Локальные переменные должны быть в нижнем регистре.
4. Стараться не переопределять глобальные переменные в функциях, возможно станет правилом
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 на весь файл
Нельзя использовать присвоение внутри if a=$(cmd); then
Все аргументы нужно сохранять в глобальные переменные вида
ARG_MYNAME=${1//--myname=/}
или sys:parse_arg «$@»
Запрещено использовать echo Без кавычек
echo $tmp
А пока читать коменты в 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 |
|
~~OWNERAPPROVE~~
Обсуждение
Как правильно?
или
Иными словами: правило описывает пробел слева, справа или с обоих сторон вокруг символа перенаправления вывода?
+ как быть с перенаправлениями в другой дескриптор?
Если пробел справа важен, то есть ли разница между абсолютным и относительным путём к файлу?
В общем нужно чуток пополнить число примеров, дабы не смущать Колю.
Если файл то пробел справа обязателен если дескриптор то запрещен
Поставьте пробел перед символом перенаправления
crab-indent не работает с
:(
а зачем кавычки? без них все работает
Если не ставить кавычки, то баш выполняет подстановки в тексте. Альтернатива кавычкам, эскейпить все спец-символы в тексте. Когда спец-символов много, и чтение, и написание кода, может превратиться в ад.
Ясно тогда надо мердж реквест, у меня нет времени решать эту задачу
TODO По мотивам идей Олега. думаю надо добавить shellcheck в качестве варнинг проверок. Но опять же grep -с и ${#name} не каждый по памяти помнит и лоускил правило нарушено. read -r это полезно, и вообще бы все это проанализировать и полезное выделить цифрами и грепать их дополнительно в выводе shellcheck. Добавил в туду
Добавить проверку, чтобы вместо ln -fs делали ln -nfs, иначе внутри папки создается симлинк.
UPD: всегда делать ln -ns
+
Думаю нужно правило: всегда использовать кавычки, кроме случаев, где необходим word split. #skipcheck в таких случаях будет говорить, что это кавычки пропущены умышленно.
Согласен добавь
Правило: по аналогии с set -eu всегда ставить в шапку shopt -s nullglob (если файлы по маске не находятся, то результат - пусто, а не само выражение).
Сейчас регулярно случаются баги, когда * не разворачивается. И для фикса используют дополнительные условия по проверки результата развёртывания *.
Случаи когда этот флаг не нужен в скрипте, скорее всего баг.
Есть ещё хороший флаг dotglob, но его полезность зависит от ситуации. И работает он совместно с переменной GLOBIGNORE.
По поводу нуллглоба: мне кажется что иногда лучше оставить неразвернутое выражение и получить сообщение об ошибке «Нет такого файла/директории», чем получить пустоту и непредсказуемое поведение утилит, которые принимают позиционные аргументы.