Skip to content

Instantly share code, notes, and snippets.

@jeswin
Created November 29, 2017 06:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeswin/8ecef2ab704815df00f0965adcad40d6 to your computer and use it in GitHub Desktop.
Save jeswin/8ecef2ab704815df00f0965adcad40d6 to your computer and use it in GitHub Desktop.

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.name}, \${x.age}

You can use an Array of objects.

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

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('http://oaks.nvg.org/basho.html')" \
 -e echo \${x}

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

basho

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