Grunt plugin for executing shell commands with comprehensive configuration options
npx @tessl/cli install tessl/npm-grunt-exec@3.0.0grunt-exec is a Grunt plugin that provides comprehensive shell command execution capabilities within Grunt build processes. It offers extensive configuration options for process control, input/output handling, error management, and both synchronous and asynchronous execution modes with cross-platform compatibility.
npm install grunt-exec --save-devAfter installing, load the plugin in your Gruntfile:
grunt.loadNpmTasks('grunt-exec');grunt.initConfig({
exec: {
simple_command: 'echo "Hello World"',
advanced_command: {
command: 'ls -la',
stdout: true,
stderr: true,
cwd: './src'
},
function_command: {
cmd: function(arg1, arg2) {
return 'echo "Arguments: ' + arg1 + ', ' + arg2 + '"';
}
}
}
});
// Run tasks
grunt.registerTask('default', ['exec:simple_command']);grunt-exec is built around the Grunt multitask system:
Core capability for executing shell commands with comprehensive configuration options.
/**
* Main plugin registration function
* @param grunt - The Grunt object instance
*/
function(grunt: GruntObject): void;
/**
* Exec task configuration object
*/
interface ExecTaskConfig {
/** The shell command to execute (required) */
command?: string | CommandFunction;
/** Alias for command */
cmd?: string | CommandFunction;
/** Current working directory */
cwd?: string | CwdFunction;
/** Control stdin behavior */
stdin?: boolean | string;
/** Control stdout behavior */
stdout?: boolean | string;
/** Control stderr behavior */
stderr?: boolean | string;
/** Shorthand for stdio configuration */
stdio?: 'inherit' | 'pipe' | 'ignore';
/** Use synchronous execution */
sync?: boolean;
/** Expected exit code(s) */
exitCode?: number | number[];
/** Alias for exitCode */
exitCodes?: number | number[];
/** Custom callback function */
callback?: CallbackFunction;
/** Additional callback arguments */
callbackArgs?: any[];
/** Character encoding for output */
encoding?: string;
/** Maximum buffer size for output */
maxBuffer?: number;
/** Command timeout in milliseconds */
timeout?: number;
/** Signal to use when killing process */
killSignal?: string;
/** Shell to use for command execution */
shell?: boolean | string;
/** Advanced process options */
options?: ProcessOptions;
}
/**
* Command function type for dynamic commands
*/
interface CommandFunction {
(...args: any[]): string;
}
/**
* Working directory function type
*/
interface CwdFunction {
(...args: any[]): string;
}
/**
* Callback function for handling command results
*/
interface CallbackFunction {
(error: Error | null, stdout: string | Buffer, stderr: string | Buffer, callbackArgs?: any[]): void;
}
/**
* Advanced process options
*/
interface ProcessOptions {
/** Current working directory */
cwd?: string;
/** Environment variables */
env?: { [key: string]: string };
/** Character encoding */
encoding?: string;
/** Shell to execute command with */
shell?: string | boolean;
/** Timeout in milliseconds */
timeout?: number;
/** Maximum buffer size */
maxBuffer?: number;
/** Kill signal */
killSignal?: string;
/** User identity */
uid?: number;
/** Group identity */
gid?: number;
/** Stdio configuration */
stdio?: (string | number)[];
}grunt-exec supports multiple configuration patterns for different use cases.
For basic commands, use a simple string:
exec: {
simple_task: 'echo "Hello World"'
}For advanced options, use an object:
exec: {
advanced_task: {
command: 'npm test',
cwd: './packages/core',
stdout: true,
stderr: true,
exitCodes: [0, 1],
timeout: 30000
}
}For dynamic commands that accept command-line arguments:
exec: {
dynamic_task: {
cmd: function(environment, version) {
return 'deploy --env=' + environment + ' --version=' + version;
}
}
}Run with: grunt exec:dynamic_task:production:1.2.0
For processing command output:
exec: {
callback_task: {
cmd: 'git log --oneline -10',
callback: function(error, stdout, stderr) {
if (error) {
grunt.log.error('Git command failed: ' + error.message);
return;
}
var commits = stdout.split('\n').filter(Boolean);
grunt.log.writeln('Found ' + commits.length + ' recent commits');
commits.forEach(function(commit) {
grunt.log.writeln(' ' + commit);
});
}
}
}Comprehensive control over process stdin, stdout, and stderr.
// Enable stdout (default)
stdout: true
// Disable stdout
stdout: false
// Pipe stdout
stdout: 'pipe'
// Inherit from parent process
stdout: 'inherit'
// Ignore stdout
stdout: 'ignore'// Enable stderr (default)
stderr: true
// Disable stderr
stderr: false
// Pipe stderr for processing
stderr: 'pipe'
// Inherit from parent process
stderr: 'inherit'// Enable stdin interaction (experimental)
stdin: true
// Disable stdin (default)
stdin: false
// Inherit stdin from parent
stdin: 'inherit'// Inherit all stdio from parent
stdio: 'inherit'
// Pipe all stdio
stdio: 'pipe'
// Ignore all stdio
stdio: 'ignore'Advanced options for controlling command execution behavior.
// Static working directory
cwd: '/path/to/directory'
// Dynamic working directory
cwd: function(arg1, arg2) {
return './environments/' + arg1;
}// Single expected exit code (default: 0)
exitCode: 0
// Multiple allowed exit codes
exitCodes: [0, 1, 2]
// Allow any exit code
exitCodes: []// 30 second timeout
timeout: 30000
// No timeout (default)
timeout: 0// Custom kill signal
killSignal: 'SIGKILL'
// Default termination signal
killSignal: 'SIGTERM'Control execution mode based on your needs.
exec: {
async_task: {
cmd: 'long-running-command',
sync: false // or omit (default)
}
}exec: {
sync_task: {
cmd: 'quick-command',
sync: true
}
}Control how command output is handled and processed.
// UTF-8 encoding (default)
encoding: 'utf8'
// Binary encoding
encoding: 'binary'
// Buffer objects (no string conversion)
encoding: 'buffer'// Custom buffer size (200KB default)
maxBuffer: 1024 * 1024 // 1MB
// Unlimited buffering
maxBuffer: 0exec: {
process_output: {
cmd: 'npm ls --json',
encoding: 'utf8',
callback: function(error, stdout, stderr) {
if (error) {
grunt.fail.warn('Command failed: ' + error.message);
return;
}
try {
var packageInfo = JSON.parse(stdout);
grunt.log.writeln('Package: ' + packageInfo.name);
grunt.log.writeln('Version: ' + packageInfo.version);
} catch (e) {
grunt.log.error('Failed to parse JSON output');
}
}
}
}Comprehensive error handling with detailed logging options.
Enable verbose logging for debugging:
grunt exec:my_task --verboseVerbose mode provides detailed information about:
grunt-exec handles various error conditions:
exec: {
error_handling: {
cmd: 'potentially-failing-command',
exitCodes: [0, 1], // Allow exit code 1
callback: function(error, stdout, stderr) {
if (error) {
if (error.code === 'ENOENT') {
grunt.log.error('Command not found');
} else if (error.message.includes('timeout')) {
grunt.log.error('Command timed out');
} else {
grunt.log.error('Command failed: ' + error.message);
}
return;
}
// Process successful output
grunt.log.ok('Command completed successfully');
}
}
}Control shell behavior and compatibility across platforms.
// Use default shell (recommended for cross-platform)
shell: true
// Use specific shell
shell: '/bin/bash'
// Disable shell (direct command execution)
shell: falsegrunt-exec handles platform differences automatically:
exec: {
cross_platform: {
cmd: process.platform === 'win32'
? 'dir /b'
: 'ls -1',
shell: true
}
}Pass custom environment variables to commands.
exec: {
with_env: {
cmd: 'echo $NODE_ENV',
options: {
env: {
NODE_ENV: 'production',
API_KEY: process.env.API_KEY
}
}
}
}exec: {
build: {
cmd: 'npm run build',
cwd: './client'
},
test: {
cmd: 'npm test',
exitCodes: [0, 1] // Allow test failures
},
deploy: {
cmd: function(env) {
return 'docker build -t myapp:' + env + ' .';
}
}
}exec: {
git_status: 'git status --porcelain',
git_commit: {
cmd: function(message) {
return 'git commit -m "' + message + '"';
}
},
git_push: {
cmd: 'git push origin main',
callback: function(error, stdout, stderr) {
if (error) {
grunt.fail.warn('Push failed: ' + stderr);
} else {
grunt.log.ok('Push successful');
}
}
}
}exec: {
cleanup: {
cmd: process.platform === 'win32' ? 'del /q *.log' : 'rm -f *.log',
stdout: false,
stderr: false
},
backup: {
cmd: 'tar -czf backup.tar.gz ./src',
timeout: 60000 // 1 minute timeout
}
}exec: {
dev_server: {
cmd: 'npm run dev',
stdio: 'inherit', // Show server output
timeout: 0 // No timeout for long-running server
},
stop_server: {
cmd: process.platform === 'win32'
? 'taskkill /f /im node.exe'
: 'pkill -f "npm run dev"',
exitCodes: [0, 1] // Allow if no process found
}
}