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 (facultative)
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 can be a grep pattern, but will typically be just an option 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."
}
#
# Error codes
#
declare -ri Error_not_enough_parameters=1
declare -ri Error_invalid_grep_color_opt=2
declare -ri Error_command_param_is_opt=3
declare -ri Error_too_many_keywords=4
declare -ri Error_missing_grep_flags_end=5
declare -ri Error_missing_command_param=6
declare -ri Error_missing_keyword_param=7
declare -ri Error_command_not_found=8
declare -ri Error_grep_not_found=9
#
# 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 $Error_not_enough_parameters
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 $Error_invalid_grep_color_opt
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 $Error_command_param_is_opt
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 many search keywords."
say_more_info
\exit $Error_too_many_keywords
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 $Error_missing_grep_flags_end
fi
elif [[ $Parsing_phase -eq ${Phase[COMMAND]} ]]; then
\echo "${Base_name} error: missing command name."
say_more_info
\exit $Error_missing_command_param
elif [[ ${#Search_flag} -eq 0 ]]; then
\echo "${Base_name} error: missing keyword (flag to search for)."
say_more_info
\exit $Error_missing_keyword_param
fi
#
# Check command validity
#
if ! command_exists "$Command"
then
\echo "${Base_name} error: couldn't find or execute \"$Command\"."
\exit $Error_command_not_found
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 $Error_grep_not_found
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