Portable Unix shell commands for Node.js that provide cross-platform compatibility across Windows, Linux, and macOS.
—
Configuration system, error handling, environment variables, and utility functions for controlling ShellJS behavior and extending functionality.
Global configuration object that controls the behavior of all ShellJS commands.
/**
* Global configuration object for controlling ShellJS behavior
*/
const config: {
/** Suppress all command output if true (default: false) */
silent: boolean;
/** Throw JavaScript errors on command failures if true (default: false) */
fatal: boolean;
/** Print each command as it executes if true (default: false) */
verbose: boolean;
/** Options passed to fast-glob for file matching (deprecated) */
globOptions: object;
/** Path to Node.js executable (null for auto-detect) */
execPath: string | null;
/** Maximum directory depth for recursive operations (default: 255) */
maxdepth: number;
/** Disable glob expansion if true (default: false) */
noglob: boolean;
/** Buffer length for I/O operations (default: 64KB) */
bufLength: number;
/** Reset configuration to default values */
reset(): void;
};Usage Examples:
// Enable silent mode (suppress output)
shell.config.silent = true;
shell.ls(); // No output to console
// Enable fatal mode (throw errors on failures)
shell.config.fatal = true;
try {
shell.cp('nonexistent.txt', 'dest.txt'); // Throws error
} catch (e) {
console.log('Command failed:', e.message);
}
// Enable verbose mode (show commands)
shell.config.verbose = true;
shell.cd('mydir'); // Prints: cd mydir
// Save and restore configuration
const originalSilent = shell.config.silent;
shell.config.silent = true;
// ... do silent operations
shell.config.silent = originalSilent;
// Reset to defaults
shell.config.reset();
// Custom exec path (for bundled environments)
shell.config.execPath = '/custom/path/to/node';
// Disable glob expansion
shell.config.noglob = true;
shell.ls('*.txt'); // Treats *.txt literally, not as glob patternDirect access to environment variables as a convenient alias for process.env.
/**
* Environment variables object (alias for process.env)
*/
const env: NodeJS.ProcessEnv;Usage Examples:
// Read environment variables
console.log('Home directory:', shell.env.HOME);
console.log('Path:', shell.env.PATH);
// Set environment variables
shell.env.NODE_ENV = 'production';
shell.env.CUSTOM_VAR = 'custom_value';
// Use in commands
const nodeVersion = shell.env.NODE_VERSION || '18';
shell.exec(`nvm use ${nodeVersion}`);
// Check if variable exists
if (shell.env.CI) {
shell.echo('Running in CI environment');
}
// Remove environment variable
delete shell.env.TEMP_VAR;
// Environment-aware operations
const configFile = shell.env.NODE_ENV === 'production'
? 'config.prod.json'
: 'config.dev.json';Returns the error message from the last command that failed, or null if no error occurred.
/**
* Check if error occurred in the last command
* @returns Error message string if error occurred, null otherwise
*/
function error(): string | null;Usage Examples:
// Check for errors after commands
shell.cp('source.txt', 'dest.txt');
if (shell.error()) {
console.log('Copy failed:', shell.error());
}
// Error handling in loops
const files = ['file1.txt', 'file2.txt', 'file3.txt'];
files.forEach(file => {
shell.cp(file, 'backup/');
if (shell.error()) {
console.log(`Failed to backup ${file}:`, shell.error());
}
});
// Conditional logic based on errors
shell.mkdir('new_directory');
if (!shell.error()) {
shell.echo('Directory created successfully');
shell.cd('new_directory');
}
// Clear error state (done automatically by next command)
shell.rm('nonexistent.txt'); // Sets error
console.log(shell.error()); // Shows error message
shell.echo('test'); // Clears error state
console.log(shell.error()); // Returns nullReturns the numeric error code from the last command, or 0 if no error occurred.
/**
* Get error code from the last command
* @returns Numeric error code (0 for success, non-zero for errors)
*/
function errorCode(): number;Usage Examples:
// Check error codes
shell.exec('npm test');
if (shell.errorCode() !== 0) {
shell.echo(`Tests failed with code ${shell.errorCode()}`);
shell.exit(shell.errorCode());
}
// Compare error codes
shell.rm('protected_file.txt');
const code = shell.errorCode();
switch (code) {
case 0:
shell.echo('File removed successfully');
break;
case 1:
shell.echo('Permission denied');
break;
case 2:
shell.echo('File not found');
break;
default:
shell.echo(`Unknown error: ${code}`);
}
// Use error codes for flow control
function tryOperation() {
shell.mkdir('temp_dir');
if (shell.errorCode() === 0) {
// Success path
shell.cd('temp_dir');
return true;
} else {
// Error path
return false;
}
}Sets shell options similar to the bash set command, providing an alternative way to configure behavior.
/**
* Set shell options (similar to bash set command)
* @param options - Option string (e.g., '-e', '+e', '-v', '+v')
* @returns ShellString with operation result
*/
function set(options: string): ShellString;Options:
-e / +e: Enable/disable exit on error (sets config.fatal)-v / +v: Enable/disable verbose mode (sets config.verbose)-f / +f: Disable/enable filename expansion (globbing) (sets config.noglob)Usage Examples:
// Enable exit on error
shell.set('-e');
shell.cp('nonexistent.txt', 'dest.txt'); // Will throw error
// Disable exit on error
shell.set('+e');
shell.cp('nonexistent.txt', 'dest.txt'); // Won't throw, sets error state
// Enable verbose mode
shell.set('-v');
shell.ls(); // Shows command before execution
// Disable verbose mode
shell.set('+v');
// Disable globbing
shell.set('-f');
shell.ls('*.txt'); // Treats *.txt literally, not as pattern
// Enable globbing
shell.set('+f');
shell.ls('*.txt'); // Expands *.txt pattern
// Multiple options
shell.set('-ev'); // Enable both error exit and verbose mode
// Use in scripts for strict error handling
shell.set('-e'); // Exit on any error
shell.exec('npm install');
shell.exec('npm test');
shell.exec('npm run build');
// Script will exit early if any command failsThe return type for most ShellJS commands, extending String with additional properties and methods for command chaining and file operations.
/**
* ShellJS command return type with additional properties and methods
*/
class ShellString extends String {
/** Standard output from the command */
stdout: string;
/** Standard error output from the command */
stderr: string;
/** Exit code from the command (0 for success) */
code: number;
/**
* Create a new ShellString
* @param stdout - Standard output content
* @param stderr - Standard error content
* @param code - Exit code
*/
constructor(stdout: string | string[], stderr?: string, code?: number);
/**
* Write ShellString content to file (overwrite)
* @param file - Target file path
* @returns Same ShellString for chaining
*/
to(file: string): ShellString;
/**
* Append ShellString content to file
* @param file - Target file path
* @returns Same ShellString for chaining
*/
toEnd(file: string): ShellString;
}Usage Examples:
// Access command output properties
const result = shell.exec('ls -la');
console.log('Exit code:', result.code);
console.log('Output:', result.stdout);
console.log('Errors:', result.stderr);
// Use as string
const files = shell.ls();
console.log('Files:', files.toString());
console.log('Count:', files.split('\n').length);
// Method chaining with file output
shell.cat('input.txt')
.sed('old', 'new')
.to('output.txt');
// Pipe to append
shell.echo('Log entry: ' + new Date())
.toEnd('application.log');
// Create custom ShellString
const customResult = new shell.ShellString('custom output', '', 0);
customResult.to('custom.txt');
// Check for command success
const gitStatus = shell.exec('git status', { silent: true });
if (gitStatus.code === 0) {
console.log('Git repository is clean');
} else {
console.log('Git error:', gitStatus.stderr);
}
// Array-like behavior for some commands
const fileList = shell.ls();
fileList.forEach(file => {
console.log('Processing:', file);
});ShellJS provides a plugin system for extending functionality with custom commands.
/**
* Plugin utilities for extending ShellJS (from 'shelljs/plugin')
*/
interface PluginAPI {
/** Register a new command */
register(name: string, implementation: Function, options?: RegisterOptions): void;
/** Signal errors from within commands */
error(message: string, code?: number, options?: ErrorOptions): void;
/** Parse command options */
parseOptions(optionString: string, optionMap: object, options?: ParseOptions): object;
/** Read input from pipe for commands that accept piped input */
readFromPipe(): string;
}
interface RegisterOptions {
/** Allow command to receive piped input */
canReceivePipe?: boolean;
/** Command-specific option definitions */
cmdOptions?: object;
/** Allow glob patterns in arguments */
allowGlobbing?: boolean;
/** Use Unix-style behavior */
unix?: boolean;
/** Don't wrap output in ShellString */
wrapOutput?: boolean;
}Usage Examples:
// Create a plugin file
const shell = require('shelljs');
const plugin = require('shelljs/plugin');
// Register a custom command
plugin.register('count', countLines, {
canReceivePipe: true,
cmdOptions: {
'l': 'lines',
'w': 'words',
'c': 'chars'
}
});
function countLines(options, ...files) {
// Get piped input if available
let input = plugin.readFromPipe();
// Parse options
const opts = plugin.parseOptions(options, {
'l': 'lines',
'w': 'words',
'c': 'chars'
});
// Implementation
if (!input && files.length === 0) {
plugin.error('No input provided');
return;
}
// Process files or input
const content = input || shell.cat(files).toString();
const lines = content.split('\n').length;
const words = content.split(/\s+/).length;
const chars = content.length;
if (opts.lines) return lines;
if (opts.words) return words;
if (opts.chars) return chars;
return `${lines} ${words} ${chars}`;
}
// Use the custom command
shell.count('file.txt');
shell.cat('file.txt').count('-l');ShellJS includes a make-like build system for task automation.
/**
* Make system for task automation (from 'shelljs/make')
*/
const target: {
[targetName: string]: (args?: string[]) => any;
};Usage Examples:
// In a make.js file
require('shelljs/make');
target.all = function() {
target.build();
target.test();
};
target.build = function() {
shell.echo('Building project...');
shell.exec('npm run build');
};
target.test = function() {
shell.echo('Running tests...');
shell.exec('npm test');
};
target.clean = function() {
shell.echo('Cleaning build artifacts...');
shell.rm('-rf', 'dist/');
shell.rm('-rf', 'coverage/');
};
target.deploy = function(args) {
const env = args && args[0] || 'staging';
shell.echo(`Deploying to ${env}...`);
shell.exec(`npm run deploy:${env}`);
};
// Run with: node make.js build
// Run with: node make.js deploy production
// Run with: node make.js --help// Setup based on environment
function configureShell() {
if (shell.env.NODE_ENV === 'production') {
shell.config.silent = true;
shell.config.fatal = true;
} else if (shell.env.NODE_ENV === 'development') {
shell.config.verbose = true;
}
// CI environment settings
if (shell.env.CI) {
shell.config.silent = true;
shell.config.fatal = true;
}
}
// Scoped configuration changes
function withSilentMode(callback) {
const originalSilent = shell.config.silent;
shell.config.silent = true;
try {
return callback();
} finally {
shell.config.silent = originalSilent;
}
}
// Usage
const result = withSilentMode(() => {
return shell.exec('npm list --depth=0');
});// Comprehensive error checking
function robustOperation() {
// Method 1: Check error state
shell.mkdir('temp');
if (shell.error()) {
console.log('Failed to create directory:', shell.error());
return false;
}
// Method 2: Check error code
shell.cp('source.txt', 'temp/');
if (shell.errorCode() !== 0) {
console.log('Copy failed with code:', shell.errorCode());
return false;
}
// Method 3: Use fatal mode with try/catch
const originalFatal = shell.config.fatal;
shell.config.fatal = true;
try {
shell.exec('npm install');
shell.exec('npm test');
return true;
} catch (e) {
console.log('Command failed:', e.message);
return false;
} finally {
shell.config.fatal = originalFatal;
}
}Install with Tessl CLI
npx tessl i tessl/npm-shelljs