开发者

What logic can I use to resume an interrupted shell script?

I have written a large script to run a series of tests. Each test is a function on its own and I call these functions serially to get the job done.

I now want to make provisions to resume script run from the last function where it was aborted (e.g. in case system hangs). The logic I am using now is to write progress status into a file and then check the file content to determine where to start the script and resume from there.

However, I can't continue using this logic because I want to introduce a menu into the script where I want to be able to choose from:

1) Run all tests from beginning to end

2) Run individual tests (brings up another menu with individual test options)

3) Resume from last abort

I have the written the code for the menu but still haven't figured out how to fit my existing logic with the case construct used in my menu system. I am unhappy with the current resume from last abort logic I am using presently, which is:

fn1()
{
  #do a bunch of things
  echo 1 > progress.log
  fn2
}

fn2()
{
STATUS=`cat progress.log`
case "$STATUS" in 
1)
  #do a bunch of things
  echo 2 > progress.log
  fn3
;;
*)
 fn3
;;
esac
}

..
..
# 开发者_JAVA技巧and more functions in the same style as fn2
..

# main call

if [ -f progress.log ]
 fn2
else
 fn1
fi

Any ideas for a cleaner way to do this resume logic while integrating it with the ability to run tests individually when required? Thanks.


How about something like this. Drop the logic in fn1..N that link them together, but still write the progress.log file in there, and then do this:

runtest()
{
    # run test X where X is 1..N
    case "$1" in
    1) fn1 ;;
    2) fn2 ;;
    3) fn3 ;;
    *) echo "Invalid test number $1" ;;
    esac
}

runtests()
{
    START="$1"; shift
    [ -z "$START"] && START=1 # default to test 1
    for i in $(seq "$START" 50); do # replace 50 with max test number
        runtest $i
    done
}

# from your menu, run from start:
runtests 1
# from menu, run from progress
runtests $(< progress.log)
# from menu, run test TESTNUM
runtest $TESTNUM
# or by name (this is dangerous if you don't trust the user):
eval "$TESTNAME"

EDIT: with this version, you can just replace fn1, fn2, etc with the test names, so that you still get a runtest N interface that you can use to tie it all together.


Bash supports "case cascade" aka fall through. Hence you can write/read progress into/from a file and the act accordingly via case cascade. For instance

function write_progress
{
    local step_name=$1
    echo "$(date '+%F %H:%M:%S') $step_name" >> "$PROGRESS_FILE"

    if [ $? -ne 0 ]; then
        error_out "failed to update progress"
    fi
}


function read_proress
{
    local step_name
    step_name=$(tail -1 "$PROGRESS_FILE" | cut -d' ' -f 3)

    if [ $? -ne 0 ]; then
        error_out "failed to read progress"
    fi
}

case $(read_proress) in
    step1) 
        echo do_sth_for_step1
        ;&
    step2)
        echo step2_thing
        ;&
    step3)
        echo step3_thing
        ;;
esac


If i understand you right, you may use something like eval fn$(cat status). In the end of each function you call runnext which derives the number of the current function, adds 1 and calls the resulting function through eval.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜