Command provides a way to execute system commands and processes from @effect/platform.
Overview
import { Command } from "@effect/platform"
type Command = StandardCommand | PipedCommand
interface StandardCommand {
readonly _tag: "StandardCommand"
readonly command: string
readonly args: ReadonlyArray<string>
readonly env: HashMap<string, string>
readonly cwd: Option<string>
readonly shell: boolean | string
readonly stdin: Command.Input
readonly stdout: Command.Output
readonly stderr: Command.Output
}
Creating Commands
make
Create a command.const make: (command: string, ...args: Array<string>) => Command
import { Command } from "@effect/platform"
const cmd = Command.make("ls", "-la", "/tmp")
Executing Commands
string
Execute and get output as string.const string: {
(encoding?: string): (command: Command) => Effect<string, PlatformError, CommandExecutor>
(command: Command, encoding?: string): Effect<string, PlatformError, CommandExecutor>
}
import { Command } from "@effect/platform"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const output = yield* Command.string(
Command.make("echo", "Hello, World!")
)
console.log(output) // "Hello, World!\n"
})
lines
Execute and get output as lines.const lines: (
command: Command,
encoding?: string
) => Effect<Array<string>, PlatformError, CommandExecutor>
import { Command } from "@effect/platform"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const lines = yield* Command.lines(
Command.make("ls", "-1", "/tmp")
)
lines.forEach(line => console.log(line))
})
stream
Execute and get output as stream.const stream: (
command: Command
) => Stream<Uint8Array, PlatformError, CommandExecutor>
import { Command } from "@effect/platform"
import { Effect, Stream } from "effect"
const program = Effect.gen(function* () {
const output = Command.stream(Command.make("cat", "/var/log/app.log"))
yield* Stream.runForEach(output, (chunk) =>
Effect.sync(() => console.log(chunk))
)
})
streamLines
Execute and get output as stream of lines.const streamLines: (
command: Command,
encoding?: string
) => Stream<string, PlatformError, CommandExecutor>
exitCode
Get the exit code.const exitCode: (
self: Command
) => Effect<ExitCode, PlatformError, CommandExecutor>
import { Command } from "@effect/platform"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const code = yield* Command.exitCode(
Command.make("test", "-f", "/tmp/file.txt")
)
console.log(code) // 0 if file exists, non-zero otherwise
})
start
Start command and get process handle.const start: (
command: Command
) => Effect<Process, PlatformError, CommandExecutor | Scope>
import { Command } from "@effect/platform"
import { Effect } from "effect"
const program = Effect.scoped(
Effect.gen(function* () {
const process = yield* Command.start(
Command.make("node", "server.js")
)
// Process runs in background
// Automatically killed when scope closes
})
)
Configuration
env
Set environment variables.const env: {
(environment: Record<string, string | undefined>): (self: Command) => Command
(self: Command, environment: Record<string, string | undefined>): Command
}
import { Command } from "@effect/platform"
const cmd = Command.make("node", "script.js").pipe(
Command.env({ NODE_ENV: "production", API_KEY: "secret" })
)
workingDirectory
Set working directory.const workingDirectory: {
(cwd: string): (self: Command) => Command
(self: Command, cwd: string): Command
}
import { Command } from "@effect/platform"
const cmd = Command.make("npm", "install").pipe(
Command.workingDirectory("/path/to/project")
)
runInShell
Run command in shell.const runInShell: {
(shell: string | boolean): (self: Command) => Command
(self: Command, shell: string | boolean): Command
}
import { Command } from "@effect/platform"
// Run in default shell
const cmd1 = Command.make("echo", "$PATH").pipe(
Command.runInShell(true)
)
// Run in specific shell
const cmd2 = Command.make("echo", "$PATH").pipe(
Command.runInShell("/bin/bash")
)
Input/Output
stdin
Set standard input.const stdin: {
(stdin: Command.Input): (self: Command) => Command
(self: Command, stdin: Command.Input): Command
}
type CommandInput = "inherit" | "pipe" | Stream<Uint8Array, PlatformError>
import { Command, Stream } from "effect"
const input = Stream.make(
new TextEncoder().encode("line 1\n"),
new TextEncoder().encode("line 2\n")
)
const cmd = Command.make("sort").pipe(
Command.stdin(input)
)
feed
Feed string to stdin.const feed: {
(input: string): (self: Command) => Command
(self: Command, input: string): Command
}
import { Command } from "@effect/platform"
const cmd = Command.make("wc", "-l").pipe(
Command.feed("line 1\nline 2\nline 3")
)
stdout
Set standard output.const stdout: {
(stdout: Command.Output): (self: Command) => Command
(self: Command, stdout: Command.Output): Command
}
type CommandOutput = "inherit" | "pipe" | Sink<Uint8Array, Uint8Array>
stderr
Set standard error.const stderr: {
(stderr: Command.Output): (self: Command) => Command
(self: Command, stderr: Command.Output): Command
}
Piping
pipeTo
Pipe one command to another.const pipeTo: {
(into: Command): (self: Command) => Command
(self: Command, into: Command): Command
}
import { Command } from "@effect/platform"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const output = yield* Command.string(
Command.make("cat", "file.txt").pipe(
Command.pipeTo(Command.make("grep", "error")),
Command.pipeTo(Command.make("wc", "-l"))
)
)
console.log(`Found ${output.trim()} error lines`)
})
flatten
Flatten piped commands into array.const flatten: (
self: Command
) => NonEmptyReadonlyArray<StandardCommand>
Examples
Execute Simple Command
import { Command } from "@effect/platform"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const output = yield* Command.string(
Command.make("git", "status", "--short")
)
console.log(output)
})
Process Output Line by Line
import { Command } from "@effect/platform"
import { Effect, Stream } from "effect"
const program = Effect.gen(function* () {
const lines = Command.streamLines(
Command.make("tail", "-f", "/var/log/app.log")
)
yield* Stream.runForEach(lines, (line) =>
Effect.sync(() => console.log(`[LOG] ${line}`))
)
})
Complex Pipeline
import { Command } from "@effect/platform"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const errorCount = yield* Command.string(
Command.make("cat", "app.log").pipe(
Command.pipeTo(Command.make("grep", "ERROR")),
Command.pipeTo(Command.make("wc", "-l"))
)
)
console.log(`Total errors: ${errorCount.trim()}`)
})
With Environment and Working Directory
import { Command } from "@effect/platform"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const output = yield* Command.string(
Command.make("npm", "test").pipe(
Command.env({ NODE_ENV: "test", CI: "true" }),
Command.workingDirectory("/path/to/project")
)
)
console.log(output)
})