30 comments

[ 3.6 ms ] story [ 85.3 ms ] thread
Is this project still maintained?
The maintainer has not had any activity on this project in over a year, but it looks like he account is still active
If you really really want long options, it's not that hard to just write your own argument parsing function. Certainly it would save you a couple thousand lines of complexity over this. (personally I just use getopts)

  #!/usr/bin/env sh
  while [ $# -gt 0 ] ; do
    x="$1"; shift
    case "$x" in
      -v|--verbose)
         echo "verbose mode" ;;
      -o|--output)
         OUTFILE="$1" ; shift ;
         echo "Output file is '$OUTFILE'" ;;
    esac
  done

  $ ./foo here is -v an option --output foo.txt command here
  verbose mode
  Output file is 'foo.txt'
Don’t forget the end option notation `-—`.

As in:

   --)
     break
   ;;

:-D
Okay, now do grouped short options - `yourscript -vo /path/to/file`. Note that getopt(3) (what CLI tools written in e.g. C would use) allows this - `grep -qf /path/to/file` works the same as `grep --quiet --file /path/to/file`. Come to think of that, you also don't allow `--output=/path/to/file` with the "=".

Argument parsing is one of those things where doing an approximation is easy, doing it correctly is hard.

Agreed! That's why I just use getopts :) Long options are nice but I never seem to need them.
IMHO, we've reached the point where shortopts for "non-superstar" commands are considered harmful (especially when the command args are used in scripting).

Basically, of course, all your posix commands should keep shortopts and keep the "smush together shortopts", but 99.999% of simple scripts and commands that you as a person / developer write should be 100% longopts (and long-opts only!).

Bash is a near-dead/dying language in the sense that OSX is pushing zsh, and there are some fundamental flaws in bash that are difficult to consistently work around (eg: see bash-pitfalls and shellcheck).

CLI usage is trending towards more "bundle and standardize" (ie: make, npm run, Dockerfile, etc...) and for those purpose I _really_ prefer committing explicit long-opts instead of shortopts (eg: `rm --interactive --recursive --force` v. `rm -irf`), as it's moderately self-documenting and harder to make preventable mistakes.

For your whatever internal scripts that parse options, I guarantee you it's better that you explicitly call out `foo.sh --force || foo.sh --file` instead of `foo.sh -f` after you've left it and are coming back to it a year from now.

For "superstar" commands like "docker run -i -t ..." then let there be 100k's of users before you start cluttering with shortopts, and only then add them judiciously for truly time-saving interactive uses.

Apple swapped to zsh as the default shell because Bash moved to GPLv3 from GPLv2.
>Bash is a near-dead/dying language in the sense that OSX is pushing zsh, and there are some fundamental flaws in bash that are difficult to consistently work around (eg: see bash-pitfalls and shellcheck).

Recently tried my hand at some bash on osx and was frustrated to find that the osx version of getopt does not support long options, so it looks like there's no portable way to conveniently handle long options on osx, and forcing users to install GNU getopt kind of defeats the purpose of bash (portability)?

I guess I don't know the standard, is it reasonable to bundle in a dependency download with a simple bash script (e.g. download the correct version of getopt before parsing args)? I imagine not.

How does one properly support long and short options in bash on osx without rolling your own parser?

> frustrated to find that the osx version of getopt does not support long options

Probably best not to use getopt, but use getopts instead. In bash, getopts is a builtin. See the link below.

http://mywiki.wooledge.org/ComplexOptionParsing#util-linux.2...

Sorry, I completely disagree with practically everything you wrote above.

There’s nothing complicated, oblique, or arcane, about short options - if one is versed in Unix.

Usually, the sentiment you’ve expressed, comes from the mouthes of developers, more comfortable with Windows, and/or Python - regurgitating cargo cult lore, parrot fashion - sometimes followed with another regurgitated quip, about moving to a “proper” language, if said script, exceeds n lines. A similar phenomenon, can be seen with the Zawinski quote, regarding regex - usually parroted by people who have not learnt regex.

If one wants to leave supplementary information, regarding the invocation of anything within a shell script, there is already the facility to do such - comments - which take the same form, as comments within the Python language.

Your assertion that Bash is a dead language, is absurd. Particularly when juxtaposed with your assertion concerning Z Shell, immediately thereafter - which is practically identical to Bash, in syntax, aside from a few differences.

Command options, short or long, are not even shell specific. They’re a Unix convention. Why should the writer of a shell script, be at all concerned, with anyone who hasn’t grasped the fundamental basics of working with the platform, for which the script is intended to operate?!

Personally, I’m of the opinion that long options, are GNU bloat.

As for the linked Github repo - getopts - it’s builtin.

(comment deleted)
> There’s nothing complicated, oblique, or arcane, about short options - if one is versed in Unix.

Need to list a directory reversively?

  ls -R
Need to list a directory in reverse?

  ls -r
Need to sort a list in reverse?

  sort -r
Need to grep recursively?

  grep -r
Need to copy recursively?

  cp -R
  cp -r  # obsolete!
Definitely not confusing!
Thank you, I was trying not to feed the trolls. ;-)

http://www.robertames.com/blog.cgi/entries/the-unix-way-comm...

...I link to some good references that I found w.r.t. command line options which was a useful baseline even for looking at what options your command may try to support.

> Thank you, I was trying not to feed the trolls. ;-)

Who’s the troll? Your comment is probably out of line with site guidelines mate. Please report yourself to the admins, for attitude realignment.

> Definitely not confusing!

Heh, well... I never said it wasn’t confusing. I agree with you on the above.

If the Unix Philosophy was being properly followed, with your ls example, as a case in point, I would use:

    ls | tac | column
Now, that’s not even considering the ridiculousness of the naming of the “tac” command, which would ideally be renamed “rev” - with rev ideally being expanded, to to ALL reversals, not just character based, per line.

The sort of nonsense above, is why I started writing my own core utils and OS from scratch - which I suppose is a sort of suckless meme, by now.

POSIX, is both a blessing, and a curse.

> Okay, now do grouped short options

Seems doable. I may have done it before. I don't have an example handy, though.

It's possible, yes - by adding yet more easy-to-get-wrong boilerplate.

My point isn't that it's impossible. My point is that it's hard and annoying.

I guess I disagree with your point.
> doing it correctly is hard

What is "correct" though?

`grep -iEt file` complains that `-t` is not a valid option (i.e. compressed form of `grep -i -E t`)

So Grep does not support passing values to the last of a series of short options that are grouped.

But tar does:

`tar -cvffoo.tar file` happily creates foo.tar from file.

and Python does: `python3.5 -Ec'import sys; print(sys.argv[1:]);' foo` prints `['foo']`

and Gzip does:

`gzip -kvSsuff file` creates `file.suff`

I write a reasonable amount of shell. I've written a number of reasonably complex tools (mostly server admin related but also some more developer focused) in shell. I've written a library in shell, to make writing programs in shell easier. Part of that library does argument passing.

Until yesterday it supported short and long opts, with values, without values, with optional values, using space or `=` as a value separator. It also supported short opts with an 'attached' value (i.e. `-fmy-file.txt` in place of `-f my-file.txt`).

Yesterday, based on this discussion, I added the ability for it to handle `-abc` (e.g. `-a -b -c` where none of the options expects an argument) or `-abc` (e.g. `-a -b c` where the final option either requires or has an optional argument).

So, once again: what is "correct", when it comes to option handling? Your example was grep, but by the examples of other GNU tools, it only does an "approximation".

GNU grep doesn't take a value for `-E`, so it interprets this as `grep -i -E -t`, which explains the error. It doesn't have a `-t` option.

I don't know of any grep that does take a value for `-E`.

It does support taking a value for `-f`, and it also does support that as the last option in a group:

    grep -qf/path/to/file
works, even if path is something like "patterns" (see that "t" in there?).

>So, once again: what is "correct", when it comes to option handling? Your example was grep, but by the examples of other GNU tools, it only does an "approximation".

I believe you're simply mistaken about what options it takes, and which options take values, the parsing seems "correct".

Welp yep, the folly of -E vs -e (was someone saying one character options are fine?)

You're right that grep does support what I intended to try (`grep -iet`).

If you're okay with depending on Git, you can use `git rev-parse --parseopt`.

https://git-scm.com/docs/git-rev-parse#_parseopt

> In --parseopt mode, git rev-parse helps massaging options to bring to shell scripts the same facilities C builtins have. It works as an option normalizer (e.g. splits single switches aggregate values), a bit like getopt(1) does.

> It takes on the standard input the specification of the options to parse and understand, and echoes on the standard output a string suitable for sh(1) eval to replace the arguments with normalized ones. In case of error, it outputs usage on the standard error stream, and exits with code 129.

This is really brilliant and I have never heard of this method before. Noting it down thank you.
ksh's declarative getopts is easy to use and automatically provides help messages, and is one major reason I use it for day-to-day scripts. It's too bad bash didn't embrace and extend that before they extinguished it.
Or, even better: don't use shell scripts to express the kind of complex logic which requires parsing arguments in multiple forms/modes.