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 abortI 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
.
精彩评论