nscript
v0.1.10
Published
Javascript powered shell scripts
Downloads
102
Readme
nscript
Write shell scripts like a bearded guru by unleashing your javascript skills!
- API documentation
- npm package (includes typescript typings)
nscript
is a tool to write (sophisticated) shell scripts using javascript. It offers synchronous process spawning, command line argument parsing, parameter expansions, parallel execution, stream redirection, pipes; in short; anything that you liked about bash is now possible in javascript. nscript
ships with a REPL as well.
nscript
is already being used in for production systems at Mendix to power CI scripts for deployment, automated testing, saucelabs integration etc.
#!/usr/bin/nscript
module.exports = function(shell, echo, $beard) {
if ($beard)
echo("Awesome, you invoked this script with --beard. You probably are an unixian.");
else if (shell.prompt("Do you have a mustache at least?","y") === "y")
echo("Welcome, oh hairy ", shell.env.USER);
else
shell.exit(1, "Epic fail.");
};
$ ./unixian.js
Do you have a mustache at least? [y]: n
Epic fail.
$
More examples can be found in the examples directory.
Installing nscript
Install nscript
using: npm install [-g] [--save[-dev]] nscript
.
nscript
relies on node-gyp, so if any errors occur upon installation, check its dependencies.
nscript
primer
An nscript
script is just a function exposed by a CommonJS module, preceded by a hashbang. The first parameter passes in the shell
object, other parameternames are filled with wrapped executables with the same name. Use $flag
or $$param
as parameter names to make it possible for users to pass in arguments to your script.
The full API documentation
#!/usr/bin/nscript
module.exports = function(shell, grep, ls, cat, echo, gedit, sort, whoami) {
// run a command
// bash: echo hello world
echo("hello","world")
// use shell expansions
// bash: echo src/*.js
echo("lib/*.js")
// or, to display all files recusively in lib/
cat("lib/**/*.js")
// prevent shell expansion
// bash: echo 'lib/*.js'
echo(["lib/*.js"])
// obtain output
var result = echo.get("hello","world")
// nest commands
// bash: echo hello `whoami`
echo("hello", whoami.get())
// check exit status
// bash: echo hello world; echo $?
var exitCode = echo.code("hello","world")
// supress output
// bash: ls > /dev/null
ls.silent().run()
// write output to file
// bash: ls > dir.txt
ls.write('test/tmp/dir.txt')
// append output to file
// bash: ls >> dir.txt
ls.append('test/tmp/dir.txt')
// pipe data into a process
// bash: echo "pears\napples" | sort
sort.input("pears\napples").run()
// prompt for input
// bash: echo -n "Your age? "; read $AGE
var age = shell.prompt("Your age?")
// start a process in the background
// bash: gedit test/groceries.txt &
gedit.detach("test/groceries.txt")
// pipe processes
// bash: cat test/groceries | grep '.js' | sort -i
var sortedMilks = cat.args("test/groceries.txt").pipe(grep,"milk").pipe(sort,"-i").get()
// read input from file and to file
// bash: grep milk < groceries.txt > milksonly.txt
grep.read('test/groceries.txt').args('milk').write('test/tmp/milksonly.txt')
// spawn() provides fine grained input / output control append standard error to file
// bash: ls *.js 2>> errors.txt | sort -u
ls.args("lib/*.js").spawn().appendError('test/tmp/errors.txt').pipe(sort, "-u").wait()
}
Anatomy of a nscript
script
The anatomy of script file can best be explained by looking at the following example script:
#!/usr/bin/nscript
module.exports = function(shell, $0, echo, whoami, $verbose) {
if ($0)
echo("Hello, ", $0)
else
echo("Hello, ", whoami.get())
}
The lines explained in detail:
- The first line is a so called
shell bang
to indicate unix based systems how to run this script. It is basically sugar fornscript thisfile.js
. The line is further meaningless and ignored by node. - A
nscript
script exposes a single function throughmodule.exports
. This is the function that will be interpreted and run bynscript
. Of course it is possible to define many functions in the javascript file, but only one should be exposed. $0
is the first argument passed to this script.$verbose
makes sure the--verbose
command line flag is parsed automatically. See the documentation for more details about automatic parsing of command line flags.echo
is passed into the function bynscript
as an alias for the "echo" command. This is basically sugar for:var echo = shell.alias("echo");
. By invoking theecho
function,nscript
starts theecho
executable, and passes in the arguments provided to the function.whoami
is an alias for the "whoami" command, which returns the name of the currently logged in user (on Unix systems). The.get()
functions grabs the standard output of a command. In this cause, the output is passed to echo. (In shell scripts, this statement would be expressed asecho "Hello, " `whoami`
.
Running nscripts without global nscript
.
If nscript
is installed as module of your node/npm project, you can also start nscript
scripts without requiring a globally installed nscript:
#!/usr/bin/env node
require('nscript')(function(shell, echo, $0) {
echo("Hello", $0);
});
$ ./greeter.js Michel
Hello Michel
Creating scripts with nscript --touch
To quickly start with a new script, you can use the convenient nscript --touch
command, but of course you can also create script manually. nscript
scripts are just plain javascript (commonjs module) files. The --touch
command also makes sure the script will be executable. Use the additional --local
if the script shouldn't rely on a globally installed nscript, but a node
project dependency instead.
michel@miniub ~/demo $ nscript --touch myscript.js
Generating default script in 'myscript.js'
Marking script as executable: 'myscript.js'
michel@miniub ~/demo $ ./myscript.js
Hello, world!
Random questions
Starting nscripts programmatically
This option does not require nscript
to be installed globally.
$ npm install nscript --save
require('nscript')(require('./script.js'));
Todo: working with Futures
(add example of async code + futures & nested nscript functions)
Future plans
- Windows support
- Minor improvements
Comparison to other tools.
Grunt
Grunt allows for high level declaritive writing of tasks. However, spawning new jobs, grabbing there output or using pipes and stdin/ stdout streams can not be done out of the box. Luckily, nscript
can be used from within grunt scripts as well, so feel free to combine the best of both worlds! Or feel free to write a grunt-nscript plugin ;-).
ShellJS
ShellJS is an excellent tool and performs many typical build script tasks in a synchronous manner. Furthermore it behaves consistently on all platforms.
Nscript tries to be a more heavy duty tool and does not (yet) fully support windows. On the other hand, it supports more typical shell (for example bash
) features. Some differences with ShellJS:
- nscript allows for parallel execution of commands, even if they are invoked in a synchronously / blocking manner. See for example
tests/shell.js testParallel
- nscripts is very flexibile in input and output stream handling, it is possible to pipe streams, redirect to output (without buffering) or processing output streams in parallel. TODO: add test case for onLine(cb) + wait()
- ShellJS offers a default implementation of several system commands. The advantage of this is that they work uniform on every platform, the disadvantage is that only a limited set of features is supported
- nscript aims to help you to write fully fledged shell scripts and offers command line parsing out of the box
- it is possible start processes in the background
- nscript is more efficient in its approach to spawn processes synchronously; it doesn't block the main event loop
- in short: nscript offers more fine grained process control, ShellJS offers less features, but the features that are offered are platform consistent and the api is a bit simpler.