It has happened in a few occasions, to launch a command in a shell only after a command in another shell has successfully finished (for instance, after a long task like source code compilation, i want to launch tests on another shell).

Usually you might want to concatenate the commands:

first_command && second_command

But this is not always possible, because maybe you need to different windows opened, and maybe you'd also need to change directory in between, or run some other commands. This is why I wrote this helper scripts. It uses a sqlite3 database to queue jobs, and can handle different named queues with the -q parameter. Just run it with --help to see all the possible usage. I'm publish the first original version here; an updated version will be maintained on my github GuLinux-Commons repository, in the shell/utilities directory.

#!/bin/bash
# defaults

queue="default"
clean="false"
cmd="run"

print_help() {
    cat >&2 <<EOF
Usage:
    $0 [-q|--queue queue] [-c|--clean] [-- command]
    $0 -l|--list-queue [queue name]
    $0 --clear-all
    $0 -d|--delete id
    $0 --queues
EOF
}

queuedb="$HOME/.cache/qj.db"

doquery() {
    sqlite3 "$queuedb" <<<"$@"
}

create-schema() {
    doquery "CREATE TABLE q (
           id INTEGER PRIMARY KEY,
           queue TEXT,
           added TEXT,
           shpid INTEGER,
           status INTEGER,
           exitcode INTEGER,
           task TEXT
         );"
}

print-queues() {
    doquery "SELECT DISTINCT queue FROM q;"
}

print-task-header() {
    echo -e "id|queue|added|status|exitcode|task"
}
print-task() {
    id="$1"
    queue="$( doquery "SELECT queue FROM q WHERE id = $id;" )"
    added="$( doquery "SELECT added FROM q WHERE id = $id;" )"
    status="$( doquery "SELECT status FROM q WHERE id = $id;" )"
    exitcode="$( doquery "SELECT exitcode FROM q WHERE id = $id;" )"
    task="$( doquery "SELECT task FROM q WHERE id = $id;" )"
    case "$status" in
        0)
        status="queued"
        ;;
        1)
        status="finished"
        ;;
        2)
        status="failed"
        ;;
        3)
        status="queue failed"
        ;;
    esac
    echo -e "$id|$queue|$added|$status|$exitcode|$task"
}

print-queue-tasks() {
    queue="$1"
    print-task-header
    doquery "SELECT id FROM q WHERE queue = '$queue' ORDER BY added ASC;" | while read id; do
        print-task "$id"
    done
}

print-tasks() {
    queues="${@:-$(print-queues)}"
    for queue in $queues; do
        echo
        echo "Queue: $queue"
        print-queue-tasks "$queue" | column -t -s '|'
    done
}

while [ -n "$1" ] && [ "$1" != "--" ]; do
    case "$1" in
        -q|--queue)
            queue="$2"; shift
            ;;
        -c|--clean|--clear)
            clean="true"
            ;;
        --clear-all)
            rm "$queuedb"
            create-schema
            exit 0
            ;;
        -l|--list-queue)
            shift
            print-tasks "$@"
            exit 0
            ;;
        --queues)
            print-queues
            exit 0
            ;;
        -d|--delete)
            doquery "DELETE from q WHERE ID = $2;"
            exit $?
            ;;
        *)
            print_help
            exit 1
            ;;
    esac
    shift
done

shift

[ -r "$queuedb" ] || ( mkdir -p "$( dirname "$queuedb")"; create-schema )
if [ "$clean" = "true" ]; then
    doquery "DELETE FROM q WHERE queue = '$queue';"
    [ -z "$@" ] && exit 0
fi

if [ -z "$1" ]; then
    print_help
    exit 1
fi

#  echo "Queuing $@ to queue name $queue" >&2
query_text="INSERT INTO q
    (queue, added, shpid, status, exitcode, task)
    VALUES('$queue', datetime($( date +%s ), 'unixepoch'), $$, 0, 0, '$( echo "$@" | sed "s/'/''/g" )');
    select last_insert_rowid();" 
job_id="$( doquery "$query_text" )"
#  echo "Job queued, id: $job_id" >&2
NEEDS_ENDL=false
while [ "$job_id" != "$( doquery "SELECT id FROM q WHERE status = 0 ORDER BY added ASC LIMIT 1;" )" ]; do
    echo -n .
    NEEDS_ENDL=true
    sleep 1
done
[ "$NEEDS_ENDL" = true ] && echo

last_id="$( doquery "select id FROM q where queue = '$queue' AND id <> "$job_id" AND status < 3 order by added DESC LIMIT 1;" )"
#echo "Last id: $last_id" >&2
if [ -n "$last_id" ] && [ "$( doquery "SELECT status FROM q WHERE id = $last_id;" )" -ge 2 ]; then
  echo "[ERROR] Last command on queue $queue exited with an error; skipping queue execution" >&2
  echo "Command: $(doquery "SELECT task FROM queue WHERE id = $last_id;" ); exit code: $( doquery "SELECT exitcode FROM q WHERE id = $last_id;" )" >&2
  echo "If you want to clean up the queue, run $0 $queue clean" >&2
  doquery "UPDATE queue SET status = 3 WHERE id = $job_id;"
  exit 1
fi

#echo "Running job $@" >&2
"$@"
exit_code="$?"
#echo "Command exit code: $exit_code" >&2
if [ "$exit_code" == 0 ]; then
    status=1
else
    status=2
fi
doquery "UPDATE q set status = $status, exitcode = $exit_code WHERE id = $job_id;"
exit "$exit_code"

Previous Post Next Post