Контрольные точки сценария Bash

#bash #checkpointing

#удар #контрольные точки

Вопрос:

Я разрабатываю большой скрипт, скелет которого выглядит следующим образом:

 #!/bin/bash  load_variables()  function_1() function_2() function_3() [...] function_n()  
  1. Во время каждого взлета пользовательские флаги сначала загружаются в load_variables() функцию.
  2. Затем скрипт продолжит выполнение 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 и другие сбои, за исключением ошибок:


Спасибо вам, ребята, за вашу помощь!