10

I have a number of functions defined in my .bashrc, intented to be used interactively in a terminal. I generally preceded them with a comment describing its intended usage:

# Usage: foo [bar]
# Foo's a bar into a baz
foo() {
  ...
}

This is fine if browsing the source code, but it's nice to run type in the terminal to get a quick reminder of what the function does. However this (understandably) doesn't include comments:

$ type foo
foo is a function
foo ()
{
    ...
}

Which got me thinking "wouldn't it be nice if these sort of comments persisted so that type could display them?" And in the spirit of Python's docstrings I came up with this:

foo() {
  : Usage: foo [bar]
  : "Foo's a bar into a baz"
  ...
}

$ type foo
foo is a function
foo ()
{
    : Usage: foo [bar];
    : "Foo's a bar into a baz";
    ...
}

Now the usage is included right in the type output! Of course as you can see quoting becomes an issue which could be error-prone, but it's a nicer user experience when it works.

So my question is, is this a terrible idea? Are there better alternatives (like a man/info for functions) for providing users of Bash functions with additional context?

Ideally I'd still like the usage instructions to be located nearby the function definition so that people viewing the source code also get the benefit, but if there's a "proper" way to do this I'm open to alternatives.

Edit these are all fairly simple helper-style functions and I'm just looking to get a little extra context interactively. Certainly for more complex scripts that parse flags I'd add a --help option, but for these it would be somewhat burdensome to add help flags to everything. Perhaps that's just a cost I should accept, but this : hack seems to work reasonably well without making the source much harder to read our edit.

2 Answers 2

9

I don't think that there is just one good way to do this.

Many functions, scripts, and other executables provide a help message if the user provides -h or --help as an option:

$ foo() {
[[ "$1" =~ (-h|--help) ]] && { cat <<EOF
Usage: foo [bar]
Foo's a bar into a baz
EOF
return;
}
: ...other stuff...
}

For example:

$ foo -h
Usage: foo [bar]
Foo's a bar into a baz

$ foo --help
Usage: foo [bar]
Foo's a bar into a baz
5
  • Yep, I should have mentioned that. These are simple functions and I'm trying to avoid making them overly complex. For commands that merit parsing flags I'd certainly add a --help option.
    – dimo414
    Commented Jul 10, 2016 at 22:01
  • In programming, consistency is a virtue. Also, it depends on what you mean by "complex".
    – John1024
    Commented Jul 10, 2016 at 22:08
  • And, your approach is clever and good (and your question already has my +1).
    – John1024
    Commented Jul 10, 2016 at 22:20
  • 1
    Thanks; your implementation of --help is also non-invasive, which I think is my primary criteria in this case. I may end up using the : trick since it more directly fits my use-case, but I appreciate you pointing out that it's not hard to support --help and that most users will expect it.
    – dimo414
    Commented Jul 11, 2016 at 4:33
  • 1
    +1. i was going to answer "use getopts" but this works well enough if there are no other options. if the function has other options, use getopts.
    – cas
    Commented Jul 12, 2016 at 6:48
0

I've never quite convinced myself to take the plunge and use : commands as pseduo-comments. In particular, I've always been worried about the fact that the "comments" are actually arguments that get evaluated, and could potentially have side-effects (or just break things, such as an errant '). I still like the idea's simplicity, but haven't ever found the tradeoff quite justifiable.

My hesitation with providing usage text via a --help flag or similar has always been the cruft that comes along with it. As soon as you start accepting flags you quickly get into full-fledged argument parsing which typically (in Bash) requires quite a lot of boilerplate.

To partially bridge this gap I pulled together a small utility that wraps getopts in order to reduce this boilerplate. It still takes up a few lines, so I wouldn't use it in otherwise-tiny functions, but it's a lot more concise than using getopts directly. Here's an example usage with a -h flag (getopts only supports single-letter options):

foo() {
  local _usage='foo [-a] [-b] [-f val] [-v val] [args ...]'
  eval "$(parse_opts 'f:v:abh')"
  if (( h )); then
    echo "Usage: $_usage"
    return 0
  fi
  echo "f=$f v=$v a=$a b=$b -- $#: $*"
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .