Tidbits on software development, technology, and other geeky stuff

Introducing jBash

I love Bash. I hate Bash.

Yeah, that sums of up relationship with Bash pretty well, when it comes to using it for writing shell scripts. The great (and horrible) thing about Bash is that it is bare metal. Using Bash for shell scripts is great because it is such a small abstaction from the commands you are actually going to be running. Want to write a release script for your OSS project to build, package, and upload it to your favorite package manager and then create an associated GitHub release? Bash is a natrual tool for this since you can use it to orchestrate simply calling the various commands to do what you need, just as if you were typing the commands into the shell manually. But, if you go beyond anything basic, things get confusing fast. I can barely write anything in Bash non-trival without doing a Google search. To me, that’s a problem.

Bash Pitfalls

Just take a look at this list of Bash Pitfalls. The description says “This page shows common errors that Bash programmers make”. Common errors! Want to simply echo your variable to the shell with something like echo $foo? Not so fast, because you might run into WordSplitting and/or globbing concerns. How about looping through a list of files and running a command with each with something like:

for i in $(ls *.mp3); do    # Wrong!
  some command $i           # Wrong!
done

Nope - there are at least 6 problems with that syntax.

JavaScript

I love JavaScript and the npm ecosystem but IMHO, using JavaScript in Node.js isn’t an entirely pleasant or clean solution for writing shell scripts. You have asynchronous, error handling, and encoding concerns along with a verbose child_process interface. For example, to get the Node.js version, you might write something like this:

const { execFile } = require("child_process");
const child = execFile("node", ["--version"], (error, stdout, stderr) => {
  console.log(stdout);
});

That is pretty verbose, honestly. In Bash, it’s as simple as this:

echo $(node --version)

A one liner! It’s so clean and simple in Bash.

jBash

Wouldn’t it be nice to have the best of both worlds? The simplicity and terseness of Bash with the pleasant and familiar syntax of JavaScript? Also, wouldn’t it be nice if we could tap into npm packages easily to use them from our shell scripts? There is a package for just about everything, anyway.

Those were my thoughts and so I wrote jBash. It’s a simple JavaScript library that adds Bash like helpers for writing shell scripts in JavaScript. It abstracts some of the verbosity of JavaScript and matches the sytax of your favorite commands in Bash.

Want to echo a variable to the console? It’s easy with:

echo(foo);

How about the syntax to get the version of Node.js, like I showed above. Easy:

echo($(`node --version`));

jBash has helpers for arguments passed into the script ($1, $2), environmental variables ($HOME), and running commands. Also, there are some extended helpers for reading and writing files, setting verbosity level and error handling.

Helpers

Here is the full list of helpers.

Bash jBash notes
echo "Hello" echo("Hello") print text to console
$1, $2, ... $1; $2; ... $1, $2, etc. variables contain args passed in
args[0], args[1] arguments passed in are also in args array
$0 $0 file path of current script
$HOME $HOME environment variables
env.HOME all env variables are also mapped on env var
set -x set("-x") echos all commands
options.xtrace=true alternative to set(“-x”)
set -e set("-e") throw when a command exits with non-zero status
options.errexit=true alternative to set(“-x”)
cd "/usr/bin" cd("/usr/bin") change current working directory
exit 1 exit(1) exit with code
result=$(cmd.sh) result=$(`cmd.sh`) $(…) buffers output as return value
eval ping g.cn eval(`ping g.cn`) eval() streams output to console (no return value)
exec(`ping g.cn`) exec() is an alias for eval()
config=$(cat cnf.txt) config=$(`cat cnf.txt`) read text from file
config=readFile(`cnf.txt`) readFile reads file to text
echo $cnf > cnf.txt $(`echo ${cnf} > cnf.txt`) save text to file
writeFile(`cnf.txt`, cnf) writeFile saves text to file

Use It

The GitHub repository is located here: https://github.com/bradymholt/jBash. Enjoy!

Discuss on Twitter