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'
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.
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.
>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?
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.
...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.
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.
`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".
> 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.
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.
30 comments
[ 3.6 ms ] story [ 85.3 ms ] threadAs in:
:-DArgument parsing is one of those things where doing an approximation is easy, doing it correctly is hard.
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.
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?
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...
For anyone new to parsing args with getopts, a guide like this might help: https://www.shellscript.sh/tips/getopts/
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.
Need to list a directory reversively?
Need to list a directory in reverse? Need to sort a list in reverse? Need to grep recursively? Need to copy recursively? Definitely not confusing!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.
Who’s the troll? Your comment is probably out of line with site guidelines mate. Please report yourself to the admins, for attitude realignment.
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:
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.
Seems doable. I may have done it before. I don't have an example handy, though.
My point isn't that it's impossible. My point is that it's hard and annoying.
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".
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:
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".
You're right that grep does support what I intended to try (`grep -iet`).
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.
Where does the POSIX standard say that getopts is discouraged?
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/g...
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V...