Skip to content

func

Functions that are useful for writing utility functions that use getopts or similar and want to print simple error message in the terminal.

The idea is to print usable message to the user and spent no time creating it.

How it works:

  • Bash function has a help message stored in the comment preceeding the function.
  • We can extract the comment by finding the function definition and parsing the file.
  • The comment becomes the help message.
  • Usage is extracted from comment by parsing lines that match the format of https://github.com/Kamilcuk/mkdocstrings-sh .
  • # @option -o <var> description - describes a short option taking an argument
  • # @option -o description - describes a short option not taking an argument
  • # @arg name description - describes an argument called name.
  • # @usage description - allows to specify custom usage line

How to use:

  • Use L_func_help to print the help message.
  • Use L_func_error "error message" || return 2 to print the error message with usage of the function, and then return from your function.
  • Use L_func_assert "not enough arguments" test "$#" -lt 1 || return 2 to print the error message with usage of the function and then return from your function when the command test "$#" -lt 1 fails, which effectively checks if there are enough arguments.

func

Function for writing function programs.

L_func_comment

Extract the comment above the function.

By default, get the comment of the calling function.

Example

# some unrelated comment followed by an empty line

# some comment
somefunc() {
   L_func_comment
}

somefunc  # outputs '# some comment'

L_func_comment -f somefunc

Options:

  • -v <var> Assign result to this variable.
  • -f <funcname> Print the comment of given function instead of the calling function.
  • -s <int> Consider the calling function this many stackframes above. Default: 0
  • -b Use Bash, do not use sed. For testing.
  • -h Print this help and return 0.

Return: 0 if extracted an non-empty comment above the function definition.

L_func_help

Print function comment as usage message.

:

# @option -t this is an option
# @option -g <arg> this is an option with an argument
# @option -h Print this help and return 0.
# @arg arg This is an argument
utility() {
  local OPTIND OPTARG OPTERR opt t g
  while getopts tg:h opt; do
    case "$opt" in
      t) t=1 ;;
      g) g=$OPTARG ;;
      h) L_func_help; return 0 ;;
      *) L_func_usage; return 2 ;;
    esac
  done
  shift "$((OPTARG-1))"
  L_func_assert "one positional argument required" test "$#" -eq 1 || return 2
  #
  : utility logic
}

utility -h        # prints the comment above the function
utility -invalid  # prints 'Usage: utility [-th] [-g arg] arg'

Argument: [int] How many stack frames up.

Return: 0

See:

L_func_usage

Print funtion usage to stderr.

Argument: [$1] How many stack frames up.

L_func_error

Print function error to stderr.

Arguments:

  • [$1] Message.
  • [$2] How many stack frames up.

See: L_func_help for example

L_func_usage_error

Print function error with usage.

Arguments:

  • [$1] Message.
  • [$2] How many stack frames up.

See: L_func_help for example

L_func_assert

Assert that the command exits with 0.

If it does not, call L_func_error and return 2. If the message starts with -[0-9]+, the number is used as the number of stackframes up the message is about.

Example

utility() {
  local num="$1"
  L_func_assert "not a number: $num" L_is_integer "$num" || return 2
}

Arguments:

  • $1 Message to print, may be empty.
  • $@ Arguments to test.

Return: 2 if the expression failed.

L_func_log

Print a line prefixed by the calling function name.

Argument: $@ line to print

L_function_copy

Make a copy of a function.

Currently there is no sanitization done in the function. The second argument allows for execution under eval.

Arguments:

  • $1 existing function
  • $2 new function name

L_function_modify

Add a script on the top or the end of a function.

Arguments:

  • $1 The function to modify
  • $2 Script to put in front of the function body.
  • $3 Script to put on the end of the function body.

L_decorate

Apply a decorator on a function.

The next call on a function will call the decorator with arguments followed by function name with arguments.

Example

func() { echo something; }
print_and_call() {
    echo "CALLING: $@" >&2
    "$@"
}
func  # outputs something
func  # outputs something
L_decorate print_and_call func
func  # outputs "CALLING func" and then "something"
func  # outputs "CALLING func" and then "something"

Example

func() { L_print_traceback; }
L_decorate L_setx func
L_decorate time func
L_decorate L_setx time func
func arg  # calls: [L_setx time func arg]
          # which calls [time func arg]
          # which calls L_setx func arg

Arguments:

  • $@ Decorator to apply with arguments.
  • $#-1 Function.

L_time

Measure time with the command, but include the command in the time message output and use %6l format.

Argument: $@ command to measure.

L_duration_to_usec

Parse 1w1d1h2m2s into number of microseconds.

Option: -v <var>

Argument: $1 Duration string.

See: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#configuration-file

L_duration_to_usec_v

L_cache

Cache the execution of a command.

The command execution is cached in _L_CACHE global variable or in file when -f option is present. The second execution of the command will result in a cached execution. On cached execution the exit status of the command will be extracted from the cache.

Example

L_cache -T 10m -O output -f /tmp/cache.L_cache curl -sS https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html

myfunc() {
   var=$(( 1 + 2 ))
}
L_decorate L_cache -v var -k myfunc myfunc
myfunc
myfunc

Options:

  • -o

    Cache the stdout of the command.

    It will run the command in a process substitution.

  • -O <var>

    Cache the stdout of the command and store it in variable instead of printing.

    It will run the command in a process substitution.

  • -s <var> Add this variable to the cache. All cache variables will be restored on cached execution.
  • -f <file>

    Use the file as cache.

    The file has a header with version number. The file stores internal cache state from declare -p _L_cache variable. The file content is eval-ed upon loading.

  • -r Instead of executing, remove the cache entry associated with the command.
  • -l Instead of executing, only list the entires in the cache.
  • -T <ttl> Set time to live in duration string. Default: infinity.
  • -L <01> Lock the file with flock. Default: use flock if available.
  • -k <key> Use this key to index the cache. Default: %q quoted command with argumnets.
  • -h Print this help and return 0.

Arguments:

  • $1 Command to execute.
  • $@ Arguments.

Sets variable: _L_CACHE

Uses environment variable: _L_CACHE

Shellcheck disable= SC2094

Return:

222 on internal error

otherwise returns the exit status of the cached command.

L_handle_v_scalar

Wrapper function for handling -v arguments to other functions.

It calls a function called <caller>_v with arguments, but without -v <var>. The function <caller>_v should set the variable nameref L_v to the returned value. When the caller function is called without -v, the value of L_v is printed to stdout with a newline. Otherwise, the value is a nameref to user requested variable and nothing is printed.

The fucntion L_handle_v_scalar handles only scalar value of L_v or 0-th index of L_v array. To assign an array, prefer L_handle_v_array.

:

L_hello() { L_handle_v_arr "$@"; }
L_hello_v() { L_v="hello world"; }
L_hello          # outputs 'hello world'
L_hello -v var   # assigns var="hello world"

Option: -v <var> Store the output in variable instead of printing it.

Argument: $@ arbitrary function arguments

Shellcheck disable= SC2317

Exit: Whatever exitcode does the <caller>_v funtion exits with.

See: L_handle_v_array

L_handle_v_array

Version of L_handle_v_scalar for arrays.

The options and arguments and exitcode is the same as L_handle_v_scalar.

This additionally supports assignment to arrays. This is not possible with L_handle_v_scalar.

The function L_handle_v_scalar is slightly faster and uses printf -v to assign the result. On newer Bash, printf -v can assign to array and associative arrays variable indexes.

In constract, L_handle_v_array has to first assert if the string is a valid variable name. Only then it uses eval with an array assignment syntax to assign the result to the user requsted variable.

Currently array indexes are not preserved. This could be worked on in the future when needed.

:

L_hello() { L_handle_v_arr "$@"; }
L_hello_v() { L_v=(hello world); }
L_hello          # outputs two lines 'hello' and 'world'
L_hello -v var   # assigns var=(hello world)

Shellcheck disable= SC2317

See: L_handle_v_scalar.