Hacker News new | ask | show | jobs
by xelxebar 2167 days ago
This reminds me of a nice sed one-liner I recently happened to craft.

Do you ever collect families of functions in your shell scripts under different sections? Here's a nice way of printing out all the functions under a given section:

    funs(){ sed -n '/^## /h;x;/'"$1"'/{x;s/^\(\w\+\)().*/\1/p;x};x' "$0";}
Where "sections" are delimited by comments of the form "## Section Name" at the beginning of a line. A particularly nice use case is when you write scripts that expect "subcommand" arguments, like

    $ foo.sh bar baz
and wish to keep track of the available subcommands in the help documentation. Simply collect all your subcommands under the heading "## Subcommands" and stick a funs call in your documentation:

    usage=$(cat <<USAGE
    Usage: foo <subcommand>
    Subcommands: $(funs Subcommands)
    USAGE
    )
The sed one-liner above uses the oft-ignored "hold space" which lets you store data that persists between lines. Here's the same sed but expanded with comments:

    funs(){ sed -n '/^## /h  # Store header line in hold space
        x               # Swap out current line with header in hold space.
        /'"$1"'/{       # Run block if last encountered header matches $1
            x           # Return to processing current line (instead of header)
            s/^\(\w\+\)().*/\1/p    # Print function names
            x           # Whether or not this block runs, we want to return to
                        # processing the current line. If the block does not
                        # run, then the hold space contains our current line
                        # with the active line being our header. So we must
        }               # return to that state as whell when the block is run.
        x               # Restore current line from hold space' "$0"
    }