MJN All Blog Cheatsheets Elasticsearch GCP JS LinuxBash Misc Notes Other ShortcutKeys / - Search

Home / Other / Command Line Interface Builder / CLI Builder script


This bash script generates a cli (command line interface) bash script from a definition file. The structure of the definition file is code header below. This script also generates an alias file which contains a set of alias commands which run each command in the definition file using the associated shortcut prepended with an ‘@’.

#!/bin/bash
help_text="
NAME
  cli-builder - A bash script which generates a CLI bash script from a definition file.

USAGE
  cli-builder [-d] <definition_filename>

OPTIONS
  -d
    Run in debug mode.

  -h|--help
    Show help text.

DESCRIPTION
  This bash script generates a cli (command line interface) bash script from a definition file.
  The structure of the definition file is described below.  This script also generates an alias
  file which contains a set of alias commands which run each command in the definition file using
  the associated shortcut prepended with an '@'.

  Definition File Structure
   -------------------------

  # - a line starting with a # is a comment

  = - a line starting with a = is a group description for the set of commands following this
      line up until the next line starting with a = or the end of the file.

  ( / - is the line continuation character.  Any line ending / is joined to the next line. )

  All other lines in the file are command definitions.  These lines are structured as follows:

  name-1..name-n (shortcut) <param> [<opt_param] :: command \$1 \$2

  Where:

  name-1..name-n
    A list of words which describe the command and which are typed to run the command.

  shortcut
    A single word which can also be typed to run the command.  If the aliases are created
    then the alias can also be run direct from the command as @alias.

  param
    A mandatory parameter.  There can be 0 or more mandatory parameters.

  opt_param
    A optional parameter.  There can me 0 or more optional parameters.  The must always follow
    the mandatory parameters and there can be no gaps.  This means if optional parameter 3 is
    passed in so must optional parameters 1 and 2.

  ::
    Separates the command definition with the Linux command that is run.

  command
    Is an Linux command.  Parameter values entered after the command are specified using there
    position preceded by a \$.  e.g \$1, \$2.

  Example

  sort_cli.def:
  ----------------------------------------------------------------
  # FILE OPTIONS
  sort file (sf) <filename> <sort_parameter> :: cat \$1 | sort \$2
  ----------------------------------------------------------------

  This example sorts a file specified by a mandatory filename.  An optional sort parameter can
  be passed to modify the sort order.  Once the cli is built the command can be run as follows.
  Note: the name of the cli command is the same as the name of the definition file
  (without the .def).

  > sort_cli sort file my_file

  > sort_cli sf my_file --ignore_case

  > @sf my_file

AUTHOR
  mjnurse - 2019
"
name=${0##*/}
try="Try '$name -h' for more information"

# Get usage from help_text.
usage=${help_text##*USAGE}
usage=${usage%%OPTIONS*}
usage=${usage%%PARAMETERS*}
usage=${usage%%DESCRIPTION*}
usage=${usage//[$'\n\r']}
usage=${usage##*   }
usage="Usage: ${usage}"

function debug {
  if [[ $debug_yn == y ]]; then
    echo "$*"
  fi
}

debug_yn=n

if [[ "$1" == "" ]]; then
  echo $usage
  echo $try
  exit
elif [[ "$1" == "-h" || "$1" == "--help" ]]; then
  echo "$help_text" 
  exit
elif [[ "$1" == "-d" ]]; then
  debug_yn=y
  shift
fi

def_file="${1/.def/}"

if [[ ! -f $def_file.def ]]; then
  echo "Error: Definition file \"$def_file.def\" missing"
  exit
fi
cli_name=${def_file}
defns="$(cat $def_file.def | sed 's/^  *//' | sed ':a;/\\ *$/{N;s/\\ *\n//;ba}' | sed '/^ *#/d; s/(/('${cli_name:0:1}'/')"

CCYA="\x1b[96m"
CGRE="\x1b[92m"
CMAG="\x1b[95m"
CWHI="\x1b[97m"
CDEF="\x1b[0m"

echo '#!/bin/bash

debug_yn=n
if [[ "$1" == "-d" ]]; then
  debug_yn=y
  shift
fi
if [[ "${CLI_DEBUG^^}" == "TRUE" ]]; then
  debug_yn=y
fi

l80="--------------------------------------------------------------------------------"
' > $cli_name

echo '# Shortcut aliases' > $cli_name.alias

defns=$defns'
= HELP
help ('${cli_name:0:1}'he) :: egrep "usage=|section=" "$0" | grep -v "grep" | sed "s/.*usage=/   /; s/.*section=/'$CGRE'/; s/\"//g"
'

echo '
function check_params() {
  # param 1 - actual number of parameters
  # param 2 - required number of parameters
  # param 3 - incorrect parameters message

  if [[ "$1" < "$2" ]]; then
    echo "$3"
    exit
  fi
}

function print_command() {
  # param 1 - command

  if [[ $debug_yn == y ]]; then
    echo "COMMAND: $*" | sed 's/./-/g'
    echo "COMMAND: $*"
    echo "COMMAND: $*" | sed 's/./-/g'
  fi
}
' >> $cli_name

debug "Commands"
debug "--------"

echo "$defns" | \
while IFS= read -r line ; do
  if [[ "${line:0:1}" == "=" ]]; then
    echo "section=\"${line:2}\""  >> $cli_name
  elif [[ "${line:0:4}" == "cmd " ]]; then
    echo "${line:4:99}" >> $cli_name
  elif [[ "$line" != "" ]]; then
    cmd="$(echo $line | sed 's/^.*:://; s/##.*//')"
    usage="$(echo ${line%% ::*} | sed "s/\[/\x1b[37m\[/g; s/\]/\]$CWHI/g; s/^/$CMAG/; s/(/$CCYA(/; s/)/)$CWHI/; s/$/$CDEF/")"
    if echo $line | grep "##" > /dev/null; then help="$(echo $line | sed "s/^.*## */$CGRE # /; s/$/\x1b[0m/")"; else help=""; fi
    word_list="${line%%::*}"
    word_list=${word_list//[/ [ }
    word_list=${word_list//]/ ] }

    key_word_template=""
    key_words=""
    num_mandatory_params=0
    optional_params_yn=n
    optional_params=""
    mandatory_params=""
    shortcut=""
    word_count=0

    for word in $word_list; do
      if [[ "$word" == "[" ]]; then
        optional_params_yn=y
      elif [[ "$word" == "]" ]]; then
        optional_params_yn=n
      elif [[ "${word:0:1}" == "<" ]]; then
        if [[ "$optional_params_yn" == "y" ]]; then
          optional_params="$optional_params $word"
        else
          mandatory_params="$mandatory_params $word"
          let num_mandatory_params=num_mandatory_params+1
        fi
      elif [[ "${word:0:1}" == "(" ]]; then
        shortcut=${word/(/}
        shortcut=${shortcut/)/}
        echo 'alias @'${shortcut}'="'$cli_name $shortcut'"' >> $cli_name.alias
      else
        let word_count=word_count+1
        key_words="$key_words $word"
        key_word_template="$key_word_template "'$'"$word_count"
      fi
    done

    key_words=${key_words:1}
    key_word_template=${key_word_template:1}
    mandatory_params=${mandatory_params:1}
    optional_params=${optional_params:1}
    while [[ "${cmd:0:1}" == " " ]]; do
      cmd=${cmd:1}
    done

    debug "- $key_words ($shortcut)"

    echo "if [[ \"$key_word_template\" == \"$key_words\" || \""'$1'"\" == \"$shortcut\" ]]; then"  >> $cli_name
    echo "   if [[ \""'$1'"\" == \"$shortcut\" ]]; then shift; else shift $word_count; fi"  >> $cli_name
    echo "   usage=\"$usage $help\""  >> $cli_name
    echo "   check_params "'$'"# $num_mandatory_params \"Usage: "'$'"usage\""  >> $cli_name
    if [[ "$key_words" != "h" && "$key_words" != "help" ]]; then
      echo "   print_command '$cmd'"  >> $cli_name
    fi
    echo "   $cmd"  >> $cli_name
    echo "   exit"  >> $cli_name
    echo "fi"  >> $cli_name
  fi
done

echo '
if [[ "$1" == "" ]]; then
  echo "No option passed"
else
  echo "$*: invalid option"
fi
echo "Try \"'$cli_name' help\" for more information."
' >> $cli_name

chmod +x $cli_name

cp $cli_name.alias ~

This page was generated by GitHub Pages. Page last modified: 25/04/24 13:15