#!/bin/sh # # ShellFlow: the simplest workflow orchestrator in under # 20 SLOC of POSIX shell, using Unix pipelines # # Usage: echo some_input | /path/to/flow.sh /path/to/workflow # # Exposes the following variables to workflows and tasks: # - SFLIB - resolved path to this file # - WFDIR - directory of the workflow # # Also exposes the debugout function to output to stderr # # Typical workflow script structure: ./task1 | ./task2 | ./task3 ... # (the workflow directory becomes the current working directory) # # Data format: workflow name, tab character, all other input # # Typical task script structure (if shell-based): # #!/bin/sh # . $SFLIB # source the library part of the script # do_task() { # accepts workflow name into $1 and all input into $2 # # your actions here # } # run-task # # You can call different workflows from inside tasks by referring to # this very script using the $SFLIB environment variable or by calling # the sfrun function # # Created by Luxferre in 2025, released into public domain # ShellFlow task function library # print to stderr debugout() { printf '%s\n' "$*" >&2; } # format the message and print to stdout flowout() { printf '%s\t%s' "$1" "$2"; } # use this in all task definitions run-task() { input="$(cat)" wf="$(printf '%s' "$input" | cut -f 1)" body="$(printf '%s' "$input" | cut -f 2-)" flowout "$wf" "$(do_task "$wf" "$body")" } # ShellFlow entry point sfrun() { SFLIB="$(readlink -f "$0")" wfbase="$(basename "$1")" WFDIR="$(readlink -f $(dirname -- "$1"))" cd "$WFDIR" debugout "Starting workflow $wfbase..." flowout $wfbase "$(cat)" | SFLIB="$SFLIB" WFDIR="$WFDIR" $SHELL $wfbase | cut -f 2- } [ "$(basename "$0")" == "flow.sh" ] && sfrun $*