Skip to content

Instantly share code, notes, and snippets.

@janderudder
Last active January 11, 2021 14:50
Show Gist options
  • Save janderudder/e9ecbaa727cfdba2bb058cc0d2bbdbf6 to your computer and use it in GitHub Desktop.
Save janderudder/e9ecbaa727cfdba2bb058cc0d2bbdbf6 to your computer and use it in GitHub Desktop.
Man Grep : conveniently search for a command's options descriptions
#!/bin/bash
declare -r Base_name=$(basename -s .sh "$0")
help()
{
cat <<-EOD
Man Grep
Conveniently lookup a keyword in a man page.
Usage:
${Base_name} [GREP-OPTIONS... [--]] COMMAND KEYWORD
GREP-OPTIONS (optional)
Options that will be passed directly to grep.
You may end this list with '--' to avoid ambiguity.
COMMAND
The name of the command of which the man page will be scanned.
KEYWORD
The sequence of characters to search for in the man page of COMMAND.
This could be a Grep's pattern, but will typically be just a flag.
Examples:
${Base_name} ls -A
${Base_name} -w ls -a
${Base_name} --color=auto -- man --warnings
EOD
}
#
# Utilities
#
init_enum_array()
{
declare -n Arr=$1
shift
declare -i i=0
for Elem in $@;
do
Arr+=([$Elem]=$((i++)))
done
}
command_exists()
{
\type "$1" &> /dev/null
\return $?
}
is_opt()
{
[[ $1 =~ ^-.*$ ]]
\return $?
}
is_opt_help()
{
case "$1" in '--help'|'-help'|'-h'|'-?')
\return $(true)
esac
\return $(false)
}
validate_color_option()
{
Tail=${1:${#1}-4:4}
case $Tail in 'auto'|'ever'|'ways')
return $(true)
esac
\return $(false)
}
say_more_info()
{
\echo "Type '${Base_name} --help' for usage information."
}
#
# Entry point
#
main()
{
#
# Command line validation, rough first pass
#
if is_opt_help "$1"; then
help;
exit 0
elif [[ $# -lt 2 ]];
then
\echo "Need at least two parameters."
say_more_info
exit 1
fi
#
# Variables for arguments parsing
#
declare -a Grep_flags=()
\declare Search_flag=""
\declare Command=""
\declare Color_flag='--color=auto'
\declare -i Parsing_phase
\declare -A Phase
init_enum_array Phase 'START GREP_OPT COMMAND KEYWORD DONE'
#
# Nested functions for argument parsing
#
parse_grep_option()
{
if [[ $1 = '--' ]]; then
Parsing_phase=${Phase[COMMAND]}
elif [[ $1 =~ ^--colou?r=.*$ ]];
then
if validate_color_option "$1"; then
Color_flag="$1"
else
\echo "${Base_name} error: invalid color option for grep ("$1")."
say_more_info
\exit 2
fi
else
Grep_flags+=("$1")
fi
}
parse_command_name()
{
if is_opt "$1"
then
\echo "${Base_name}: error in command name ("$1"). Abort"
say_more_info
\exit 3
fi
Command="$1"
}
#
# Do argument parsing
#
for Arg in $@;
do
if [[ $Parsing_phase -eq ${Phase[START]} ]];
then
if is_opt_help "$Arg"
then
help;
exit 0
elif [[ $Arg = '--' ]]; then
Parsing_phase=${Phase[COMMAND]}
elif is_opt "$Arg"; then
Parsing_phase=${Phase[GREP_OPT]}
parse_grep_option "$Arg"
else
parse_command_name "$Arg"
Parsing_phase=${Phase[KEYWORD]}
fi
elif [[ $Parsing_phase -eq ${Phase[GREP_OPT]} ]];
then
parse_grep_option "$Arg"
elif [[ $Parsing_phase -eq ${Phase[COMMAND]} ]];
then
parse_command_name "$Arg"
Parsing_phase=${Phase[KEYWORD]}
elif [[ $Parsing_phase -eq ${Phase[KEYWORD]} ]];
then
Search_flag="$Arg"
Parsing_phase=${Phase[DONE]}
elif [[ $Parsing_phase -eq ${Phase[DONE]} ]];
then
\echo "Too much search keywords."
say_more_info
exit 4
fi
done
#
# Check and validate parsing
#
if [[ $Parsing_phase -eq ${Phase[GREP_OPT]} ]];
# Here we try to repair the situation where the user forgot to pass '--'
# after grep options to change parsing phase.
# So we check if flag[last-2] is not starting with '-', and try to use
# last two flags as command name and search pattern.
then
GF_count=${#Grep_flags[@]}
if [[ $GF_count -gt 1 ]] && ! is_opt "${Grep_flags[$((GF_count-2))]}"
then
Search_flag="${Grep_flags[$((GF_count-1))]}"
Command="${Grep_flags[$((GF_count-2))]}"
unset Grep_flags[-2]
unset Grep_flags[-1]
else
\echo "${Base_name} error: grep flags string not terminated"\
"(missing '--')."
say_more_info
exit 5
fi
elif [[ $Parsing_phase -eq ${Phase[COMMAND]} ]]; then
\echo "${Base_name} error: missing command name."
say_more_info
exit 6
elif [[ ${#Search_flag} -eq 0 ]]; then
\echo "${Base_name} error: missing flag to search for."
say_more_info
exit 7
fi
#
# Check command validity
#
if ! command_exists "$Command"
then
\echo "${Base_name} error: couldn't find or execute \"$Command\"."
exit 8
fi
#
# Configure grep
#
declare Grep='/usr/bin/grep'
if [[ ! -x $Grep ]];
then
Grep='/bin/grep'
if [[ ! -x $Grep ]]; then
\echo "${Base_name} error: couldn't find grep. Abort."
exit 9
fi
fi
#
# Exec search in command's help
#
$Grep "${Grep_flags[@]}" "$Color_flag" -- "${Search_flag}" <(man ${Command})
}
#
# Launch
#
main $@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment