#bash #checkpointing
#удар #контрольные точки
Вопрос:
Я разрабатываю большой скрипт, скелет которого выглядит следующим образом:
#!/bin/bash load_variables() function_1() function_2() function_3() [...] function_n()
- Во время каждого взлета пользовательские флаги сначала загружаются в
load_variables()
функцию. - Затем скрипт продолжит выполнение
function_1() =gt; function_2() =gt; [...] =gt; function_n()
Мне нужно реализовать контрольные точки, которые будут сохранены в log.txt
.
Допустим, этот сценарий был остановлен или разбился в function_2()
.
Я хочу сохранить прогресс перед запуском каждой функции, сохранить его в log.txt
, и когда я снова запущу сценарий, я хочу load_variables()
, а затем перейти к точке сбоя/контрольной точке, хранящейся в log.txt
.
Как я могу добиться этого с помощью bash?
Ответ №1:
Это пример с ловушкой. Сохранение имен функций при ошибке с помощью trap и $LINENO
Файл журнала будет удален, если все в порядке
#!/bin/bash trap 'clear_log' EXIT trap 'log_checkpoint $LINENO' ERR CHECKLOG=checkpoints.log clear_log() { if [ $? -eq 0 ] ; then if [ -f "$CHECKLOG" ]; then rm "$CHECKLOG" fi fi } log_checkpoint() { func=$(sed -n $1p $0) echo "Error on line $1: $func" echo "$func" gt; $CHECKLOG exit 1 } retry(){ [ ! -f $CHECKLOG ] amp;amp; return 0 if grep -q "$1" "$CHECKLOG"; then echo retry "$1"; rm "$CHECKLOG"; return 0 else echo skip "$1"; return 1 fi } func1(){ retry ${FUNCNAME[0]} || return 0 echo hello | grep hello } func2(){ retry ${FUNCNAME[0]} || return 0 echo hello |grep hello } func3(){ retry ${FUNCNAME[0]} || return 0 echo hello | grep foo } func1 func2 func3 exit 0
Комментарии:
1. Спасибо, я обязательно воспользуюсь этой опцией. Счастливого Рождества!
Ответ №2:
Я хочу сохранить прогресс перед запуском каждой функции, сохранить его в log.txt и когда я снова запускаю сценарий, я хочу load_variables (), а затем перейти к точке сбоя/контрольной точке, хранящейся в log.txt.
Сделай именно это. Но вы не можете «прыгать» в скриптах bash — вместо «прыгать» просто пропустите уже запущенные функции, которые вы можете отслеживать. В основном, в псевдокоде:
load_variables() { if [[ -e log.txt ]]; then . log.txt fi } already_run_functions=() checkpoint() { # if the function was already run if "$1" in already_run_functions[@]; then # skip this function return fi { # save state with function name declare -f declare -p } gt; log.txt # run it if ! "$@"; then # handle errors exit 1 fi already_run_functions =("$1") } load_variables checkpoint function1 checkpoint function2 checkpoint function3
В целом, это оболочка, она проста. Гораздо лучше использовать систему сборки. Простого создания более чем достаточно для отслеживания зависимостей нескольких сценариев оболочки и распараллеливания работы. Сохраняйте результат в файлах после каждой задачи и распределяйте функции по нескольким файлам.
Итак, какой-нибудь реальный пример:
rm -f log.txt script() { load_variables=" if [[ -e log.txt ]]; then . log.txt cd "$PWD" else already_run_functions=() fi " oneof() { local i for i in "${@:2}"; do if [[ "$1" = "$i" ]]; then return 0 fi done return 1 } checkpoint() { # if the function was already run if oneof "$1" "${already_run_functions[@]}"; then # skip this function return fi { # save state declare -f declare -p $(compgen -v | grep -Ev '^(BASH.*|EUID|FUNCNAME|GROUPS|PPID|SHELLOPTS|UID|SHELL|SHLVL|USER|TERM|RANDOM|PIPESTATUS|LINENO|COLUMN|LC_.*|LANG)
выходы:
function1 function2 checkpoint: function2 failed function2 function3
Ответ №3:
Вот мое предложение, основанное на приведенных выше ответах. Это может быть полезно для людей, которым необходимо перехватывать CTRL C и другие сбои, за исключением ошибок:
#!/bin/bash ### Catch crash in trap and save the function name in anchor.log trap 'echo $anchor gt; anchor.log amp;amp; exit 1' SIGINT trap 'echo $anchor gt; anchor.log amp;amp; exit 1' SIGHUP trap 'echo $anchor gt; anchor.log amp;amp; exit 1' SIGKILL trap 'echo $anchor gt; anchor.log amp;amp; exit 1' SIGTERM ### --- clear_log() { ### REMOVING LOG IF PROGRAM EXIT NORMALLY if [ -f "anchor.log" ]; then rm "anchor.log" fi } anchor_check(){ ### RETRY FUNCTION IF IT'S NOT IN anchor.log anchor=$1 # Check if function name is inside "anchor.log" [ ! -f "anchor.log" ] amp;amp; return 0 if grep -q "$1" "anchor.log"; then rm "anchor.log"; return 0 else return 1 fi } ### EACH FUNCTION START WITH anchor_check function_1() { anchor_check "${FUNCNAME[0]}" || return 0 echo "test $anchor" sleep 2 } function_2() { anchor_check "${FUNCNAME[0]}" || return 0 echo "test $anchor" sleep 2 } function_3() { anchor_check "${FUNCNAME[0]}" || return 0 echo "test $anchor" } function_1 function_2 function_3 clear_log
Спасибо вам, ребята, за вашу помощь!
) } gt; log.txt # run it if ! "$@"; then # handle errors echo "checkpoint: $1 failed" exit 1 fi already_run_functions =("$1") } function1() { echo function1 } function2() { echo function2 if [[ -e file ]]; then return 1 fi } function3() { echo function3 } eval "$load_variables" checkpoint function1 checkpoint function2 checkpoint function3 } touch file ( script ) rm file ( script ) выходы:
Ответ №3:
Вот мое предложение, основанное на приведенных выше ответах. Это может быть полезно для людей, которым необходимо перехватывать CTRL C и другие сбои, за исключением ошибок:
Спасибо вам, ребята, за вашу помощь!