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>