Last active
January 11, 2021 14:50
-
-
Save janderudder/e9ecbaa727cfdba2bb058cc0d2bbdbf6 to your computer and use it in GitHub Desktop.
Man Grep : conveniently search for a command's options descriptions
This file contains hidden or 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 | |
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