If you find yourself needing this, the correct thing to do is to stop writing that shell scripts and switch to a proper language.
Shell Scripting
From Ash, Bash and Csh to Xonsh, Ysh and Zsh; all shell languages are welcome here!
Rules:
- Follow Lemmy rules!
- Posts must relate to shell scripting. (See bottom of sidebar for more information.)
- Only make helpful replies to questions. This is not the place for low effort joke answers.
- No discussion about piracy or hacking.
- If you find a solution to your problem by other means, please take your time to write down the steps you used to solve your problem in the original post. You can potentially help others having the same problem!
- These rules will change as the community grows.
Keep posts about shell scripting! Here are some guidelines to help:
- Allowed: Release Announcement of a command-line program designed for scripted use (e.g. bash, bats, awk, jq, coreutils/moreutils)
- Allowed: Tutorials on using shell languages or supplementary tools designed for scripted use
- Allowed: Code review/help requests for shell languages or supplementary tools designed for scripted use
- NOT Allowed: Announcement of a CLI or TUI program that is not designed for scripted use (Yes, your fancy Zsh plugin which pretty-prints the date and time using only builtins is very cool, but unless you actually want to discuss the code itself, please check out !commandline instead!)
- NOT Allowed: Domain-specific tutorials that do not involve shell scripting as a core component (e.g. docker-compose, ansible, nix). If you really love one of these, I'm sure there's a community out there ready to talk about them!
- NOT Allowed: Code review requests for non-shell programming languages and configuration languages (e.g. Python, Yaml)
In general, if your submission text is primarily shell code, then it is welcome here!
While I agree with you, I think it's a bit unkind to reply in this way. Sometimes bash is all you have, and it's interesting to learn about tools even if you're unlikely to use them.
I still think my favorite shell, NuShell, handles this the best:
def alphabet [
--alpha (-a)
--charlie (-c): string # comments on these lines are parsed and used for automatically generated --help flag
--delta: int
...filenames: string
] { echo \$alpha }"
So much less boilerplate
I've got one from stackoverflow i tidied up and expanded, that handles long options as well, but you really should learn Python instead. Talking from experience; even with a state machine approach and helpful little functions and debug helpers, juggling so much input is hard in shell.
Well, you're warned now and here it is:
tool_name="tool"
tool_descr="tool does tool things"
printhelp() {
printf '%s\nSyntax: %s [options]...\n%s\n' "$tool_descr" "$tool_name" "$tool_args"
}
bool() { [ -n "$OPTARG" ] && error "--${OPT} accepts no parameters" 7; }
# cli-parser
#--------------------------------------------------------------
tool_args='
-s, --stuff stuff to do
-o, --other other stuff to do
-V, --verbose Tell what i do
-v, --version Print version of this tool and exit
-h, --help This help text
'
while getopts "vVhbs:o:-:" OPT; do
# support long options: https://stackoverflow.com/a/28466267/519360
if [ "$OPT" = "-" ]; then # long option: reformulate OPT and OPTARG
OPT="${OPTARG%%=*}" # extract long option name
OPTARG="${OPTARG#"$OPT"}" # extract long option argument (may be empty)
OPTARG="${OPTARG#=}" # if long option argument, remove assigning `=`
fi
case "$OPT" in
s | stuff ) arg_stuff="$OPTARG"
;; o | other ) arg_other="$OPTARG"
;; V | verbose ) bool; verbose=true
;; v | version ) bool; printversion; exit 0
;; h | help ) printhelp; exit 0
;; ??* ) error "Illegal option --$OPT" 22 # bad long option
;; : ) exit 2 # missing parameter (error reported via getopts)
;; ? ) exit 22 # bad short option (error reported via getopts)
esac
done
[ $OPTIND -eq 1 ] && \
if { [ -e "$1" ] && [ $# -gt 0 ]; } # check for lazy file argument
then lazy_arg="$1"
else printhelp && exit 0
fi
shift $((OPTIND-1)) # remove parsed options and args from $@ list
There is also the manual way, I am not saying it's perfect but sometimes it offers better alternative than getopt.
https://gist.github.com/dgoguerra/9206418
The article mentions "GNU getopt" but as far as I know GNU doesn't have a getopt
utility; the version on most Linux OSes comes from util-linux. (Perhaps the author is confusing it with the C function with the same name.)
Note that other getopt
implementations have different features and some are simply broken. For example, BSD getopt
doesn't support long options and comes with this known bug:
Arguments containing whitespace or embedded shell metacharacters generally will not survive intact; this looks easy to fix but isn't.
For cross-platform scripts it's probably best to use the getopts
shell builtin instead, the downside being it only supports fairly basic (POSIX) syntax even on Bash.