exec method
Future<Result<ShellExecResult, ExecutionError> >
exec(
- String command, {
- ShellExecOptions? options,
override
Executes a shell command. Must never throw: all failures are encoded in the returned Result.
Implementation
@override
Future<Result<ShellExecResult, ExecutionError>> exec(
String command, {
ShellExecOptions? options,
}) async {
final token = options?.cancelToken;
if (token?.isCancelled ?? false) {
return const Err(ExecutionError(ExecutionErrorCode.aborted, 'aborted'));
}
final executable = Platform.isWindows ? 'cmd' : 'sh';
final args = Platform.isWindows ? ['/c', command] : ['-c', command];
Process process;
try {
process = await Process.start(
executable,
args,
workingDirectory: options?.cwd,
environment: options?.env,
);
} on Object catch (error) {
return Err(
ExecutionError(
ExecutionErrorCode.spawnError,
error.toString(),
cause: error,
),
);
}
final stdout = StringBuffer();
final stderr = StringBuffer();
ExecutionError? callbackError;
void collect(
StringBuffer target,
String chunk,
void Function(String)? callback,
) {
target.write(chunk);
if (callback == null) return;
try {
callback(chunk);
} on Object catch (error) {
callbackError = ExecutionError(
ExecutionErrorCode.callbackError,
error.toString(),
cause: error,
);
process.kill();
}
}
final stdoutDone = process.stdout
.transform(utf8.decoder)
.forEach((chunk) => collect(stdout, chunk, options?.onStdout));
final stderrDone = process.stderr
.transform(utf8.decoder)
.forEach((chunk) => collect(stderr, chunk, options?.onStderr));
Timer? timer;
var timedOut = false;
final timeout = options?.timeout;
if (timeout != null) {
timer = Timer(timeout, () {
timedOut = true;
process.kill();
});
}
void onCancel(_) {
process.kill();
}
token?.onCancel.then(onCancel);
final exitCode = await process.exitCode;
timer?.cancel();
await Future.wait([stdoutDone, stderrDone]);
if (callbackError != null) return Err(callbackError!);
if (timedOut) {
return Err(
ExecutionError(ExecutionErrorCode.timeout, 'timeout: $timeout'),
);
}
if (token?.isCancelled ?? false) {
return const Err(ExecutionError(ExecutionErrorCode.aborted, 'aborted'));
}
return Ok(
ShellExecResult(
stdout: stdout.toString(),
stderr: stderr.toString(),
exitCode: exitCode,
),
);
}