Skip to content

NeaByteLab/Exec-Command

Repository files navigation

Exec Command License: MIT Node.js CI

Execute shell commands in Node.js with timeouts, abort control, and streaming output.

πŸ“¦ Installation

npm install @neabyte/exec-command

πŸ”§ Module Support

// CommonJS
const exec = require('@neabyte/exec-command')

// ESM (ES Modules)
import exec from '@neabyte/exec-command'

πŸ“– API Usage

Basic Execution

import exec from '@neabyte/exec-command'

// Simple command execution - collects all output before resolving
const result = await exec('echo "Hello World"')
console.log(result.stdout) // "Hello World"
console.log(result.stderr) // ""
console.log(result.exitCode) // 0
console.log(result.kill) // Function to terminate the process

Streaming Output

// Real-time output streaming - processes data as it arrives
const stream = exec('ping -c 5 google.com', { stream: true })

// Process output in real-time using async iteration
for await (const chunk of stream.output) {
  console.log(chunk.toString()) // Buffer containing raw output
}

// Wait for process completion
await stream.promise // Returns ExecResult when done

// Access kill method for early termination
stream.kill() // Terminates the process immediately

Process Control

// Kill running processes - works for both streaming and buffered modes
const stream = exec('ping -c 20 google.com', { stream: true })

setTimeout(() => {
  stream.kill() // Kill with default SIGTERM signal
  stream.kill('SIGKILL') // Kill with specific signal
}, 5000)

// Buffered process can also be killed
const buffered = exec('long-running-command')
buffered.kill() // Terminates immediately, promise will reject

Timeout Control

// Set timeout (in milliseconds) - process will be killed if it runs too long
const result = await exec('slow-command', { timeout: 5000 })
// Process will be killed after 5 seconds with SIGTERM, then SIGKILL after 5 more seconds

// No timeout (default)
const result2 = await exec('command', { timeout: 0 })
// Process runs indefinitely until completion or manual kill

Working Directory

// Execute in specific directory - changes the current working directory
const result = await exec('ls', { cwd: '/path/to/directory' })
// Lists files in the specified directory

// Use relative paths
const result2 = await exec('pwd', { cwd: './src' })
// Shows the absolute path of the src directory

Environment Variables

// Custom environment variables - merged with existing process.env
const result = await exec('echo $MY_VAR', {
  env: { MY_VAR: 'Hello World' }
})
// Output: "Hello World"

// Multiple environment variables
const result2 = await exec('node -e "console.log(process.env.VAR1, process.env.VAR2)"', {
  env: {
    VAR1: 'First',
    VAR2: 'Second'
  }
})
// Output: "First Second"

// Override existing environment variables
const result3 = await exec('echo $HOME', {
  env: { HOME: '/custom/home' }
})
// Output: "/custom/home"

Shell Commands

// Complex shell commands with operators - automatically detected and executed with shell
const result = await exec('ls -la | grep .txt')
// Lists files and filters for .txt files

// Chained commands with &&
const result2 = await exec('cd /tmp && pwd')
// Changes to /tmp directory and shows current path

// Environment variable expansion
const result3 = await exec('echo $HOME && echo $USER')
// Shows home directory and username

// Pipes and redirections
const result4 = await exec('echo "Hello" | wc -c')
// Counts characters in "Hello" (output: 6)

// Complex shell operations
const result5 = await exec('find . -name "*.js" | head -5')
// Finds first 5 JavaScript files

βš™οΈ Options Reference

All options are optional and can be combined:

interface ExecOptions {
  /** Timeout duration in milliseconds (0 = no timeout) */
  timeout?: number
  /** Enable streaming mode for real-time output */
  stream?: boolean
  /** Working directory for command execution */
  cwd?: string
  /** Environment variables to pass to the process */
  env?: Record<string, string | undefined>
}

Option Details

  • timeout: Kill process after X milliseconds (0 = no timeout)
  • stream: true = real-time output, false = collect all output first
  • cwd: Run command in this directory
  • env: Add these environment variables

Return Types

Buffered Mode (stream: false):

interface ExecResult {
  stdout: string                             // Standard output
  stderr: string                             // Standard error
  exitCode: number                           // Process exit code
  kill(signal?: string | number): void       // Kill function
  then: Promise<ExecResult>['then']          // Promise methods
  catch: Promise<ExecResult>['catch']
  finally: Promise<ExecResult>['finally']
}

Streaming Mode (stream: true):

interface ExecStream {
  output: AsyncIterable<Buffer>              // Real-time output stream
  promise: Promise<ExecResult>               // Completion promise
  kill(signal?: string | number): void       // Kill function
}

πŸ“„ License

This project is licensed under the MIT license. See the LICENSE file for more info.