con-lib.bash

con-dev base bash library.

Author: absurd@olurdix.de
Date: 20260830
Copyright: GPL-2
Version: con-dev-0.3.3.snapshot20260830093111git51b7b7c+local
Manual section:3
Manual group:bash

SYNOPSIS

#!/bin/bash -e
PATH="${PATH}:/usr/share/con-dev" . con-lib.bash
[con::include ...]
[con::cmd:depends ...]
[con::cmd:suggests ...]

con::include may be used to pull in other con-dev libraries (see below).

con::cmd:depends and con::cmd:suggests may be used to pull in non-standard program requirements (see below) that should be globally available (it is often more desirable to use this in a specific scope, though).

Note

PATH environment may be used to initialize con-dev from a non-standard installation location like so: PATH="${PATH}:/usr/local/share/con-dev" my-con-script

DESCRIPTION

This is the base library for the con-dev "bash library suite"; all other libraries implicitly depend on it.

Contents

ENVIRONMENT

con-dev bash libraries can be globally influenced by these environment variables:

con-dev bash library code expects to be run with certain bash options enabled, so it implicitly sets these on initilization of the base library. You really should not change this precondition -- however you can if you really want to so

CONSTANTS

Sourcing and Including

con::source <prefix> [[@]<id>...]

Source a "bash library", con-dev style.

Arguments

  • <prefix>: The prefix for the libraries; con-dev, for example, uses the prefix "con", implying CON_SOURCE_DIR has previously has been set correctly to the directory containing the bash libraries, and the bash libraries' file naming scheme is "con-<id>.bash".
  • <id>: Library identity to source; if prefixed with "@", we additionaly automatically include the correct implementation (of an abstract library).

Globals

  • <prefix>_SOURCE_DIR: All your bash code needs to be in this directory. This needs to have been set globally prior to calling this function. The bash code files must use the naming scheme "<prefix>-lib<id>.[impl.]bash".
  • <prefix>_LIB<id>_SOURCED=yes: When a lib has been sourced successfully, we set this. When this is set, the resp. library will be silently skipped (not sourced again).
  • <prefix>_<id>=<IMPL>: Abstract libraries: Set when an implementation of an abstract library has been sourced, to the resp. implementation ID string.
  • <prefix>_<id>_FORCE=<IMPL>: Abstract libraries: When set (from outside), this will always be used (i.e., no detection will be run).

Abstract Library Implementation Detection System

To add a new library implementation auto detector, just add a detect function named con_lib${LIBID}::detect:[ORDER:]${IMP}.

The detect function must return 0 only in case the resp. system is detected. It should write the resp. found "conffile" (if any) to stdout (this will be available in the code as CON_${LIBID}_CONFFILE).

Use optional [ORDER:] prefix if you want or need an order in which the detection functions are called ("sort -n" is used to order the function names; the first succeeding function wins).

con::include [[@]<id>...]

Include a con-dev 'bash library'.

This is con::source with the hardcoded prefix "con".

cmd: Handling of External Commands

con::cmd:guessVersion <cmd>

Try some heuristics to find a version string from the version output of the command. Caution, as this will actually call the command with varying arguments.

con::cmd:getVersion:debian <file>

This should probably go to a OS specific lib.

con::cmd (@[<default>]|=|<name>) <cmdLine>...

Check command lines for availability, and either run them now or set global CON_CMD_`NAME` for later use.

Arguments

  • @[<default>]|=|<name>:
    • Use "@" (with optional default value) to check and run the given command lines until one succeeds. Results in the output of the first succeeding command line, or DEFAULT if no line succeeds; error output will be ignored.
    • Use "=" or "NAME" to check for the availabilty of the base command and set CON_CMD_`NAME`. When "=" is used, "NAME" will be be computed from the base name of the base command (i.e., "/usr/bin/my-command arg1 arg2" becomes CON_CMD_MY_COMMAND).
  • <cmdLine>: Command, optionally plus arguments.

You would usually use con::cmd:depends() and con::cmd:suggests() wrappers (see below) to implicitly cope with error handling.

See also: STYLEGUIDE, Compliance and Compatibility.

Examples:

# Depend on the availabilty of the tool *cryptsetup*. Sets *CON_CMD_CRYPTSETUP*.
con::cmd:depends "=" "cryptsetup"

# Suggests the tool *zenity*; user warning if not available. Sets *CON_CMD_ZENITY* when found.
con::cmd:suggests "=" "zenity"

# Depend on a tool in a non-standard location with an alternate and a custom name. Sets *CON_CMD_GIT2CL* when found.
con::cmd:depends "GIT2CL" "/usr/share/gnulib/build-aux/gitlog-to-changelog" "git log --pretty --numstat --summary"

# Guess distribution codename
MY_PROG_CODENAME="$(con::cmd "@unknown" "lsb_release --codename --short" "uname --kernel-name")"

con::cmd:(depends|suggests) (@[<default>]|=|<name>) <cmdLine> [<cmdLine>...]

Like con::cmd(), but

  • depends: throws if no command line is applicable.
  • suggests: warns if no command line is applicable.

term: Terminal Support

con::term:set <attr>[,<attr>...]

Set terminal attributes.

log: Logging

con::log:level

Print current log level number.

con::log:levelName

Print current log level string.

con::log:debugMode

true if loglevel is >= DEBUG.

con::log:backtrace [<start>=0]

Create a debug log output, for error perusal.

con::log:init [<level>]

Initialize logging; if <LEVEL> if given, it will be checked and "normalized" to a known sane log level. <LEVEL> may be given as number or as string.

con::log:levelInfo

Print current log level status to stdout.

con::log <level> <prefixTerm> <messageTerm> <format> [<arguments>...]

Generic log function; usually you just use one of the shortcuts below.

con::log:fatal <format> [<arguments>...]

Log a fatal log: Issue the message, log a debug backtrace, and exit the program with error code CON_ERRSTATUS_FATAL.

con::log:(error|warn|notice|info|debug) <format> [<arguments>...]

Log shortcuts for all non-fatal levels. Use level notice for messages the user should usually always see.

trap: Handling Cleanups and Errors

con::trap <sigspec> <commandLine>...

Wrapper for bash builtin "trap", allowing multiple trap commands and subshell-local trap commands.

This is like builtin "trap", but the given command is added to the list of commands to run, and it works seemlessly for subshells. The commands are run in reverse order (i.e., last added runs first). The command line may be given just like any other shell code after the SIGSPEC.

Unlike with the builtin "trap", only one signal may be given on the command line, so that no trap functions will be overwritten; for the same reason, you must not mix this call with calls to the trap bultin directly.

Examples:

con::trap EXIT    rm -v "my temp file with spaces"
con::trap ERR     printf "%s\n" "Ough, some error occured."
con::trap SIGALRM printf "%s\n" "Got alarm signal."

con::trap:throw <message> [<errStatus>=CON_ERRSTATUS_THROW]

Set up internal variables for later catch handling, and fail with errStatus.

This will stop processing at this point, and the run all traps set up for ERR -- until the final (internal) catch trap which will do the generic error handling, and exits the script.

con::trap:catch [<defaultMessage>...]

Catch and finalize an error trap.

con::trap:mktemp <varName> [<mktempOption>...]

Wrapper for mktemp(1) with automatic removal on script exit.

var: Variable Handling

Various tools and helpers around bash variables.

Note

bash 4.2 and 4.3 have introduced further features to help with variable handling. Even with 4.3 however, there is still not enough support to get rid of this evilry completely, plus we currently support compatibility down to 4.2. So for the time being, most of these helpers are still used or needed.

Version con-dev Feature Notes
2.0 ${!var} Should be preferred over con::var:get where convenient.
4.2 declare -g / local -g Obsoletes con::var:set, con::var:setArray when global variables should be set (still needed to set variables in-scope only).
4.3 declare -n / local -n May be used as alternative to con::var:get or code like ivar="${a}_${b}"; printf "${!ivar}".

con::var:get <varName>

Get a variable value.

For convenience, you may use this filthy little devil where the usual bash idioms ${!var} or declare -n var run short, for example:

value="$(con::var:get "VAR")"                    # Equivalent to ``value="${VAR}"`` (rather use the bash idiom).
value="$(con::var:get "${ivar}")"                # Equivalent to ``${!ivar}`` (rather use the bash idiom).
value="$(con::var:get "${x}_${y}")"              # When your variable name is a build from other variables.
value="$(con::var:get "${my}_${array}[${i}]")"   # Works for arrays too...

con::var:set <varName> <value>

Indirectly set a variable.

For convenience, you may use this filthy little devil where the bash syntax runs short, for example:

con::var:set "VAR" VAL                       # Equivalent to ``VAR="VAL"`` (rather use the bash syntax).
con::var:set "${ivar}" VALUE...              # When your variable name is in a variable.
con::var:set "${x}_${y}" VALUE...            # When your variable name is a build from other variables.
con::var:set "${my}_${array}[${i}]" VALUE    # Works for arrays too...

con::var:(setArray|addArray) <arrayVarName> [<value>...]

Like con::var:set, but sets/adds all <value> arguments as array.

con::var:copyArray <srcVar> <destVar>

Copy array variable into another.

con::var:copyAArray <srcVar> <destVar>

Copy assosiative array variable into another.

con::var:printAArray <arrayVar>

Print assosiative array variable (mostly for debugging).

con::var:(isSet|isEmpty) <varName>

Check if a variable named <varName> is set or empty, resp.

con::var:(setIfUnset|setIfEmpty) <varName> [<value>...]

Set <varName> if unset or empty, resp.

con::var:setVarCode <id> <value> [<prefix>]

Print code (suitable vor eval) to set a variable.

Note

This will quote using ", and escape any occurence of " in value.

con::var:setLocalVarCode <id> <value>

Just like con::var:setVarCode, but using prefix "local ".

list: Delimiter-separated Value Lists

A list in this context is any string containing tokens separated by a special delimiter character (DSV).

By design, all these function are independent of the current IFS value; the delimiter is fixed to be the SPACE character, or must be given explicitely with any call.

1st, DSV support gives you join() and split() to convert to and from DSVs and work with bash arrays internally.

2nd, there are convenience functions to do some manipulations on DSVs directly.

Example 1: Using bash arrays internally:

declare -a MY_PROG_MY_ARRAY
con::list:split MY_PROG_MY_ARRAY "my,comma,separated,list" ","
... work with MY_PROG_MY_ARRAY internally...
printf "Result: %s" "$(con::list:join MY_PROG_MY_ARRAY ",")"

Example 2: Directly manipulate a DSV string:

# Add a user path to PATH to front, and delete any duplicates in PATH
PATH="$(con::list:uniq "$(con::list:prepend "${PATH}" "${HOME}/bin" ":")" ":")"

con::list:split <arrayVarName> <list> [<delimiter>=" "]

Split a list into an array variable.

con::list:join <arrayVarName> [<delimiter>=" "]

Join an array variable into a list.

con::list:insert <list> <token> [(append|prepend) [<delimiter>=" "]]

Insert a token at the beginnng or end of a a list. Rather use con::list:append() or con::list:prepend() shortcuts than this function.

con::list:uniq <list> [<delimiter>=" "]

Remove duplicates from list.

con::list:reverse <list> [<delimiter>=" "]

Reverse list tokens ("A B C" -> "C B A").

func: Function Support

con::func:exists <func>

Check if function exists.

con::func:list [<regex>]

Print defined function names, optionally limited by REGEX.

con::func:copy <src> <dst>

Monkey-copy a function.

con::func:rename <src> <dst>

Monkey-rename a function.

con::func:getopts <optionSyntax> : <options>

Option support for functions.

This allows to have proper option arguments for functions like those known from executables. It requires the eval trick, and will also considerably slow things down, so usage should be limited to "top level" functionality.

Example:

my_function()
{
  eval "$(con::func:getopts "p=Value" "d=" "T=string" 0 : "${@}")"
  if ${_d_given}; then
    printf "-d given: %s\n" "${_d}"
  fi
  ...
}
...
my_function -p 3 -d 17 -T int

type: Handle Variable Types

con::type:readlineHistoryCompletion <item> [<item>...]

Helper: Set readline history and bind TAB to history completion.

con::type (-B|-R|-V <varName>) <type>[:<extra>]

Validate, readline-complete or bash-complete a variable type.

  • -B: Print bash completion code (compgen args).
  • -R: Set up readline completion.
  • -V varName: Validate variable content.
  • <type>[:<extra>]:
    • Either one of the internal types: (string[:<regex>]|int[:<min>:<max>]|choice:<choice>[:<choice>...]|bool).
    • or any valid arg for 'complete -A' (see 'man bash'): <completeAction>.
    • or a name of a custom type function to call, serving -B, -R, -V: <functionName>.

ui: User Input

con::ui:ask [-p <prompt>] [-d <default>] <varName>

con::ui:read wrapper for simple yes/no questions.

Functions: Basic Tools

con::compareVersions VERSION1 (le|lt|ge|gt) VERSION2

Compare versions (via coreutil's 'sort --version-sort').

con::str2tec <string>

Convert a arbitrary string to an identifier usually suitable for technical purposes.

con::regexExtract <string> <regex> [<match>=1]

Extract a submatch from regular expression.

con::relMax <relation> <value>...

Get arithmetic maximum for any relation.

con::(max|min) <value>...

Get arithmetic maximum or minimum, resp.

con::xargs <command> <arg>...

Run <command> for each argument given as first argument.

con::qos <command> <arguments>...

Run command, be quiet on success.

con::xchars <command> <string>

Run <command> for each character in <string> as first argument.

con::filterXLines <command>...

Run <COMMAND> for each line in stdin as first argument.

con::filterLinePrefix <prefix>

Filter each line to be prefixed by <prefix>.

con::backtrace [<start>=0]

Backtrace using builtin "caller" for all found levels.