Hah! I have to admit that I spent quite some time thinking about whether I wanted to use this or switch to a "real" language.
For all its shortcomings, I find that shell scripting is still the easiest way to put together a tool that needs to interact with a lot of other commands, no matter how large it ends up being. I love Python to death, but using `subprocess` for everything is the opposite of my idea of a good time.
The syntax for basically every "real" programming language is amazingly unurgonomic at interoperating with the outside world and moving/transforming data which is why shells are still super popular.
You would think there would be more effort supporting this since it's so tedious to have a billion libraries that are just wrappers around http apis and external utility calls.
"Real" programming languages are leagues ahead of shell scripting languages in terms of transforming and moving data around.
Most shell utilities are focused on text processing or are thin wrappers around system calls. If you're using something like python's `subprocess` module to get at these, you're doing it wrong.
Interactivity is where shells have an advantage. Their syntax lends itself well to that. It's also why the syntax sucks for regular programming.
Before there are 10 people with "there is a library for that in Python" (because they are not and can not be ergonomic enough):
Next Generation Shell is a "real" programming language. It also has syntax for calling external programs. It handles exit codes properly. It has facility for constructing command line arguments.
edit: ah, yes, NGS also supports command line arguments automatic parsing when main() is called; augmenting this facility would be the sane way to solve the problem.
In my case, I used it to build an all-in-one tool for a local dev environment. It wraps all kinds of operations for starting/stopping things, extracting and modifying data, etc. while providing some useful tips when things go wrong.
At its core it's just interacting with existing software like docker-compose, git, and some internal tools, which I find is still much, much easier to do with shell scripting than "proper" languages.
I have, and I quite like it! That doesn't seem to be the way most people feel about it though.
I need to build things that pass the bus audit: other people also have to understand them and work on them and, for better or for worse, Bash is a very popular lowest common denominator for many devs.
Ansible is awesome, but would largely be overkill for something like this. I'm not doing much more than ensuring a few things are in place, merging a couple files, and then running docker-compose up/down, or exec-ing into running containers to run commands. I certainly wouldn't want people to have to run an entire playbook just to do all this: it's slow, requires even more setup, and is not really well suited for quick CLI-like use.
make is powerful, but it has some weaknesses. The syntax and macros are complicated to parse and troubleshoot. There are some common gotchas like .PHONY and indentation.
There are of course other alternatives such as `doit`
Very neat. I have written custom Ruby scripts for this kind of thing in the past. There's also an `sh` library for Python that makes it easier to call external commands inline, although it has some issues in non-trivial cases. Thanks for sharing!
When you want to create a bash script - and you choose bash for portability or for whatever reason - and still want to have a decent-looking command line interface, without investing effort in creating argument parsers and usage help.
I really liked it because it drew my attention when I might have skipped over a block of text.
However, I wanted to go back and see what was earlier or view the source input and output. So I would also like it if the contents of the gif was printed out below for easier access.
It is both too fast and too slow. It's too fast to read it, and it's too slow for a drive-by look. Just put the document on the page like a normal person.
No need to repeat step 3. Code for each command (in case you want a bash script with subcommands), is placed in `src/<command_name>_command.sh`. Running `bashly generate` will always read it and re-merge it.
1. Am I the only one who gets annoyed by the abundance of software being described as "beautiful" (or some other vague, subjective term)? And, honestly, Bash is probably the least "beautiful" "programming language" I can think of.
2. If you need this level of complexity, I feel like you should be reaching for Python or Go or something else that definitely isn't Bash
For 1, I do agree that the term is overused, but in this case it's not really about the beauty of the code itself: it's about the fact that the final generated script acts in a predictable way and prints information and help messages in a format that people are already familiar with. You don't have to write your own usage message: it does it for you, and it looks the way I would expect a usage message to look.
I also see argument 2 a lot, and I've already addressed this in other comments, but sometimes you really just need to call a couple of commands while prepping some env vars or config files for convenience.
In those cases, reaching for Python or Go ends up being a lot of boilerplate for very little advantage, especially when you want to pass control over to other applications once you're done setting things up.
Re 1, the problem is that "beautiful" doesn’t tell you anything useful, other than (being uncharitable) the author having succumbed to tech marketing hype speech.
And then I have to learn Python or Go. Bash is ubiquitous and for the things I'm doing scripting for, something like this would be a pretty good use-case. Usually my scripts start out simple, but then become unwieldy monsters over time. I could use something like Bashly to refactor my monster Bash script to something more manageable.
No you aren't. This irritates me also. Bash is about as beautiful as perl.
> 2. If you need this level of complexity, I feel like you should be reaching for Python or Go or something else that definitely isn't Bash
This is true. Otherwise you risk [in bash] creating something very difficult to maintain for future contributors. The lengths people will go to avoid "programming" can be quite staggering.
One could make a Venn diagram of positive adjectives used frequently in Hacker News headlines and positive adjectives used frequently by a certain US ex-president.
"Beautiful" would be in the intersection. "Modern" is popular on HN, but not so much with the ex-president.
Since the landing page is not the clearest, you can think of this as an argparse equivalent for Bash.
You provide a YAML file listing commands, subcommands, arguments, and flags, and it automatically generates a Bash script that can parse and validate them, provide help messages, and run your code for each command.
It also lets you keep the actual code for each command and subcommand in separate files, which are merged together into one distributable Bash script at generation time.
It's basically a templating system to auto-generate argument parsing so you don't have to solve that again or deal with things like optparse.
Have you considered submitting your 3 sentences to the project?? Because in the mere moments that it took me to read your 3 sentences i got the gist of what this does instantly! Thanks for providing this, and please do consider submitting this so that others can learn from you and benefit the creator of their app.!
The mistake here is assuming that every shell script needs complex argument parsing, subcommand support, and a verbose usage message. I would even go so far as to say they're _rarely_ needed.
hof uses CUE instead of Yaml, can output to any and multiple languages, and uses diff3 so you can work in both the output and the design at the same time.
I'm a tiny bit disappointed to see that the tool itself is written in Ruby - writing it in Bash (with a command-line interface that was generated by itself) would be deeply nerdily pleasing to me.
Totally understand why you would write this in Ruby instead though!
IMHO, I don't think most people commenting here are getting it.
As an example, here's a bash alias generator, alf [0], which itself is a bash script, created using this. The bash script for alf is note really small [1]. Additionally, the output is extremely readable unlike most other bash scripts I myself have written!
It has its own DSL for describing tasks, along with help strings and argument descriptors, and can manages generating help screens, parsing args and invoking tasks (sub-commands).
Each task can be written in its own language, one task could be Bash, one could be zsh, one could be python, etc.
If you're in the market for a flexible task runner, I hope you'll give it a chance.
53 comments
[ 3.8 ms ] story [ 93.8 ms ] threadFor all its shortcomings, I find that shell scripting is still the easiest way to put together a tool that needs to interact with a lot of other commands, no matter how large it ends up being. I love Python to death, but using `subprocess` for everything is the opposite of my idea of a good time.
You would think there would be more effort supporting this since it's so tedious to have a billion libraries that are just wrappers around http apis and external utility calls.
Most shell utilities are focused on text processing or are thin wrappers around system calls. If you're using something like python's `subprocess` module to get at these, you're doing it wrong.
Interactivity is where shells have an advantage. Their syntax lends itself well to that. It's also why the syntax sucks for regular programming.
Next Generation Shell is a "real" programming language. It also has syntax for calling external programs. It handles exit codes properly. It has facility for constructing command line arguments.
Project - https://github.com/ngs-lang/ngs
Motivation related to the above - https://ilya-sher.org/2017/10/10/why-i-have-no-favorite-prog...
edit: ah, yes, NGS also supports command line arguments automatic parsing when main() is called; augmenting this facility would be the sane way to solve the problem.
At its core it's just interacting with existing software like docker-compose, git, and some internal tools, which I find is still much, much easier to do with shell scripting than "proper" languages.
I need to build things that pass the bus audit: other people also have to understand them and work on them and, for better or for worse, Bash is a very popular lowest common denominator for many devs.
There are of course other alternatives such as `doit`
When you want to create a bash script - and you choose bash for portability or for whatever reason - and still want to have a decent-looking command line interface, without investing effort in creating argument parsers and usage help.
The examples page shows real-world examples, and per-feature examples: https://bashly.dannyb.co/examples/
However, I wanted to go back and see what was earlier or view the source input and output. So I would also like it if the contents of the gif was printed out below for easier access.
It's probably clever, sure, but it seems like this would wind up more declarative than imperative?
1. Define your commands + flags + args in YAML (`bashly init` creates this YAML).
2. Bashly generates the standalone script and empty "placeholder" scripts for your actual bash code (`bashly generate`).
3. You plug in your bash code.
4. Bashly merges it all back to the same single final script (`bashly generate`).
5. Rinse and repeat.
What happens when I get to 5 and want to change an arg for cdm/foo? Does step 3 need to be repeated in its entirety?
I think this is a good minimal example:
https://github.com/DannyBen/bashly/tree/master/examples/mini...
2. If you need this level of complexity, I feel like you should be reaching for Python or Go or something else that definitely isn't Bash
I also see argument 2 a lot, and I've already addressed this in other comments, but sometimes you really just need to call a couple of commands while prepping some env vars or config files for convenience.
In those cases, reaching for Python or Go ends up being a lot of boilerplate for very little advantage, especially when you want to pass control over to other applications once you're done setting things up.
No you aren't. This irritates me also. Bash is about as beautiful as perl.
> 2. If you need this level of complexity, I feel like you should be reaching for Python or Go or something else that definitely isn't Bash
This is true. Otherwise you risk [in bash] creating something very difficult to maintain for future contributors. The lengths people will go to avoid "programming" can be quite staggering.
https://ilya-sher.org/2021/03/19/running-elegant-bash-on-sim...
"Beautiful" would be in the intersection. "Modern" is popular on HN, but not so much with the ex-president.
You provide a YAML file listing commands, subcommands, arguments, and flags, and it automatically generates a Bash script that can parse and validate them, provide help messages, and run your code for each command.
It also lets you keep the actual code for each command and subcommand in separate files, which are merged together into one distributable Bash script at generation time.
It's basically a templating system to auto-generate argument parsing so you don't have to solve that again or deal with things like optparse.
> ${1:?help message}
Using python more these days however
https://github.com/hofstadter-io/hofmod-cli is the same idea for generating Go CLI boilerplate.
hof uses CUE instead of Yaml, can output to any and multiple languages, and uses diff3 so you can work in both the output and the design at the same time.
Anyone know what tool is used to create that?
It was created with asciinema and svg-term-cli
Totally understand why you would write this in Ruby instead though!
Note there is a docker image if you want to use it without having Ruby.
As an example, here's a bash alias generator, alf [0], which itself is a bash script, created using this. The bash script for alf is note really small [1]. Additionally, the output is extremely readable unlike most other bash scripts I myself have written!
Really amazing piece of software! Kudos.
[0]: https://github.com/DannyBen/alf [1]: https://raw.githubusercontent.com/DannyBen/alf/master/alf
Run : https://github.com/TekWizely/run
It has its own DSL for describing tasks, along with help strings and argument descriptors, and can manages generating help screens, parsing args and invoking tasks (sub-commands).
Each task can be written in its own language, one task could be Bash, one could be zsh, one could be python, etc.
If you're in the market for a flexible task runner, I hope you'll give it a chance.