[HN Gopher] Argbash - Bash Argument Parsing Code Generator
       ___________________________________________________________________
        
       Argbash - Bash Argument Parsing Code Generator
        
       Author : quincepie
       Score  : 84 points
       Date   : 2023-03-20 12:53 UTC (10 hours ago)
        
 (HTM) web link (argbash.dev)
 (TXT) w3m dump (argbash.dev)
        
       | caymanjim wrote:
       | What's with:                   # [ <-- needed because of Argbash
       | 
       | There's also this bit:
       | 
       |  _The square brackets in your script have to match (i.e. every
       | opening square bracket [ has to be followed at some point by a
       | closing square bracket ]).
       | 
       | There is a workaround -- if you need constructs s.a.
       | red=$'\e[0;91m', you can put the matching square bracket behind a
       | comment, i.e. red=$'\e[0;91m' # match square bracket: ]._
       | 
       | That kind of kludginess is a turn-off.
        
       | iamjackg wrote:
       | This is very similar to Bashly (https://bashly.dannyb.co/) but
       | with a lot more weird magic going on.
        
       | chrsig wrote:
       | I really wish bash could evolve...in particular around control
       | flow, variable assignment, string interpolation, and arithmetic.
       | 
       | I love working with bash, but it has some footguns that really
       | require an expert hand.
       | 
       | I know bash as-is will always be around for backwards
       | compatibility with the god-knows-how-many scripts out there. It'd
       | just be nice if there were a widely embraced path forward that
       | kept the shell scripting spirit while shedding some of the
       | unintuitive behaviors
        
         | oweiler wrote:
         | I think there is a place for a transpile to Bash language,
         | which just adds some tiny bits to fix Bash's idiosyncrasis
        
         | chubot wrote:
         | Evolving bash, removing footguns, and providing a path forward
         | is exactly what https://www.oilshell.org is about :)
         | 
         | It's run thousands of lines of unmodified shell AND bash
         | scripts for years, and provides an upgrade path to a new
         | language.
         | 
         | The new language is described here, and you can try it:
         | https://www.oilshell.org/release/latest/doc/oil-language-tou...
         | 
         | I've gotten some great feedback on it, but it's not stable yet.
         | You can still influence the direction of the language (join us
         | on Zulip)
         | 
         | Latest release gives the status of the project:
         | https://www.oilshell.org/blog/2023/03/release-0.14.2.html
         | 
         | As an anecdote, one thing that was extremely difficult was
         | fixing all the footguns around set -e / errexit in bash.
         | 
         | In shell and bash, "you're damned if you do set -e and damned
         | if yuo don't"
         | 
         | However I believe we have done it:
         | https://www.oilshell.org/release/0.14.2/doc/error-handling.h...
         | 
         | We simply provide options to make sure that every exit code is
         | checked. That's really all. But shells do NOT do this. In fact
         | the standard specifies that shells shouldn't, which has left
         | people mystified for decades.
         | 
         | I've gotten good feedback about error handling and the rest of
         | the changes. Again, you can try it right now if you want to
         | verify that the footguns are indeed fixed.
        
           | urxvtcd wrote:
           | > As an anecdote, one thing that was extremely difficult was
           | fixing all the footguns around set -e / errexit in bash.
           | 
           | Shameless plug: I've recently written a blog post about how
           | set -e suddenly ceases to work when using function calls in
           | flow control (what you refer to as "disabled errexit quirk"):
           | https://snails.dev/posts/set-e.html
           | 
           | Your comment made me interested in Oil, thanks!
        
         | johnchristopher wrote:
         | I gave up on bash (more precisely on bashisms. edit: and
         | because of bashims) and moved to dash or whatever is at
         | /bin/sh. If it can't be done with ash/dash then I look into
         | Python or node or PHP or what fits the situation best.
        
           | skowalak wrote:
           | I did the same. I believe the biggest selling point of shell
           | over other scripting languages is its availability on so many
           | platforms. Unfortunately bashisms have ruined the effort of
           | writing a one-size-fits-all script. So I, too, moved to POSIX
           | shell syntax for the small problems and another shell
           | scripting language for the bigger problems.
        
             | dwheeler wrote:
             | Bash is very widely available. If you use bashisms like
             | arrays, just use bash as the first line.
        
       | nickjj wrote:
       | If anyone is looking for a snippet to handle both positional args
       | along with required and optional flags (both with short and long
       | form formats) along with basic validation I put together:
       | https://nickjanetakis.com/blog/parse-command-line-positional...,
       | it includes the source code annotated with comments and a demo
       | video.
        
       | feisuzhu wrote:
       | I've found that every time my bash scripts become sufficiently
       | complex, I end up rewriting them in Python.
        
         | [deleted]
        
         | captnswing wrote:
         | this ^
        
         | vaughan wrote:
         | TypeScript is now tenable for bash scripts with the fast-
         | starting Bun runtime https://bun.sh/.
         | 
         | Before Bun, Node+V8 was just too slow to start.
         | 
         | IMHO all scripts should be written in TypeScript...you get
         | typechecking and all the rest of the editing experience. Plus
         | things like Wallaby.js for live coding/testing.
         | 
         | My `.bashrc` now just runs a TypeScript script with Bun. Allows
         | you to use proper structure instead of brittle spaghetti stuff.
         | The number of times I'm debugging silly PATH stuff was too
         | much...
        
           | c0wb0yc0d3r wrote:
           | Do you have an example?
        
         | JohnFen wrote:
         | When I'm working on/writing complex bash scripts, it's because
         | the scripts have to run on a variety of different customer
         | machines where Python is not consistently available (let alone
         | being able to count on a particular version) and, if it's not
         | there, cannot be installed.
         | 
         | The main advantage of bash is that it exists on just about
         | every unix machine.
        
         | vultour wrote:
         | Honestly, I don't think I found a single case where this was
         | true. Every time I try to rewrite a moderately complex bash
         | script in Python it becomes hundreds or thousands of lines of
         | code dealing with calling external binaries, streams and proper
         | error handling. Perhaps if you're only dealing with text
         | processing it will work, but the moment you start piping
         | together external programs via Python it's all pointless.
        
           | epr wrote:
           | Glad I'm not the only one. Honestly, whenever bash comes up
           | in any context, 10 different people feel compelled to express
           | this whole replace all bash scripts with python sentiment and
           | I just have no clue what the fuck they're talking about.
           | 
           | I would consider myself an expert or at least near-expert in
           | python, but I don't see opportunities to replace my shell
           | scripts with python popping up left and right. Do you open
           | files manually and set up pipe chains with subprocess.Popen?
           | I've done this, and its generally many more LOC compared to
           | the shell original, and harder to read.
           | 
           | On the other hand, I'd consider myself maybe 7/10 skill level
           | with bash, but most developers are only ever a 2/10 or 3/10
           | with bash/shell. I can't help but think that the average
           | developer's lack of shell understanding is where all these
           | suggestions to convert to python come from. If it's that easy
           | or beneficial to convert to python, then it probably should
           | have been written in python originally.
        
           | iamjackg wrote:
           | 100% agree. There are some libraries like
           | https://amoffat.github.io/sh/ that aim to make that easier,
           | but they always have some quirks that, funnily enough, are
           | often the corner cases you were hitting in your complicated
           | Bash script in the first place.
        
         | ff317 wrote:
         | Yeah, I figure if a bash script goes over ~10-20 lines, or
         | involves really long lines, or quotes within quotes within
         | quotes of different kinds, it's time to move on to Python or
         | similar.
        
         | jen20 wrote:
         | Fair - I find that every time a Python program becomes
         | sufficiently complex though (defined as "needs something not in
         | the stdlib of Python 3.6"), I end up rewriting them in Go or
         | Rust ;-)
        
       | Joker_vD wrote:
       | Huh, it uses M4 to build its DSL, how quaint.
       | 
       | I wish there were more projects on the other side of the
       | spectrum: take the script's self-reported usage string, a la
       | docopt [0], and derive argument-parsing code from _that_. After
       | all, we have GPT-4 now.
       | 
       | [0] https://github.com/docopt/docopts
        
       | softskunk wrote:
       | as cool as this is, i feel that anything with the complexity of
       | more than one or two arguments should really be written in a
       | different language, like Python (or AppleScript for macOS users).
       | bash just isn't the right tool for the job then.
        
         | meindnoch wrote:
         | >or AppleScript for macOS users
         | 
         | You must be joking.
        
           | user3939382 wrote:
           | AppleScript: It's easy because you write it like natural
           | English! Sort of. We'll it's not really natural English. It
           | also doesn't have semantics that make sense for a programming
           | language. Good luck!
        
             | xp84 wrote:
             | Hey but don't worry, it has great documentation, in the
             | form of question posts from 2006 on "MacOSXHints" that may
             | or may not have been answered!
             | 
             | Also don't worry, you can write AppleScript in JavaScript
             | syntax too! Too bad 100% of the code samples out there to
             | help you are in the "other" syntax :D
        
               | user3939382 wrote:
               | I never had another interpreter that I had to learn by
               | manually fuzzing it. AppleScript makes bash look like F#.
        
       | nadaviv wrote:
       | GPT is pretty good at figuring out argument parsing code based on
       | example CLI usage. Like this:                   > Write bash code
       | to parse the following arguments:         >         > $
       | ./export.sh --out-file dump.json --db foo.sqlite --include-
       | headers --exclude-rev -vvv -p9000              Here's an example
       | bash code to parse the arguments:              #!/bin/bash
       | # Default values for optional arguments
       | INCLUDE_HEADERS=false         EXCLUDE_REV=false
       | VERBOSE_LEVEL=0         PORT=0              # Parse the arguments
       | while [[ "$#" -gt 0 ]]; do             case $1 in
       | --out-file) OUT_FILE="$2"; shift ;;             --db) DB="$2";
       | shift ;;             --include-headers) INCLUDE_HEADERS=true ;;
       | --exclude-rev) EXCLUDE_REV=true ;;             -v)
       | VERBOSE_LEVEL=$((VERBOSE_LEVEL+1)) ;;             -p) PORT="$2";
       | shift ;;             -h|--help)                  echo "Usage:
       | ./export.sh [OPTIONS]"                 echo ""
       | echo "Options:"                 echo "--out-file     Output file
       | path (required)"                 echo "--db           Database
       | path (required)"                 echo "--include-headers  Include
       | column headers in output (default: false)"                 echo "
       | --exclude-rev   Exclude revisions in output (default: false)"
       | echo "-v              Increase verbosity level (default: 0)"
       | echo "-p              Port number (default: 0)"
       | echo "-h, --help      Show help"                 exit 0
       | ;;             *) echo "Unknown parameter passed: $1"; exit 1 ;;
       | esac             shift         done              # Check required
       | arguments         if [[ -z $OUT_FILE ]]; then             echo
       | "Error: missing argument --out-file"             exit 1
       | fi              if [[ -z $DB ]]; then             echo "Error:
       | missing argument --db"             exit 1         fi
       | # Print parsed arguments         echo "OUT_FILE=$OUT_FILE"
       | echo "DB=$DB"         echo "INCLUDE_HEADERS=$INCLUDE_HEADERS"
       | echo "EXCLUDE_REV=$EXCLUDE_REV"         echo
       | "VERBOSE_LEVEL=$VERBOSE_LEVEL"         echo "PORT=$PORT"
        
         | version_five wrote:
         | Honestly nobody cares. It's insulting to every reader to post
         | this kind of crap.
        
           | nadaviv wrote:
           | Why is it insulting to point out that GPT can produce good
           | results for this particular use-case?
           | 
           | Being able to define your argument types and generate parsing
           | code for them using an example CLI invocation feels very
           | natural and expressive to me. I personally found it to be
           | useful for my work.
        
           | akira2501 wrote:
           | I wish it was crap, then I could pretend I'm participating in
           | some form of modern performance art. This is, unfortunately,
           | just propaganda.
        
             | nadaviv wrote:
             | Wait what? Propaganda? I really don't get why this is
             | invoking such strong reactions...
        
       | shaftway wrote:
       | I've always assumed that there was some argument parser available
       | that just sets things as environment variables, and that my
       | google-fu is just too weak to find it.
       | 
       | Why ccouldn't I just go `source argbash _ARG_ --single option o
       | --bool print --position positional -- $@` and get _ARG_OPTION,
       | _ARG_PRINT, and _ARG_POSITIONAL environment variables set based
       | on the commands passed in, without having to dump a hundred lines
       | of code in my script?
        
       | akho wrote:
       | Yet another point where Fish is a delight.
       | 
       | Going to Python/whatever is not quite the same -- shell scripts
       | are not as much written as extracted from shell history, so
       | switching to a separate language is a large extra step.
        
       | databasher wrote:
       | Handling command-line arguments in Bash is easy. Bash's `getopts`
       | handles short and long arguments gnu-style without any problem,
       | out of the box, without any need for libraries or complicated
       | packages.
       | 
       | This pattern handles lots of styles of options: short and long
       | options (-h, --help), `--` for separating options from positional
       | args, with GNU-style long options (--output-file=$filename).
       | while getopts :o:h-: option       do case $option in
       | h ) print_help;;              o ) output_file=$OPTARG;;
       | - ) case $OPTARG in                      help ) print_help;;
       | output-file=* ) output_file=${OPTARG##*=};;
       | * ) echo "bad option $OPTARG" >&2; exit 1;;
       | esac;;              '?' ) echo "unknown option: $OPTARG" >&2;
       | exit 1;;              : ) echo "option missing argument: $OPTARG"
       | >&2; exit 1;;              * ) echo "bad state in getopts" >&2;
       | exit 1;;          esac       done       shift $((OPTIND-1))
       | (( $# > 0 )) && printf 'remaining arg: %s\n' "$@"
        
         | sacnoradhq wrote:
         | I don't know where you learned shell scripting, but your
         | formatting is very confusing and nonstandard.
         | 
         | - while...; do / if ...; then
         | 
         | - space before ;;
         | 
         | - no space before case match
         | 
         | - use util-linux getopt because getopts doesn't handle long
         | args
         | 
         | - DRY with a die() function rather than exits littered all over
        
           | databasher wrote:
           | If you refer to my example above, you'll find that Bash's
           | native "getopts" handles long options just fine. It accepts
           | short option `-`, with an argument. This handles --long-
           | options and --long-options=with-arguments.
           | 
           | Feel free to use your own formatting preferences.
        
             | sacnoradhq wrote:
             | https://google.github.io/styleguide/shellguide.html
        
           | fsckboy wrote:
           | > _I don 't know where you learned shell scripting, but your
           | formatting is very confusing_
           | 
           | i don't know where you learned English composition, but I
           | can't follow your critique. are you pointing out your
           | preferences (which may well match gnu or bsd standards, i
           | don't know tell me) or things that actually make a
           | difference? Did he break emacs auto-indent? Are you pointing
           | out all the flaws or the correct ways, i'm having to eyeball
           | diff. Use more of your words.                   while
           | do
           | 
           | doesn't seem substantially worse/confusing compared to
           | while;do unless you have some reason?
           | 
           | spacing out ;; does it make a difference? or do you like
           | things spaced out?
           | 
           | etc.
        
             | sacnoradhq wrote:
             | Flagged for not being a professional response to
             | constructive feedback.
        
               | yesenadam wrote:
               | I thought it was "professional", helpful, and a great
               | comment. I didn't think yours was constructive, just
               | unhelpfully treating a different formatting style as
               | objectively worse, in an unfriendly way. "Be kind"! It
               | was also hard to parse, as the GP pointed out.
               | 
               | edit: You changed your comment after I wrote this. Now it
               | mentions that you flagged the GP. That's ridiculous.
        
               | sacnoradhq wrote:
               | You have way too much time on your hands and seem
               | determined to argue nonconstructively. Have a good one.
        
               | yesenadam wrote:
               | > You have way too much time on your hands and seem
               | determined to argue nonconstructively. Have a good one.
               | 
               | Please refresh on the guidelines. And reconsider who has
               | been "constructive" vs "nonconstructive" in this thread.
        
       | rickydroll wrote:
       | I have used are - for many projects and it is wonderful. But as
       | others have indicated, be mindful of whether or not Bash is the
       | right tool for the task at hand.
        
         | synergy20 wrote:
         | what is 'are', interesting name
        
           | philote wrote:
           | Heh, I'm guessing it was voice to text for "argbash" but got
           | translated to "are dash".
        
       | simonw wrote:
       | Languages that I work with infrequently enough to remember how to
       | use them - like Bash - are the absolute perfect place to apply
       | LLM tech like ChatGPT.
       | 
       | Prompt:
       | 
       | > Write a bash script "foo.sh" that accepts a required filename,
       | optional flags for "-r/--reverse" and "-s/--skip" and an optional
       | "-o/--output=other-file" parameter. It should have "-h/--help"
       | text too explaining this.
       | 
       | Then copy and paste out the result and write the rest of the
       | script (or use further prompts to get ChatGPT to write it for
       | you).
       | 
       | Could it be done better if I spent more time on it or was a Bash
       | expert? Absolutely, but for most of the times when I need to do
       | something like this I really don't care too much about the
       | finished quality.
        
       | ndsipa_pomu wrote:
       | I'm a fan of BashBoilerPlate (Bash3BoilerPlate) -
       | https://github.com/xwmx/bash-boilerplate
       | 
       | It uses a similar style of deriving the arguments from the usage
       | declaration, but it also includes some useful logging functions
       | and is all in one script. There's some more info available on
       | their style choices here: https://bash3boilerplate.sh/
        
       | tveyben wrote:
       | I have great pleasure when using docopt in Python.
       | 
       | I see docopts is 'the same' implementation but for shell, have
       | never tried it though.
       | 
       | ==== docopt helps you:
       | 
       | - define the interface for your command-line app, and -
       | automatically generate a parser for it. ====
       | 
       | http://docopt.org/
       | 
       | https://github.com/docopt/docopts
        
       | sacnoradhq wrote:
       | Cute and lots of effort went into this, but code generation is,
       | unfortunately, unmaintainable and inflexible. This seems targeted
       | at users who want to avoid mastery of their tools, which is fine
       | for some.
       | 
       | util-linux getopt exists.
        
       | [deleted]
        
       ___________________________________________________________________
       (page generated 2023-03-20 23:01 UTC)