2017-10-17 Atomic Bash.md

Различия

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

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

корзина:статьи_участников:2017-10-17_atomic_bash.md [04.09.2018 07:15]
127.0.0.1 внешнее изменение
корзина:статьи_участников:2017-10-17_atomic_bash.md [20.05.2019 15:18]
Строка 1: Строка 1:
-# Атомарность в bash 
-Tags: bash, правила программирования 
- 
-В программировании большой частью является работа с исключительными ситуациями,​ ошибками и прочими внезапными вещами,​ кооторые отвлекают тебя от непосредственной логии приложения. 
-Ситуация ухудшается,​ когда ты начинаешь пользоваться bash, часто люди вообще забивают на надежность и скрипты могут падать в очень странной конфигурации. 
-Причем,​ по-умолчанию,​ bash продолжит выполнять твой скрипт,​ даже если одна из команд упала или когда ты написал переменную с ошибкой ​ 
-```bash 
-data_dir='/​tmp/​test_data'​ 
-rm -rf "​${vata_dir}/"​ 
-``` 
-Конечно,​ первой рекомендацией при начале написания скрипта,​ является включение всех необходимых флагов:​ 
- 
-```bash 
-set -euEo pipefail 
-``` 
- 
-Важно заметить,​ что стиль кода при этом серьезно меняется,​ часто если скрипт был написан без этих опций, включение этих опций потребует значительной переработки кода, мы часто код вообще переписываем. 
-Имейте в виду, что set -e пропадает внутри функций,​ вызываемых из условных выражений и вернуть его внутри просто не получится. 
- 
-```bash 
-set -euEo pipefail 
-f(){ 
-    set -e # Не сработает 
-    false 
-    echo "​Hello!"​ 
-    return 0 
-} 
-if ! f; then 
-    echo "Newer happen"​ 
-fi 
-``` 
- 
-Здесь вы заметите return 0 в конце функции. Мы ввели правило,​ что в конце любой функции должно находиться return 0, это спасает от срабатываний set -e, когда это не нужно: 
- 
-```bash 
-set -euEo pipefail 
-f(){ 
-    echo "Do somphing..."​ 
-    [ -f /​tmp/​test.$$ ] && rm -f /​tmp/​test.$$ 
-} 
-echo "​Hello!"​ 
-f 
-echo "Newer happen!"​ 
-``` 
- 
-Раньше мы условное выражение расписывали в виде if then fi, пока не додумались,​ что return 0 избавит нас от этого 
- 
-```bash 
-set -euEo pipefail 
-f(){ 
-    echo "Do somphing..."​ 
-    [ -f /​tmp/​test.$$ ] && rm -f /​tmp/​test.$$ 
-    return 0 
-} 
-echo "​Hello!"​ 
-f 
-echo "​Goodbye!"​ 
-``` 
- 
-Но вернемся к первоначальной проблеме:​ скриптов все больше,​ багов тоже, как жить/​что делать?​ 
-Возьмем среднестатистический пример:​ нам нужно распарсить один файл и записать результат в соседний. ​ 
- 
-```bash 
-cat /​home/​one_file | sed '​s/​old_stuff/​new_stuff/'​ > /​tmp/​tmp_file 
-echo "Do somphing..."​ 
-cat /​tmp/​tmp_file > /​home/​second_file 
-``` 
- 
-Однажды у вас файл стал в 2 раза больше,​ через пол года файл вообще опустел. Самое забавное,​ что в этом начинают винить нагрузку на диск, внезапную многопоточность,​ солнечные бури... 
-В принципе,​ можно считать что скрипт свою задачу таки выполняет и это правда. Скрипт уходит в продакшен,​ программист со временем теряет уверенность в работе всей системы,​ тихо материт гребаный баш, вот бы на питоне... 
-Но давайте разбираться,​ скрипт не защищен от одновременного использования временными файлами,​ исправляем:​ 
- 
-```bash 
-trap _exit EXIT 
-_exit(){ 
-    res=$? 
-    rm -f /​tmp/​test_script*.$$ 
-    return $res 
-} 
-cat /​home/​one_file | sed '​s/​old_stuff/​new_stuff/'​ > /​tmp/​test_script.tmp_file.$$ 
-echo "Do somphing..."​ 
-cat /​tmp/​test_script.tmp_file.$$ > /​home/​second_file 
-``` 
- 
-Все временные файлы мы помечаем PIDом текущего процесса,​ плюс ловим выход из скрипта и подчищаем за собой все временные файлы, независимо от того, выполнился скрипт до конца или нет. 
-Удаление всех файлов по маске с именем скрипта и пидом позволит не задумываться о «сборке мусора» и захламлению каталога /tmp кучей мелких файлов,​ хотя у нас в последнее время пропагандируется отказ от этого, но я пока не буду это рассматривать. 
-Но что делать,​ если скрипт упадет в середине записи в файл (например,​ от банальной нехватки места)?​ Сохранять результат во временный файл и когда он полностью готов, заменять новой версией старый файл атомарно,​ одной командой mv -f: 
- 
-```bash 
-cat /​home/​one_file | sed '​s/​old_stuff/​new_stuff/'​ > /​tmp/​test_script.tmp_file.$$ 
-echo "Do somphing..."​ 
-mv -f /​tmp/​test_script.tmp_file.$$ /​home/​second_file 
-``` 
- 
-В этом случае,​ даже если параллельно запустить несколько копий этого скрипта,​ файл /​home/​second_file всегда будет в корректном состоянии и к нему можно обращаться за данными в любой момент. 
-Придерживаясь всех этих советов,​ вы всегда будете иметь актуальные и корректные версии файлов,​ без их резервных копий и блокировок от повторного запуска. ​ 
- 
- 
-~~OWNERAPPROVE~~