run static method
Runs command with no shell, so its arguments need no escaping.
timeout kills it and throws a ProcessTimedOutException.
Implementation
static Future<ProcessResultData> run(
List<String> command, {
String? workingDirectory,
Map<String, String>? environment,
Duration? timeout = const Duration(seconds: 60),
String? input,
void Function(ProcessOutputType type, String chunk)? onOutput,
}) async {
final fake = _fake;
if (fake != null) {
return fake._answer(command);
}
if (command.isEmpty) {
throw ArgumentError.value(command, 'command', 'Empty command');
}
final process = await io.Process.start(
command.first,
command.sublist(1),
workingDirectory: workingDirectory,
environment: environment,
);
final output = StringBuffer();
final errorOutput = StringBuffer();
Future<void> collect(
Stream<List<int>> stream,
StringBuffer buffer,
ProcessOutputType type,
) => stream.transform(utf8.decoder).forEach((chunk) {
buffer.write(chunk);
onOutput?.call(type, chunk);
});
final reading = Future.wait([
collect(process.stdout, output, ProcessOutputType.stdout),
collect(process.stderr, errorOutput, ProcessOutputType.stderr),
]);
// A command may exit without reading it all (`head -n 1`): a broken pipe
// is not a failure of this call.
process.stdin.done.ignore();
try {
if (input != null) {
process.stdin.write(input);
}
await process.stdin.close();
} on io.IOException {
// The exit code says how it went.
}
final int exitCode;
try {
exitCode = timeout == null
? await process.exitCode
: await process.exitCode.timeout(timeout);
} on TimeoutException {
process.kill(io.ProcessSignal.sigkill);
throw ProcessTimedOutException(command, timeout!);
}
await reading;
return ProcessResultData(
command: command,
exitCode: exitCode,
output: output.toString(),
errorOutput: errorOutput.toString(),
);
}