Annotation of badi/public_scripts/parallelstarter/parallelstarter, revision 1.1.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>