9 releases
0.1.8 | Sep 22, 2021 |
---|---|
0.1.7 | Sep 22, 2021 |
#2068 in Command line utilities
32 downloads per month
61KB
261 lines
runsh
A CLI frontend for your bash scripts.
Parses scripts and pretty prints the functions it finds. Similar to run_lib but rustier.
Say you have a script called script.sh
that looks like this:
#!/usr/bin/env bash
# This function is very clever and awesome and does a lot of neat stuff.
# And here is some more detailed description about this funciton. Isn't it great?
some_function() {
echo "hello world from a script"
echo "foobar"
sleep 1
echo "ending function now"
}
# More functions
yet_more_functions() {
echo "hello from another function"
}
You can append the follwing the the file:
runsh $(basename "$0") "$@" || "$@"
Then when you execute ./script.sh
you'll see this:
Then you can run something like this to execute the function:
./script.sh some_function
Installation
From the crate:
cargo install runsh
Update
cargo uninstall runsh
cargo install runsh
Use
Add the following to the end of your script:
runsh $(basename "$0") "$@" || "$@"
The just execute your script and follow the usage instructions.
On some projects I find myself having many scripts. E.g. ops
, test
, aws
, twitter
.
Hidden functions
Prefix '_' to a function to have runsh
ignore it and not pretty print it. This is useful for helper functions. "Private" functions I guess. E.g.:
_hidden_function() {
echo "blah blah"
}
File headers
runsh
will extract comments in the file header, if it finds any, and display them alongside all your runnable functions. It relies on these comments following the form in the Google Shell Style Guide, i.e. like this:
#!/usr/bin/env bash
#
# Some comments.
# And some more.
Challenges
Executing a bash function from rust:
- Command executes the program directly and does not create a shell, so you can't source a script and then invoke it.
- One could use shellfn to source and execute but one loses the ability to see what the script is doing as it runs. This has to support long-running scripts so that isn't any good.
It's easier to have the script invoke itself and that's what the last line does. This means runsh doesn't actually run anything, it's just a pretty-printer for bash scripts. I'd rather have it run the functions becuase I want to keep the bash simple, so if anyone reading this has any better ideas please do get in touch.
Why not run_lib?
I already wrote this in bash and called it run_lib. There are a few reasons why this might be better. Here are some considerations:
- A Rust executable is easier to distribute via
cargo
. It's easier for people to update their version. - Integration with a script is more or less the same.
- The processing is much easier in Rust than it is in bash, i.e. finding and displaying multi-line comments.
- Rust so hot right now.
How the integration works
Integration looks like this:
runsh $(basename "$0") "$@" || "$@"
There are two parts to this:
runsh $(basename "$0") "$0"
: this executesrunsh
, passing two parameters:- The name of the script being run (
$(basename "$0")
). E.g. in./script some_function
it will bescript
. - The parameters to the shell command as issued by the user (
"$@"
). This will be the function name and args, e.g. in./script.sh some_function some_args
it will besome_function some_args
. This is for validation.
- The name of the script being run (
|| "$@"
is the fall back for whenrunsh
returns a non-zero exit code. This is suppsoed to happen. It is how the function ends up getting run.runsh
will validate the function name and return a non-zero exit code if it exists. When this happens"$@"
will execute, which is a quick bash way to run the actual function.
Dependencies
~5–17MB
~164K SLoC