Skip to content

Instantly share code, notes, and snippets.

@jeswin jeswin/

Created Nov 29, 2017
What would you like to do?

Basho: Shell pipelines with the goodness of JS templates

Basho evaluates a pipeline of instructions left to right. Instructions can be JavaScript code, reference to an external JS file, or a shell command. To evaluate a JavaScript expression, use the option -j. Let’s start with a single item in the pipeline, a JavaScript constant.

# Prints 100
basho -j 100

# Prints true
basho -j true

# Prints 100
basho -j 10**2

Good news is, the option -j can be omitted for the first expression.

# This works too
basho 100

# Prints 100
basho 10**2

The option -p avoids printing the final result. I am not sure where you'd need to use it, but it's there.

# Prints nothing
basho -p 100

Working with strings is a little difficult. Since bash with chew the quotes for itself. So you’d need to either use single quotes around your double quotes, or just use the -q option for quoting.

# Prints hello, world
basho '"hello, world"'

# Here's a better way to do this
basho -q hello, world

You can pipe an expression into a subsequent expression. The variable ‘x’ is always used as a placeholder for receiving the previous input.

# Prints 10000
basho 100 -j x**2

Execute shell commands with the -e option. The shell command is expanded as a JS template string, with the variable ‘x’ holding the input from the preceding command in the pipeline. Remember to quote or escape characters which hold a special meaning in your shell, such as $, >, <, |, () etc.

# Prints 1000. Escape the $.
basho 1000 -e echo \${x}

You can import a function from a JS file or an npm module with the -i option. The -i option takes two parameters; a filename or module name and an alias for the import. An import is available in all subsequent expressions throughout the pipeline.

# cat square.js
module.exports = function square(n) { return n ** 2; }

# prints 100. Imports square.js as sqr.
basho 10 -i square.js sqr -j "sqr(x)"

# Prints 40000. Does sqr(10), then adds 100, then sqr(200)
basho 10 -i square.js sqr -j "sqr(x)" -j x+100 -j "sqr(x)"

basho can receive input via stdin. As always, ‘x’ represents the input.

# Prints 100
echo 10 | basho parseInt(x)**2

If the input ‘x’ to a shell command is an array, the command is executed for each item in the array.

# echo 1; echo 2; echo 3; echo 4
basho [1,2,3,4] -e echo \${x}

The input ‘x’ can also be an object, which you can expand in the template string.

basho "{ name: 'jes', age: 100 }" -e echo \${}, \${x.age}

You can use an Array of objects.

# echo kai; echo niki
basho "[{name:'kai'}, {name:'niki'}]" -e echo \${}

Array of arrays, sure.

# echo 1 2 3; echo 3 4 5
basho "[[1,2,3], [3,4,5]]" -e echo \${x[0]} \${x[1]} \${x[2]}

A command can choose to receive the entire array at once with the -a option.

# echo 4
basho [1,2,3,4] -a x.length -e echo \${x}

That’s useful for filtering arrays.

# echo 3; echo 4
basho [1,2,3,4] -a "x.filter(x => x > 2)" -e echo \${x}

There’s a shorthand for filter, the option -f.

# echo 3; echo 4
basho [1,2,3,4] -f x>2 -e echo \${x}

There’s reduce too. Here’s the long form.

# Prints the sum 10
basho [1,2,3,4] -a "x.reduce((acc,x)=>acc+x,0)" -e echo \${x}

Shorthand for reduce, the option -r. The first parameter is the lambda, the second parameter is the initial value of the accumulator.

# Prints the sum 10
basho [1,2,3,4] -r acc+x 0 -e echo \${x}

Btw, you could also access an array index in the template literal as the variable ‘i’ in lambdas and shell command templates.

# echo a1; echo b2; echo c3
basho "['a','b','c']" -e echo \${x}\${i}

You can extend the pipeline further after a shell command. The shell command’s stdout becomes the input for the next command.

# echo 110 - which is (10^2) + 10
basho 10 -j x**2 -e echo \${x} -j "parseInt(x)+10" -e echo \${x}

There’s nothing stopping you from using all the piping magic built into your shell.

# Prints 100
basho 10 "x**2" | echo

Promises! If an JS expression evaluates to a promise, it is resolved before passing it to the next command in the pipeline.

# Prints 10
basho "Promise.resolve(10)" -e echo \${x}

# Something more useful
basho -i node-fetch fetch \
 -j "fetch('')" \
 -e echo \${x}

Typing basho without any parameters does nothing but might make you happy. Or sad.


Real world use-cases

Count the number of occurences of a word in a string or file.

echo hello world hello hello | basho "(x.match(/hello/g) || []).length"```

That’s all guys. Report issues or ping me on Twitter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.