Created
March 22, 2026 13:28
-
-
Save giladbarnea/ff6c1b08d062d8d680f88181ade33bc7 to your computer and use it in GitHub Desktop.
helpall | recursive subcommand help printer
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
| #!/usr/bin/env zsh | |
| set -euo pipefail | |
| if (( $# < 1 )); then | |
| print -u2 "Usage: helpall <command> [fixed-arg ...]" | |
| exit 1 | |
| fi | |
| typeset -a root_argv | |
| root_argv=("$@") | |
| root_command="${(j: :)root_argv}" | |
| help_timeout="${HELPALL_TIMEOUT:-5}" | |
| if ! command -v -- "$root_argv[1]" >/dev/null 2>&1; then | |
| print -u2 "helpall: command not found: $root_argv[1]" | |
| exit 1 | |
| fi | |
| typeset -a queue | |
| typeset -A seen | |
| queue=("$root_command") | |
| seen[$root_command]=1 | |
| extract_subcommands() { | |
| local current="$1" | |
| local help_text | |
| help_text="$(cat)" | |
| local normalized_help_text | |
| normalized_help_text="$(printf '%s\n' "$help_text" | perl -pe 's/.\x08//g')" | |
| local command_blocks | |
| command_blocks="$( | |
| printf '%s\n' "$normalized_help_text" \ | |
| | awk ' | |
| /^[A-Z][A-Z ]*COMMANDS$|^AVAILABLE COMMANDS$|^Commands:$/ {in_block=1; next} | |
| in_block && /^[^[:space:]]/ {in_block=0} | |
| in_block {print} | |
| ' | |
| )" | |
| local subcommand_blocks | |
| subcommand_blocks="$( | |
| printf '%s\n' "$normalized_help_text" \ | |
| | awk ' | |
| /^Subcommands:$/ {in_block=1; next} | |
| in_block && /^[^[:space:]]/ {in_block=0} | |
| in_block {print} | |
| ' | |
| )" | |
| { | |
| printf '%s\n' "$normalized_help_text" | grep -Po '^\h{2,}\K[[:alnum:]_][[:alnum:]_.-]*(?=\h{2,})' | |
| printf '%s\n' "$command_blocks" | grep -Po '^\h{2,}\K[[:alnum:]_][[:alnum:]_.-]*(?=:\h{2,}|\h{2,})' | |
| printf '%s\n' "$subcommand_blocks" | grep -Po '^\h*-\h+\K[[:alnum:]_][[:alnum:]_.-]*(?=:\h)' | |
| printf '%s\n' "$normalized_help_text" | grep -Po "^\h*(?:[Uu]sage:|or:)\h+\Q${current}\E\h+\K[[:alnum:]_][[:alnum:]_.-]*(?=\h|$)" | |
| printf '%s\n' "$normalized_help_text" | grep -Po "^\h*(?:[Uu]sage:|or:)\h+\Q${current}\E\h+\[\K[[:alnum:]_][[:alnum:]_.-]*(?=\h|\])" | |
| printf '%s\n' "$normalized_help_text" | grep -Po "^\h+\Q${current}\E\h+\K[[:alnum:]_][[:alnum:]_.-]*(?=\h|$)" | |
| printf '%s\n' "$normalized_help_text" | grep -Po "^\h+\Q${current}\E\h+\[\K[[:alnum:]_][[:alnum:]_.-]*(?=\h|\])" | |
| } \ | |
| | grep -Pv '^(help|or|usage)$' \ | |
| | grep -Pvi '^(flags|options|command|subcommand|arguments|args)$' \ | |
| | grep -Pv '^[A-Z][A-Z0-9_-]*$' \ | |
| | awk '!seen[$0]++' || true | |
| } | |
| run_help() { | |
| local -a cmd_argv | |
| cmd_argv=("$@") | |
| local output | |
| if output="$(perl -e 'alarm shift; exec @ARGV' "$help_timeout" "${cmd_argv[@]}" --help 2>&1)"; then | |
| : | |
| else | |
| local exit_code=$? | |
| output+=$'\n' | |
| output+="[helpall] command exited non-zero while printing help (exit=$exit_code)" | |
| fi | |
| printf '%s' "$output" | |
| } | |
| run_short_help() { | |
| local -a cmd_argv | |
| cmd_argv=("$@") | |
| local output | |
| if output="$(perl -e 'alarm shift; exec @ARGV' "$help_timeout" "${cmd_argv[@]}" -h 2>&1)"; then | |
| printf '%s' "$output" | |
| fi | |
| } | |
| integer index=1 | |
| while (( index <= ${#queue[@]} )); do | |
| current="${queue[index]}" | |
| (( index++ )) | |
| label="$current" | |
| typeset -a current_argv children | |
| current_argv=(${=current}) | |
| help_output="$(run_help "${current_argv[@]}")" | |
| printf '★ ───────────[ %s ]───────────\n\n' "$label" | |
| printf '%s\n' "$help_output" | |
| printf '\n' | |
| children=("${(@f)$(printf '%s\n' "$help_output" | extract_subcommands "$label")}") | |
| if (( ${#children[@]} == 0 )) && [[ "$help_output" == *$'\b'* ]]; then | |
| short_help_output="$(run_short_help "${current_argv[@]}")" | |
| if [[ -n "$short_help_output" ]]; then | |
| children=("${(@f)$(printf '%s\n' "$short_help_output" | extract_subcommands "$label")}") | |
| fi | |
| fi | |
| for child in "${children[@]}"; do | |
| if [[ -z "$child" ]]; then | |
| continue | |
| fi | |
| next_command="$current $child" | |
| if [[ -n "${seen[$next_command]-}" ]]; then | |
| continue | |
| fi | |
| seen[$next_command]=1 | |
| queue+=("$next_command") | |
| done | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment