Simple Bash framework which provides argument parsing, usage output and text formatting variables
Find a file
2022-01-15 09:55:28 +01:00
examples add examples 2022-01-15 09:55:28 +01:00
LICENSE add license 2022-01-11 15:41:04 +01:00
README.org rename text formatting variables 2022-01-11 15:46:20 +01:00
sf add sfparr 2022-01-15 09:55:13 +01:00

sf script framework

script framework can be used to simplify and beautify bash scripts. It provides:

  • Argument parsing
  • Usage output
  • Input functions
  • Output functions
  • Text formatting variables

All just by declaring some variables and sourcing it. Or keep your scripts self-contained and include it as an oneliner.

The usage is pretty self-explanatory once you have seen it. If you're curious and don't want to read through the documentation, head directly to the example.


Here is the oneliner version of sf which was created with this tool:

  # sf -- script framework (https://github.com/Deleh/sf)
  sftrs=$'\e[0m';sftbf=$'\e[1m';sftdim=$'\e[2m';sftul=$'\e[4m';sftblk=$'\e[5m';sftinv=$'\e[7m';sfthd=$'\e[8m';sftclr=$'\e[1A\e[K';sftk=$'\e[30m';sftr=$'\e[31m';sftg=$'\e[32m';sfty=$'\e[33m';sftb=$'\e[34m';sftm=$'\e[35m';sftc=$'\e[36m';sftw=$'\e[97m';function sferr { echo "${sftbf}${sftr}ERROR${sftrs} $1";[ -z "$2" ]&&exit 1;};function sfwarn { echo "${sftbf}${sfty}WARNING${sftrs} $1";};function sfask { if [ "$2" == "" ];then read -p "$1? [${sftbf}Y${sftrs}/${sftbf}n${sftrs}] " sfin;[[ "$sfin" =~ y|Y|^$ ]]&&sfin=true||sfin=false;else read -p "$1? [${sftbf}y${sftrs}/${sftbf}N${sftrs}] " sfin;[[ "$sfin" =~ n|N|^$ ]]&&sfin=false||sfin=true;fi;};function sfget { [ "$2" != "" ]&&read -p "$1 [${sftbf}$2${sftrs}]: " sfin||read -p "$1: " sfin;[ "$sfin" == "" ]&&[ "$2" != "" ]&&sfin="$2";};function _sferr { echo "${sftbf}${sftr}SF PARSE ERROR${sftrs} $1";exit 1;};OLDIFS=$IFS;IFS=";";_sfphead="";_sfpdesc="";_sfodesc="";_sfexamples="";_sfpargs=();declare -A _sfflags;declare -A _sfargs;for a in "${sfargs[@]}";do _sfsubst=${a//";"};_sfcount="$(((${#a} - ${#_sfsubst})))";if [ $_sfcount -eq 1 ];then read -r -a _sfparsearr<<<"${a}";_sfpargs+=("${_sfparsearr[0]}");_sfphead="$_sfphead ${_sfparsearr[0]}";_sfpdesc="$_sfpdesc  ${_sfparsearr[0]};${_sfparsearr[1]}\n";elif [ $_sfcount -eq 2 ];then read -r -a _sfparsearr<<<"${a}";_sfflags["-${_sfparsearr[1]}"]="${_sfparsearr[0]}";_sfflags["--${_sfparsearr[0]}"]="${_sfparsearr[0]}";declare ${_sfparsearr[0]}=false;_sfodesc="$_sfodesc  -${_sfparsearr[1]}, --${_sfparsearr[0]};${_sfparsearr[2]}\n";elif [ $_sfcount -eq 4 ];then read -r -a _sfparsearr<<<"${a}";_sfargs["-${_sfparsearr[1]}"]="${_sfparsearr[0]}";_sfargs["--${_sfparsearr[0]}"]="${_sfparsearr[0]}";declare ${_sfparsearr[0]}="${_sfparsearr[3]}";_sfodesc="$_sfodesc  -${_sfparsearr[1]}, --${_sfparsearr[0]} ${_sfparsearr[2]};${_sfparsearr[4]} (default: ${_sfparsearr[3]})\n";else _sferr "Wrong argument declaration: $a";fi;done;for e in "${sfexamples[@]}";do _sfsubst=${e//";"};_sfcount="$(((${#e} - ${#_sfsubst})))";if [ $_sfcount -eq 1 ];then read -r -a _sfparsearr<<<"${e}";_sfexamples="$_sfexamples  ${_sfparsearr[0]};${_sfparsearr[1]}\n";else _sferr "Wrong example declaration: $e";fi;done;IFS=$OLDIFS;function _sfusage { echo -n "Usage: $(basename $0)";[ "$_sfodesc" != "" ]&&echo -n " [OPTIONS]";echo -e "$_sfphead";[ ! -z ${sfdesc+x} ]&&echo -e "\n$sfdesc";if [ "$_sfpdesc" != "" ];then echo -e "\nPOSITIONAL ARGUMENTS";echo -e "$_sfpdesc"|column -c 80 -s ";" -t -W 2;fi;if [ "$_sfodesc" != "" ];then echo -e "\nOPTIONS";echo -e "$_sfodesc"|column -c 80 -s ";" -t -W 2;fi;if [ "$_sfexamples" != "" ];then echo -e "\nEXAMPLES";echo -e "$_sfexamples"|column -c 80 -s ";" -t -W 2;fi;if [ ! -z ${sfextra+x} ];then echo -e "\n$sfextra";fi;exit 0;};for a in "$@";do [ "$a" == "-h" ]||[ "$a" == "--help" ]&&_sfusage;done;while(("$#"));do if [ ! -z ${_sfflags["$1"]} ];then declare ${_sfflags["$1"]}=true;elif [ ! -z ${_sfargs["$1"]} ];then if [ -n "$2" ]&&[ "${2:0:1}" != "-" ];then declare ${_sfargs["$1"]}="$2";shift;else sferr "Argument for '$1' missing";fi;else if [ "${1:0:1}" == "-" ];then sferr "Unsupported argument: $1";else if [ "${#_sfpargs[@]}" != 0 ];then declare ${_sfpargs[0]}="$1";_sfpargs=("${_sfpargs[@]:1}");else sferr "Too many positional arguments";fi;fi;fi;shift;done;if [ ${#_sfpargs[@]} != 0 ];then for p in "${_sfpargs[@]}";do sferr "Positional argument '$p' missing" 0;done;exit 1;fi;unset a e _sfphead _sfpdesc _sfodesc _sfexamples _sfpargs _sfflags _sfargs _sferr _sfusage

Usage

The general usage for writing a script with sf is:

  1. Declare sf-variables at the top of your script
  2. Include sf
  3. Write your script with already parsed arguments, input functions, output functions and text formatting variables

1. sf-variables

This is the list of variables which can be set before including sf. Everything is optional.

Name Description Example
sfdesc Description of the script sfdesc="This script does nothing."
sfargs Array for declaration of arguments, positional arguments and flags. Look below for more information See below
sfexamples Array for declaration of examples for the usage output. Look below for more information See also below
sfextra Additional usage output sfextra="No copyright."

A complete example which uses every variable can be found below.

sfargs

This is an array of strings. Every string defines an argument, a flag or an positional argument of the script. The type is defined by the amount of semicolons in the string.

Type Declaration order Example
Positional argument <name>;<description> sfargs+=("FILE;File to read")
Flag <name>;<shorthand>;<description> sfargs+=("verbose;v;Enable verbose output")
Argument <name>;<shorthand>;<value_name>;<default_value>;<description> sfargs+=("text;t;TEXT;done;Print TEXT when finished")

The order of declaration defines the order in the usage output.

sfexamples

This is also an array of strings. Examples are of the form <command>;<description> and can be added to sf like this:

sfexamples+=("count 8;Count to eight")

2. Include sf

Grab the sf file from the repo, place it next to your script and source it with

  source "$(dirname $0)/sf"

Or just copy and paste the oneliner from above.

3. Write your script

sf deals with missing inputs and handles the parsing of arguments. This means that after sf was included you can be sure that all variables have assigned values. Flags are either false or true, arguments have a provided value or the default value and positional arguments have a provided value.

The values are stored in variables with the name $<name>. If you declared for example a flag like this:

  sfargs+=("verbose;v;Enable verbose output")

Then the variable $verbose exists with a value of either false or true.

Input functions

User input can be requested with two functions. After calling a function, the user input is provided in the variable $sfin.

sfask Takes a string as input and asks for yes or no. If an additional argument is provided (doesn't matter what), no will be default. $sfin is either true or false
sfget Takes a string as input and asks for user input. If a second argument is provided, this will be the default if no user input was entered

Here is a small snippet to show the usage:

  sfget "Please enter your name" "John"
  echo "Hello $sfin"
  sfask "Do you want to proceed"
  if [ "$sfin" == true ]; then
      sfask "Are you sure" "no"
      [ "$sfin" == true ] && echo "Please continue..." || echo "Bye"
  else
      echo "Bye"
  fi

And the execution:

  Please enter your name [John]: Jane
  Hello Jane!
  Do you want to proceed? [Y/n]
  Are you sure? [y/N] y
  Please continue...

Note that the colon and question marks get added by the functions.

Output functions

Two output functions are provided which can be used to throw warnings and errors.

sfwarn Takes a string as input and prints a warning
sferr Takes a string as input, prints an error and exits with code 1. If an additional argument is passed (doesn't matter what), it will just throw an error and don't exit
Text formatting variables

The following text formatting variables can be used to modify the output:

sftrs Reset formatting
sftbf Bold
sftdim Dim
sftul Underline
sftblk Blinking
sftinv Invert foreground/background
sfthd Hidden
sftclr Clear the previous line
sftk Black
sftr Red
sftg Green
sfty Yellow
sftb Blue
sftm Magenta
sftc Cyan
sftw White

The variables can be used directly in echo, no -e needed. To echo the word "framework" bold and red use the variables for example like this:

  echo "${sftbf}${sftr}framework${sftrs}"

Example

Here is an example script which uses sf:

  #!/usr/bin/env bash

  # ----------------------
  # sf -- script framework
  # ----------------------

  # Declare sf variables
  sfdesc="A simple counter."

  sfargs+=("N;Number to count")
  sfargs+=("reverse;r;Count reverse")
  sfargs+=("text;t;TEXT;done;Print TEXT when finished counting")

  sfexamples+=("count 8;Count to eight")
  sfexamples+=("count -r -t go 3;Count reverse from 3 and print 'go'")

  sfextra="No copyright at all."

  # Include sf, this could be replaced with a long oneliner
  source "$(dirname $0)/sf"

  # ----------------------
  # Actual script
  # ----------------------

  if [ "$N" -ge 11 ]; then                   # Use parsed argument
      sferr "I can only count to/from 10"    # Throw an error and exit
  fi

  counter="$N"                                # Use parsed argument
  echo -n "$sftbf"                            # Print everyting from here bold
  while [ "$counter" -ge 1 ]; do
      if [ "$reverse" == true ]; then         # Use parsed argument
          echo "  $counter"
      else
          echo "  $(expr $N - $counter + 1)"  # Use parsed argument
      fi
      counter=$(expr $counter - 1)
      sleep 1
  done
  echo -n "$sftrs"                            # Reset text formatting
  echo "$text"                                # Use parsed argument

The usage output of the above script is:

  Usage: count [OPTIONS] N

  A simple counter.

  POSITIONAL ARGUMENTS
    N  Number to count

  OPTIONS
    -r, --reverse    Count reverse
    -t, --text TEXT  Print TEXT when finished counting (default: done)

  EXAMPLES
    count 8           Count to eight
    count -r -t go 3  Count reverse from 3 and print 'go'

  No copyright at all.