5 ms·
I start all my bash scripts from the following template, then modify it as needed. It is not fancy, but I think it supports the lowest common denominator that i
by bxparks 6y ago
I start all my bash scripts from the following template, then modify it as needed. It is not fancy, but I think it supports the lowest common denominator that is compatible with other flag processing systems in all other languages that I am aware of. (In particular, it does not support '--option=opt' but uses '--option opt', which is cross-compatible). I like that it is small, easy to maintain, and avoids external dependencies in my shell script:
Edit: Send error messages to stderr
Edit2: People seem to like it. I created a GitHub gist for it here: https://gist.github.com/bxparks/e67a3d6fc6b5d62b51304b3d9de25405 https://gist.github.com/bxparks/e67a3d6fc6b5d62b51304b3d9de2...
#!/bin/bash
set -eu
function usage() {
echo "Usage: parseflags.sh [--help|-h] [--binary] [--option opt] [--] files..." 1>&2
exit 1
}
binary=0
option=''
while [[ $# -gt 0 ]]; do
case $1 in
--binary) binary=1 ;;
--option) shift; option="$1" ;;
--help|-h) usage ;;
--) shift; break ;;
-*) echo "Unknown flag '$1'" 1>&2; usage ;;
*) break ;;
esac
shift
done
echo "binary=$binary"
echo "option=$option"
echo "files: $@"
- skinner927 6y agoIn my experience this is the simplest way. The only mod I’d suggest is to put positional arguments into an array so you can put flags anywhere but this will work fine for 99% of scripts.
- bxparks 6y agoThe problem I have with bash arrays is that I use them so infrequently that I cannot remember its syntax. I have to look up the manual each time, and it's difficult to find things in the bash man pages.
- matheusmoreira 6y agoThis is nice! I suggest writing messages to standard error instead of standard output.
- bxparks 6y agoYou mean like this? echo "message" 1>&2 Yes, that's probably a good idea, though I cannot recall this causing a problem for any script that I've written so far.
- deleted 6y ago[deleted]
- xandris 6y agoWait, --help should print to stdout so that fancycommand --help | less works as expected. Otherwise, the help text will dump to the screen and less will erase it when it decides to redraw its empty buffer! Actual error messages should go to stderr so that I can redirect them to an error log or so I can suppress them if need be, or I could also be doing > /dev/stdout to silence normal output but I don't want errors to be discarded.
- matheusmoreira 6y agoI agree. When a program is given the --help option, the usage information is the actual output.
- matheusmoreira 6y agoIt allows users to easily separate messages intended for the user from output which can be consumed by other scripts. $ tens.sh --verbose 5 10 100 1000 10000 100000 Calculated 5 results $ tens.sh --verbose 5 > results Calculated 5 results
- macNchz 6y agoThis is handy, thanks! Definitely one of those things I wind up badly/slowly reinventing the boilerplate for every time.
- moonchild 6y agoI recommend making 'usage' return 0, rather than 1; since asking for help is valid. (Since you use it both for -h and for unknown options, perhaps make it not exit at all, and make the caller exit with an appropriate code?) Additionally, you should output the actual executable name (as passed in on $0) rather than hardcoding something. So maybe at the top say 'executable=$0; shift'; replace parseflags.sh with $executable; and replace $1 with $0 in your option parse loop.
- bxparks 6y agoI think technically you are correct about --help being valid. But in practice, I use --help only in an interactive context where the exit code does not matter so I've never been bitten by the subtle difference. I tried using $0 years ago, but I didn't like seeing the full path name (or the "./parseflags.sh" string), or maybe it was because I often use shell alias, so the $0 becomes the alias name, or something like that. So now, I just replace the string "parseflags.sh" with the name of the script for each script. I think I tried using $(basename $0) at one point, but didn't like that either, though I don't remember why. I hope that the template is simple enough that people can customize it as they wish. Edit: Fix typo
- stormbrew 6y agojust fyi, `shift` doesn't affect $0, it slides the window of $1..$N back, so you don't want to shift after grabbing $0 and you don't want to change the option loop if you do this. And you can just use $0 everywhere so there's technically no reason to pull it out into a $executable variable, but tbh that gets a bit confusing in functions.