Last active
January 30, 2018 13:58
-
-
Save bruno-de-queiroz/a1c9e5b24b6118e45f4eb2402e69b2a4 to your computer and use it in GitHub Desktop.
Autocomplete example and annotation parser for bash scripts to make easier to expose functions and flags, to create help info and short command list for autocompletion script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Autocomplete example and annotation parser for bash scripts to make easier to expose functions and flags, | |
# to create help info and short command list for autocompletion script | |
# | |
# Usage: | |
# | |
# source $SCRIPT_PATH/core.sh | |
# ... | |
# #@flag -e|--environment | |
# #@description sets the environment to use. default: "production" | |
# ENVIRONMENT=${ENVIRONMENT:-"production"} | |
# ... | |
# #@public | |
# #@description run the script | |
# function run(){ | |
# ... | |
# } | |
# | |
# init $0 "$@" | |
# | |
GLOBAL_SCRIPT="" | |
GLOBAL_METHODS="" | |
GLOBAL_FLAGS="" | |
function _shortlist(){ | |
if [ -z "$1" ]; then | |
echo -ne "$GLOBAL_METHODS" | cut -f 1 -d " " | |
else | |
if [ ! -z "$2" ]; then | |
if [[ "$2" =~ ^- ]]; then | |
return 0 | |
fi | |
fi | |
echo -ne "$GLOBAL_FLAGS" | cut -f 1 -d " " | tr "|" " " | |
fi | |
} | |
function _help(){ | |
local methods=$(echo -e "$GLOBAL_METHODS" | cut -f 1 -d " " | tr '\n' ' ') | |
local flags=$(echo -e "$GLOBAL_FLAGS" | cut -f 1 -d " " | sed -E 's/\|\-\-[^[:space:]]+//g' | tr '\n' ' ') | |
echo -n "Usage: $(basename ${GLOBAL_SCRIPT//.sh/}) [ ${methods}]" | |
if [ -n "$GLOBAL_FLAGS" ]; then | |
echo -n "[ $flags]" | |
echo "" | |
echo "" | |
echo "Flags:" | |
echo -e "$GLOBAL_FLAGS" | |
echo "" | |
else | |
echo "" | |
echo "" | |
fi | |
echo "Commands:" | |
echo -e "$GLOBAL_METHODS" | |
} | |
function init(){ | |
local all=($@) | |
local script=${all[0]} | |
local content=$(cat $script | sed -n -E '/#@/,/^[^#]/ p' | tr '\n' '%' | sed -E 's/(%[^#]+)%#/\1\\n#/g') | |
all=(${all[@]:1}) | |
local str="${all[@]}" | |
local flags="" | |
local methods="" | |
#TODO try to put this inside a method | |
#Iterating through lines on $content | |
while read -r f; do | |
local is_flag=$(echo $f | grep '#@flag') | |
local is_method=$(echo $f | egrep '#@public.*function') | |
if [[ "$is_flag" ]]; then | |
local flag=$(echo $f | sed -E 's/.*#@flag([^\%]+)%.*/\1/g') | |
local property=$(echo $f | sed -E 's/.*%([^=]+)\=.*/\1/g') | |
local description=$(echo $f | sed -E 's/.*#@description([^\%]+)%.*/\1/g') | |
#Adding content to variable that holds flags descriptions | |
flags+=$(printf "$([[ -n "$flags" ]] && echo '\\n')%-20s %s" $flag "$description") | |
#If flag has been passed set the variables to the value passed | |
if [[ "${str}" =~ ($flag)([[:space:]]|=)([\"|\'][^\"]+[\"|\']|[^[:space:]]+) ]]; then | |
local line=${BASH_REMATCH[0]} | |
local key=${BASH_REMATCH[1]} | |
local value=${BASH_REMATCH[3]} | |
#Setting the variable with the value | |
export $property="$value" | |
str="${str//$line/ }" | |
fi | |
content=${content//"#@flag $f\n"/} | |
elif [[ "$is_method" ]]; then | |
local method=$(echo $f | sed -E 's/.*function([^\(]+)\(\).*/\1/g') | |
local description=$(echo $f | sed -E 's/.*#@description([^\%]+)%.*/\1/g') | |
#Adding content to variable that holds methods descriptions | |
methods+=$(printf "$([[ -n "$methods" ]] && echo '\\n')%-20s %s" $method "$description") | |
fi | |
done <<< "$(echo -e $content)" | |
methods+=$(printf "$([[ -n "$methods" ]] && echo '\\n')%-20s %s" help " show options and flags available") | |
all=($str) | |
GLOBAL_SCRIPT=$script | |
GLOBAL_FLAGS=$flags | |
GLOBAL_METHODS=$methods | |
#Verify if the method called is public | |
local first=${all[0]} | |
all=(${all[@]:1}) | |
if [ -n "$(echo -e $content | egrep "#@(public|protected).*function $first\(\)")" ]; then | |
#Call method with the arguments passed | |
$first "${all[@]}" | |
exit 0 | |
fi | |
#Show help if the method called was not found | |
case $first in | |
shortlist) | |
echo $(_shortlist "${all[@]}") | |
;; | |
*|help) | |
_help "$script" "$flags" "$methods" | |
;; | |
esac | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
_foo() | |
{ | |
local cur index method option last opts src | |
COMPREPLY=() | |
src="/usr/local/bin/foo" | |
cur="${COMP_WORDS[COMP_CWORD]}" | |
method="" | |
option="" | |
last="" | |
if [[ $COMP_CWORD -gt 1 ]]; then | |
index=$(($COMP_CWORD - 1)) | |
first=$(($COMP_CWORD - $index)) | |
method="${COMP_WORDS[first]}" | |
if [[ $COMP_CWORD -gt 2 ]]; then | |
option="${COMP_WORDS[first+1]}" | |
if [[ $COMP_CWORD -gt 3 ]]; then | |
last="${COMP_WORDS[index]}" | |
fi | |
fi | |
fi | |
opts=$($src shortlist ${method} ${option} ${last}) | |
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) | |
return 0 | |
} | |
complete -F _foo foo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# @description module name | |
#import core.sh | |
. core.sh | |
# All variables with the @flag annotation will be exposed as flag, and the @description is required. | |
#@flag -t|--test | |
#@description description of the flag | |
LOG_MODULE_TEST_FLAG="" | |
# Methods with the @public annotation will be exposed as command, and the @description is required. | |
#@public | |
#@description prints $LOG_MODULE_TEST_FLAG value | |
function test(){ | |
echo $LOG_MODULE_TEST_FLAG | |
} | |
# Methods with the @protected annotation will not be exposed as command in helps and autocomplete | |
# but can be called from command-line | |
#@protected | |
function protected(){ | |
echo "Protected method" | |
} | |
# Methods with no annotations will not be exposed as command in helps and autocomplete | |
# and can only be called by another scripts if they import the script. (i.e.: source $LOG_SCRIPTS/modules/test.sh) | |
function private(){ | |
echo "private" | |
} | |
# Bootstraps the parser | |
init $0 "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ ./foo.sh help | |
Usage: foo [ hello ] [ -e|--environment ] | |
Flags | |
-e|--environment sets the environment to use. default: "production" | |
Options | |
hello prints hello world | |
$ ./foo.sh hello | |
hello world production | |
$ ./foo.sh hello -e test | |
hello world test | |
$ ./foo.sh hello --environment dev | |
hello world dev |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment