Annotation of badi/public_scripts/parallelstarter/parallelstarter, revision 1.1

1.1     ! adi         1: #!/bin/bash
        !             2: 
        !             3: # Run a command in parallel
        !             4: #
        !             5: # Call a process for each configured argument line in the arglist file.
        !             6: # (provide help here, please)
        !             7: 
        !             8: # Give the command line argument "-q" to suppress all but error output, "-Q" to suppress process' output to stdout.
        !             9: 
        !            10: # (c) 2010 under GPL v2 by Adrian Zaugg.
        !            11: 
        !            12: 
        !            13: 
        !            14: # if you run on a non-GNU system, you can provide 
        !            15: # the path to GNU date command here
        !            16: GNUDATE="date"
        !            17: 
        !            18: 
        !            19: # ----don't edit below this line-----
        !            20: TIMESTAMP="$("$GNUDATE" --utc --date now "+%s")"
        !            21: 
        !            22: # provide some defaults if the users setup() isn't found
        !            23: RUN_BIN=""
        !            24: ARGLIST_FILE=""
        !            25: ARGS_LOG="my_args"
        !            26: NICENESS=19
        !            27: MAX_PROCESSES=2
        !            28: RETRY=2
        !            29: STARTMSG="Starting..."
        !            30: 
        !            31: # Help message
        !            32: function usage()
        !            33: {
        !            34: USAGE="
        !            35: Usage: $PROGNAME [-d|--rundir-basename] [-i|--rundir-infix] [-o|--runid-offset] [-q|--quiet] [-Q|--quiet-run] [-h|-?|--help] 
        !            36: 
        !            37: Start any process in parallel. See /usr/local/lib/parallelstarter/README for more info.
        !            38: 
        !            39:   -d|--rundir-basename    Base name for your result directories
        !            40:   -i|--rundir-infix       Addition to the result directories' base name
        !            41:   -o|--runid-offset       Run count start number
        !            42:   -Q|--quiet             Suppress all but error messages
        !            43:   -q|--quiet-run         Suppress output messages from started processes
        !            44:   -h|-?|--help           This message
        !            45: 
        !            46: To use parallelstarter create a new directory, change into it and call setup_parallelstarter.
        !            47: "
        !            48: # Print help
        !            49: $SPEAK && echo "$USAGE"
        !            50: }
        !            51: 
        !            52: # get command line arguments
        !            53: function get_commandlineswitches()
        !            54: {
        !            55:        # read the users' wish
        !            56:        while [ "$#" -gt 0 ]; do
        !            57:        
        !            58:           case "$1" in
        !            59:        
        !            60:              -Q|--quiet)
        !            61:                 # suppress all output but errors
        !            62:                 SPEAK=false
        !            63:                 shift
        !            64:                 ;;
        !            65: 
        !            66:              -q|--quiet-run)
        !            67:                 SILENT_PROC_OUT=true
        !            68:                 shift
        !            69:                 ;;
        !            70:              
        !            71:              -d|--rundir-basename)
        !            72:                 shift
        !            73:                 RUNDIR_BASENAME="$1"
        !            74:                 shift
        !            75:                 ;;
        !            76: 
        !            77:              -i|--rundir-infix)
        !            78:                 shift
        !            79:                 RUN_DIR_PREFIX="$1"
        !            80:                 shift
        !            81:                 ;;
        !            82:              
        !            83:              -o|--runid-offset)
        !            84:                 shift
        !            85:                 RUN_COUNT_OFFSET="$1"
        !            86:                 shift
        !            87:                 ;;
        !            88: 
        !            89:              -h|-?|--help)
        !            90:                 shift
        !            91:                 PSHELP=true
        !            92:                 ;;
        !            93: 
        !            94:              *)
        !            95:                echo "Error: Undefined argument to `basename $0`. Use --help to get a list of valid arguments."
        !            96:                exit 1
        !            97:                ;;
        !            98:  
        !            99:           esac
        !           100:        done
        !           101: 
        !           102:        # check for help request
        !           103:        if [ ! -z "$PSHELP" ]; then
        !           104:                usage
        !           105:                exit 0
        !           106:        fi
        !           107: }       
        !           108:                 
        !           109: function count_proc() {
        !           110:        
        !           111:        # count the number of running processes
        !           112:        
        !           113:        if [ ! -z "$PID_LIST" ]; then
        !           114:                if [ "$(uname -s | egrep -wc "Linux|Darwin")" -eq 1 ]; then
        !           115:                        # Linux or Darwin
        !           116:                        if $SPEAK; then
        !           117:                                psproc='/bin/sh ./parallelstarter.start_run_[0-9]+'
        !           118:                        else
        !           119:                                psproc="$RUN_BIN $COMMON_LEADING_ARGS"
        !           120:                                psproc="$(echo $psproc) .*"
        !           121:                        fi
        !           122:                        COUNT_PROC=$(ps -p "$PID_LIST" -o command | egrep -cwe '^'"$psproc"'$')
        !           123:                elif [ "$(uname -o | grep -c "Solaris")" -eq 1 ]; then
        !           124:                        # Solaris
        !           125:                        if $SPEAK; then
        !           126:                                psproc='parallel'
        !           127:                        else
        !           128:                                # This might fail, if RUN_BIN gets longer than 8 chars
        !           129:                                # -> would start all processes at once...
        !           130:                                psproc="$RUN_BIN"
        !           131:                        fi
        !           132:                        COUNT_PROC=$(ps -p "$PID_LIST" -o fname | grep -cwe '^'"$psproc"'$')
        !           133:                else
        !           134:                        echo "Unknown OS. Only tested on Linux and Darwin."
        !           135:                        echo "You need to test the function count_proc() on your system, prior to use this script."
        !           136:                        exit 1
        !           137:                fi
        !           138:        else
        !           139:                COUNT_PROC=0
        !           140:        fi
        !           141: }
        !           142: 
        !           143: function execute_user_code() {
        !           144:        # source the file parallelstarter.setup
        !           145:        if [ -e "$USERCODE_FILE" ]; then
        !           146:                . "$USERCODE_FILE"
        !           147:        fi
        !           148: }
        !           149: 
        !           150: function setup_run() {
        !           151:        USERCODE_FILE="parallelstarter.setup"
        !           152:        if [ ! -e "$USERCODE_FILE" ]; then
        !           153:                echo -e "You need to provide a file called $USERCODE_FILE in the current\n"\
        !           154:                        "directory. The file will be executed by the shell unconditionally!"
        !           155:                exit 1
        !           156:        fi
        !           157:        execute_user_code
        !           158: }
        !           159: 
        !           160: function prepare_process_start() {
        !           161:        USERCODE_FILE="parallelstarter.prepare_process_start"
        !           162:        execute_user_code
        !           163:        RUN_DIR="$(pwd)"
        !           164: }
        !           165: 
        !           166: function start_process() {
        !           167:        # start new process
        !           168:        RUN_COUNT=$(($RUN_COUNT+1))
        !           169:        RUN_ID=$(($RUN_COUNT+$RUN_COUNT_OFFSET))
        !           170:        cd "$RUN_DIR"
        !           171:        
        !           172:        # log arguments
        !           173:        echo -e "Common arguments for run $RUN_ID:\n$COMMON_LEADING_ARGS\t$COMMON_POST_ARGS\n" > "$ARGS_LOG"
        !           174:        echo -e "Changing arguments for run $RUN_ID:\n$RUN_ARGS" >> "$ARGS_LOG"
        !           175:        
        !           176:        # start process
        !           177:        if $SPEAK; then
        !           178:                # write starter script...
        !           179:                RUN_FILE="parallelstarter.start_run_$RUN_ID"
        !           180:                echo '#!/bin/sh' > "$RUN_FILE"
        !           181:                echo 'TIMESTAMP="$('"$GNUDATE"' --utc --date now "+%s")"' >> "$RUN_FILE"
        !           182:                echo "RUN_ID=\"$RUN_ID\"" >> "$RUN_FILE"
        !           183:                echo -n "nice -n $NICENESS \"$RUN_BIN\" $COMMON_LEADING_ARGS $RUN_ARGS $COMMON_POST_ARGS" >> "$RUN_FILE"
        !           184:                $SILENT_PROC_OUT && echo -n " > /dev/null" >> "$RUN_FILE"
        !           185:                echo >> "$RUN_FILE"
        !           186:                echo "echo \"\$("$GNUDATE" \"+%Y-%m-%d %H:%M:%S\"): process$RUN_ID ($RUN_COUNT/$TOTAL_RUNS) finished in"\
        !           187:                     '$('"$GNUDATE"' --utc --date "now -$TIMESTAMP seconds" "+%H:%M:%S"s)."' >> "$RUN_FILE"
        !           188:                chmod u+x "$RUN_FILE"
        !           189:                # ...and start it
        !           190:                "./$RUN_FILE" &
        !           191:        else
        !           192:                # print errors only
        !           193:                nice -n "$NICENESS" "$RUN_BIN" $COMMON_LEADING_ARGS $RUN_ARGS $COMMON_POST_ARGS > /dev/null &
        !           194:        fi
        !           195: 
        !           196:        PID_LIST="$PID_LIST $!"
        !           197:        PID_LIST="$(echo "$PID_LIST" | sed -e "s/^ *\(.*\) *$/\1/")"
        !           198: 
        !           199:        cd "$CURRENT_DIR"
        !           200: }
        !           201: 
        !           202: 
        !           203: # initialize vars and user customization, get command line switches
        !           204: COUNT_PROC=0
        !           205: CURRENT_DIR="$(pwd)"
        !           206: RUN_DIR="."
        !           207: RUN_COUNT=0
        !           208: RUN_COUNT_OFFSET=0
        !           209: RUN_ID=0
        !           210: RUN_DIR_PREFIX=""
        !           211: # default to babbly, -q suppress output, -Q suppress process output
        !           212: SPEAK=true
        !           213: SILENT_PROC_OUT=false
        !           214: 
        !           215: get_commandlineswitches "$@"
        !           216: setup_run
        !           217: 
        !           218: 
        !           219: # check for arglist file
        !           220: if [ "x$ARGLIST_FILE" = "x" ]; then
        !           221:        echo "You need to point the variable ARGLIST_FILE to a valid file containing the command line arguments for your processes."
        !           222:        echo -e "Create a file named \"parallelstarter.setup\" in the current directory and add a line like:\n\n"\
        !           223:                "\tARGLIST_FILE=/path/to/a/file/containing/the/command/line/arguments/for/each/run\n"
        !           224:        exit 1
        !           225: elif [ ! -e "$ARGLIST_FILE" ]; then
        !           226:        echo "Argument list file not found. You need to create the configuration file named $ARGLIST_FILE, which should contain"
        !           227:        echo "the command line arguments for each run on a separate line."
        !           228:        exit 1
        !           229: fi
        !           230: 
        !           231: # check that run_bin has been set
        !           232: if [ "x$RUN_BIN" = "x" ]; then
        !           233:        echo "You need to point the variable RUN_BIN to a valid executable which should get exected in parallel. "\
        !           234:              "Set this in the file named \"parallelstarter.setup\"."
        !           235:        exit 1
        !           236: fi
        !           237: 
        !           238: # count number of runs to do
        !           239: TOTAL_RUNS="$(cat "$ARGLIST_FILE" | grep -vce "^ *     *#.*$")"
        !           240: 
        !           241: while read RUN_ARGS; do
        !           242: 
        !           243:         notstarted=true
        !           244: 
        !           245:        # control the number uf running processes
        !           246:         while [ $notstarted = true ]; do
        !           247:                 count_proc
        !           248:                 if [ $COUNT_PROC -lt $MAX_PROCESSES ]; then
        !           249:                        # exclude commented out lines in the conf file (but not empty ones!)
        !           250:                        if [ $( echo "$RUN_ARGS" | grep -ce "^ *        *#.*$") -eq 0 ]; then
        !           251:                                prepare_process_start
        !           252:                                $SPEAK && echo "`"$GNUDATE" "+%Y-%m-%d %H:%M:%S"`: process$(($RUN_COUNT+1+$RUN_COUNT_OFFSET)) ($(($RUN_COUNT+1))/$TOTAL_RUNS): $STARTMSG (right now running: $COUNT_PROC)."
        !           253:                                start_process
        !           254:                        fi
        !           255:                         notstarted=false
        !           256:                        break;
        !           257:                fi
        !           258: 
        !           259:                sleep "$RETRY"
        !           260:         done
        !           261: 
        !           262: done < "$ARGLIST_FILE"
        !           263: cd "$CURRENT_DIR"
        !           264: 
        !           265: # wait for all processes to finish
        !           266: count_proc
        !           267: if [ $COUNT_PROC -gt 0 ]; then
        !           268:         $SPEAK && echo "`"$GNUDATE" "+%Y-%m-%d %H:%M:%S"`: All jobs started - $COUNT_PROC process(es) still runnning. Waiting..."
        !           269: 
        !           270:         # wait for processes to finish
        !           271:         while [ $COUNT_PROC -gt 0 ]; do
        !           272:                 sleep "$RETRY"
        !           273:                count_proc
        !           274:         done
        !           275: fi
        !           276: 
        !           277: $SPEAK && echo "`"$GNUDATE" "+%Y-%m-%d %H:%M:%S"`: All processes finished."
        !           278: $SPEAK && echo "`"$GNUDATE" "+%Y-%m-%d %H:%M:%S"`: Processed $RUN_COUNT jobs in $("$GNUDATE" --utc --date "now -$TIMESTAMP seconds" "+%H:%M:%S")s."
        !           279: 
        !           280: 
        !           281: # run users post processing script
        !           282: if [ -e "parallelstarter.cleanup" ]; then
        !           283:        $SPEAK && echo "`"$GNUDATE" "+%Y-%m-%d %H:%M:%S"`: Starting post processing..."
        !           284:        . ./parallelstarter.cleanup
        !           285:        $SPEAK && echo "`"$GNUDATE" "+%Y-%m-%d %H:%M:%S"`: done."
        !           286: fi
        !           287: 
        !           288: exit 0

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>