npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

pshell

v1.1.0

Published

Promise-based portable interface for running shell commands

Downloads

17

Readme

pshell

Provides a simple Promise-based interface for running shell commands.

Basic usage

var shell = require("pshell");

shell("node --version").then(res => {
  console.log("exit code:", res.code);
});

/****** console output *******
 /bin/sh -c node --version
 v4.5.0
 exit code: 0
******************************/

Why?

Writing a shell script in JavaScript is:

  • Easier than bash script to most developers.
  • More portable (don't leave Windows users behind).
  • More powerful in managing child processes in asynchronous way.

More details

Don't echo the commands.

var shell = require("pshell");

shell("node --version", {echoCommand: false}).then(res => {
  console.log("exit code:", res.code);
});

/****** console output *******
 v4.5.0
 exit code: 0
******************************/

Capture the output as a string.

var shell = require("pshell");

shell("node --version", {echoCommand: false, captureOutput: true}).then(res => {
  console.log("stdout:", JSON.stringify(res.stdout));
  console.log("exit code:", res.code);
});

/****** console output *******
 stdout: "v4.5.0\n"
 exit code: 0
******************************/

Configure the global options so you don't need to specify the same options every time.

var shell = require("pshell");

shell.options.echoCommand = false;
shell.options.captureOutput = true;

Promise.all([
  shell("node --version"),
  shell("npm --version")
]).then(res => {
  process.stdout.write("Node version: " + res[0].stdout);
  process.stdout.write("NPM version: " + res[1].stdout);
});

/****** console output *******
 Node version: v4.5.0
 NPM version: 3.10.6
******************************/

You can get a pre-configured version of shell function by calling the context API. This is a good way to avoid modifying the global options. Other pshell users in the same process won't be affected.

var shell = require("pshell").context({echoCommand: false, captureOutput: true});

Promise.all([
  shell("node --version"),
  shell("npm --version")
]).then(res => {
  process.stdout.write("Node version: " + res[0].stdout);
  process.stdout.write("NPM version: " + res[1].stdout);
});

/****** console output *******
 Node version: v4.5.0
 NPM version: 3.10.6
******************************/

A non-zero exit code rejects the promise by default.

var shell = require("pshell").context({echoCommand: false});

// Using double quotes here is intentional. Windows shell supports double quotes only.
shell('node -e "process.exit(1)"').then(res => {
  console.log("exit code:", res.code);
}).catch(err => {
  console.error("error occurred:", err.message);
});

/****** console output *******
 error occurred: [Error: Process 26546 exited with code 1]
******************************/

Set ignoreError to true if you want to get the promise resolved instead.

var shell = require("pshell").context({echoCommand: false, ignoreError: true});

// Using double quotes here is intentional. Windows shell supports double quotes only.
shell('node -e "process.exit(1)"').then(res => {
  console.log("exit code:", res.code);
}).catch(err => {
  console.error("error occurred:", err.message);
});

/****** console output *******
 exit code: 1
******************************/

API

shell(command[, options])

Executes the command string using the system's default shell ("/bin/sh" on Unix, "cmd.exe" on Windows). An optional options object can be given to override the base options for this session.

Returns a promise that will be resolved with the result object when the command execution completes.

shell.options

An object represents the base options to be applied to every session executed from this context. You can modify fields of this object to change the default behavior.

shell.context([options])

Creates a new shell() function that is pre-configured with the specified options (combined with the current base options).

shell.spawn(exec[, args[, options]])

Executes a child process specified in exec directly without the system shell layer. args is an array of string arguments to be given to the process. Returns an object {childProcess, promise}. See ChildProcess for more details about Node's underlying child process object.

This is the base method for implementing shell() and shell.exec().

var shell = require("pshell").context({echoCommand: false});

var ret = shell.spawn("node", ["--version"], {captureOutput: true});

console.log("Node process ID:", ret.childProcess.pid);

ret.promise.then(function(res) {
  console.log("stdout:", JSON.stringify(res.stdout));
});

/****** console output *******
 Node process ID: 16372
 stdout: "v4.5.0\n"
******************************/

shell.exec(command[, options])

Same as shell() but returns an object {childProcess, promise} instead of a promise. Primary reason that you might want to use this function instead of shell() is probably to access the child process object for advanced use cases. However, keep in mind that the child process object returned by this method represents the system shell process, not the command process.

Calling shell(cmd, opts) is same as calling shell.exec(cmd, opts).promise.

shell.env(def)

Gets an object containing key-value pairs of environment variables combined with process.env. This is a low level function that can be used to construct an object for options.rawEnv.

In most cases, you will not need to use this function directly. Using options.env would be sufficient. See options.env and options.rawEnv for more details.

Options

options.Promise (default: null)

A Promise constructor to use instead of the system default one.

options.echoCommand (default: true)

If truthy, prints the command string to the console. If you specify a function, it gets called like func(exec, args) with this set to the options object. If you return false from the function, the command is not executed. childProcess and the result object of the promise are null in this case.

options.ignoreError (default: false)

If truthy, a non-zero exit code doesn't reject the promise so you can continue to the next steps.

options.captureOutput (default: false)

If truthy, stdout of the child process is captured as a string in res.stdout. If falsy, it is printed to the parent's stdout. If you specify a function, it gets called like func(buf) with this set to the options object. buf is an instance of Buffer containing the captured content. The return value from this function is set to res.stdout. You can use this feature for advanced use cases such as handling custom character encoding or parsing JSON.

var shell = require("pshell");

shell('node -e "console.log(JSON.stringify({a:1,b:2}))"', {
  echoCommand: false,
  captureOutput: function(buf) {
    return JSON.parse(buf.toString());
  }
}).then(function(res) {
  console.log("type:", typeof res.stdout);
  console.log("data:", res.stdout);
});

/****** console output *******
 type: object
 data: { a: 1, b: 2 }
******************************/

options.captureError (default: false)

If truthy, 'stderr' of the child process captured as a string in res.stderr. You can specify a function as in captureOutput options.

options.normalizeText (default: true)

If truthy, the end of line characters in a captured string are normalized to "\n". Only used when capturing to a string is enabled (by options.captureOutput and/or options.captureError).

options.env

An object containing key-value pairs of environment variables, in addition to the default process.env. Each env object in context chain is merged together to form a combined object and that object is given to shell.env() function to construct a final raw object, combined with process.env for Node's underlying API.

var shell = require("pshell");

shell("tape **/*.js", {
  env: {
    PATH: ["node_modules/.bin", process.env.PATH]
  }
});

This example effectively inserts "node_modules/.bin" at the beginning of PATH while passing down other values of process.env intact to the child process. If you specify an array as a value of an environment variable, all elements are joined into a string, separated with a path delimiter (':' on Unix, ';' on Windows).

Also, options.env does one more important job automatically for you. Because environment variable names are case insensitive on Windows, having multiple variables that are only different in case causes a problem. For example, simply cloning process.env and setting PATH will create a new key PATH in additon to an existing key Path on Windows because Path is the default key name. options.env prevents this from happening by removing matching keys regardless of case before setting a new value.

options.rawEnv

If you don't want the automatic features of env, you can specify a raw object in rawEnv.

options.inputContent (default: null)

You can specify a string or an instance of Buffer to supply the input data to stdin of the child process.

Node's child_process.spawn() options

In addition to the proprietary options above, the following options of child_process.spawn() also work.

  • cwd
  • arg0
  • stdio (if specified, inputContent, captureOutput and captureError options are ignored)
  • detached
  • uid
  • gid

Result object

This objects is given as a value when the promise returned by one of execution functions is resolved.

res.code (number)

The exit code of child process.

res.stdout (string or any)

The captured stdout of child process. This field exists only when options.captureOutput is truthy. This is a string value by default but you can override its behavior with your own custom handler.

res.stderr (string or any)

The captured stderr of child process. This field exists only when options.captureError is truthy. This is a string value by default but you can override its behavior with your own custom handler.

Develop & contribute

Setup

git clone https://github.com/asyncmax/pshell
cd pshell
npm install
npm run lint  # run lint
npm test      # run test

Coding style

  • Use two spaces for indentation.
  • Use double quotes for strings.
  • Use ES5 syntax for lib & test.

License

MIT