None protocol
export declare class AdbNoneProtocolSubprocessService {
spawn(
command: string | string[],
signal?: AbortSignal
): Promise<AdbNoneProtocolProcess>;
pty(command?: string | string[]): Promise<AdbNoneProtocolPtyProcess>;
}
An instance of AdbNoneProtocolSubprocessService is available at adb.subprocess.noneProtocol.
spawn
Start a process in raw mode.
import type { MaybePromiseLike } from "@yume-chan/async";
import type {
MaybeConsumable,
ReadableStream,
WritableStream,
} from "@yume-chan/stream-extra";
export interface AdbNoneProtocolProcess {
get stdin(): WritableStream<MaybeConsumable<Uint8Array>>;
get output(): ReadableStream<Uint8Array>;
get exited(): Promise<void>;
kill(): MaybePromiseLike<void>;
}
export declare class AdbNoneProtocolSubprocessService {
spawn(
command: string | string[],
signal?: AbortSignal
): Promise<AdbNoneProtocolProcess>;
}
command
The command parameter can either be a single string, or an array of strings
- JavaScript
- TypeScript
await adb.subprocess.noneProtocol.spawn("ls -al /");
// same as
await adb.subprocess.noneProtocol.spawn(["ls", "-al", "/"]);
import type { Adb } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
await adb.subprocess.noneProtocol.spawn("ls -al /");
// same as
await adb.subprocess.noneProtocol.spawn(["ls", "-al", "/"]);
Escaping arguments
Because the command is executed by a shell, and different shells have different escaping rules, we can't escape the command automatically.
If the command is an array, whitespace characters in an element will split the argument into two:
adb.subprocess.noneProtocol.spawn(["mv", "a b", "c d"]);
// is actually
adb.subprocess.noneProtocol.spawn(["mv", "a", "b", "c", "d"]);
A escapeArg method is provided which follows basic sh rules. It uses single quotes to wrap the argument, meaning that it will also prevent shell expansion:
import { escapeArg } from "@yume-chan/adb";
adb.subprocess.noneProtocol.spawn(["mv", escapeArg("a b"), escapeArg("c d")]);
// evaluates to
adb.subprocess.noneProtocol.spawn("mv 'a b' 'c d'");
signal
A optional AbortSignal to kill the process.
When the process is killed by signal:
stdinwill be closed, writing to it will throw an error.outputwill be closed after all buffered data is consumed.exitedwill be rejected withsignal.reason.
- JavaScript
- TypeScript
const abortController = new AbortController();
setTimeout(() => abortController.abort(), 1000);
const process = await adb.subprocess.noneProtocol.spawn("logcat", abortController.signal);
import type { Adb } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
const abortController = new AbortController();
setTimeout(() => abortController.abort(), 1000);
const process = await adb.subprocess.noneProtocol.spawn("logcat", abortController.signal);
stdin
Writes to the process's stdin.
- JavaScript
- TypeScript
import { encodeUtf8 } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
// `cat` will output whatever is written to its `stdin`
const process = await adb.subprocess.noneProtocol.spawn("cat");
const writer = process.stdin.getWriter();
await writer.write(encodeUtf8("Hello World!"));
for await (const chunk of process.output.pipeThrough(new TextDecoderStream())) {
console.log(chunk); // "Hello World!"
await process.kill();
}
import type { Adb } from "@yume-chan/adb";
import { encodeUtf8 } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
// `cat` will output whatever is written to its `stdin`
const process = await adb.subprocess.noneProtocol.spawn("cat");
const writer = process.stdin.getWriter();
await writer.write(encodeUtf8("Hello World!"));
for await (const chunk of process.output.pipeThrough(new TextDecoderStream())) {
console.log(chunk); // "Hello World!"
await process.kill();
}
Closing stdin
None protocol doesn't support closing stdin stream manually, so if the child process keeps reading from it, it will hang (like the cat example above).
If closing stdin feature is required, use Shell protocol instead.
If you know when the child process can be stopped, use signal or kill() to stop it manually.
output
Read from the process's stdout and stderr. The stream ends when the process exits.
- JavaScript
- TypeScript
import { TextDecoderStream } from "@yume-chan/stream-extra";
const process = await adb.subprocess.noneProtocol.spawn("ls -al /");
for await (const chunk of process.output.pipeThrough(new TextDecoderStream())) {
console.log(chunk);
}
import type { Adb } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
const process = await adb.subprocess.noneProtocol.spawn("ls -al /");
for await (const chunk of process.output.pipeThrough(new TextDecoderStream())) {
console.log(chunk);
}
ADB is a multiplexing protocol (multiple logic streams are transmitted over one connection), so blocking one stream will block all other streams.
You must continuously read from all incoming streams (either by piping them to WritableStreams, using for await loop, or calling reader.read() in a loop) to prevent this from happening.
If the remaining data is not needed, stream.cancel() (or reader.cancel() if using a reader) can be called to discard them.
adb exec-out ls -al /
Note: adb exec-out doesn't forward stdin to the process, so it's not 100% equal.
exited
A Promise that resolves when the process exits.
- If the process exits normally, was killed by a signal, or was killed by calling
kill(), the promise resolves withundefined. - If the
signaloption aborts the process, the promise rejects withsignal.reason.
- JavaScript
- TypeScript
const process = await adb.subprocess.noneProtocol.spawn("sleep 1");
await process.exited; // Resolves after 1 second
import type { Adb } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
const process = await adb.subprocess.noneProtocol.spawn("sleep 1");
await process.exited; // Resolves after 1 second
kill
Send a SIGHUP to the process, which usually causes it to terminate.
If there are any remaining data in output, they can still be read after the process has exited.
- JavaScript
- TypeScript
import { TextDecoderStream } from "@yume-chan/stream-extra";
const process = await adb.subprocess.noneProtocol.spawn("logcat");
setTimeout(() => process.kill(), 1000);
for await (const chunk of process.output.pipeThrough(new TextDecoderStream())) {
console.log(chunk);
}
import type { Adb } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
const process = await adb.subprocess.noneProtocol.spawn("logcat");
setTimeout(() => process.kill(), 1000);
for await (const chunk of process.output.pipeThrough(new TextDecoderStream())) {
console.log(chunk);
}
spawn.wait
The result of spawn is a mixin between Promise<AdbNoneProtocolProcess> and a Wait helper.
It provides a simple way to wait for the process to exit, and read its output.
export interface WaitOptions {
stdin?: ReadableStream<MaybeConsumable<Uint8Array>> | undefined;
}
export interface Wait {
wait(options?: WaitOptions): Promise<Uint8Array>;
}
Basic usage:
- JavaScript
- TypeScript
const output = await adb.subprocess.noneProtocol.spawn("ls -al /").wait();
import type { Adb } from "@yume-chan/adb";
declare const adb: Adb;
const output: Uint8Array = await adb.subprocess.noneProtocol.spawn("ls -al /").wait();
Optionally, you can provide a stdin stream to write to the process's stdin.
- JavaScript
- TypeScript
import { ReadableStream } from "@yume-chan/stream-extra";
const output = await adb.subprocess.noneProtocol.spawn("cat").wait({
stdin: new ReadableStream({
start(controller) {
controller.enqueue(encodeUtf8("Hello World!"));
},
}),
});
import type { Adb, encodeUtf8 } from "@yume-chan/adb";
import { ReadableStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
const output: Uint8Array = await adb.subprocess.noneProtocol.spawn("cat").wait({
stdin: new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(encodeUtf8("Hello World!"));
},
}),
});
Note that cat doesn't work with wait, as it will wait for the stdin to be closed before exiting, but None protocol doesn't support closing stdin stream manually.
spawn.wait.toString
The result of spawn.wait is also a mixin between Promise<Uint8Array> and a WaitToString helper.
It does the same thing as wait, but decodes the output to a UTF-8 string.
- JavaScript
- TypeScript
const output = await adb.subprocess.noneProtocol.spawn("ls -al /").wait().toString();
import type { Adb } from "@yume-chan/adb";
declare const adb: Adb;
const output: string = await adb.subprocess.noneProtocol.spawn("ls -al /").wait().toString();
pty
Start a process in PTY mode.
export declare class AdbNoneProtocolPtyProcess {
get input(): WritableStream<MaybeConsumable<Uint8Array>>;
get output(): ReadableStream<Uint8Array>;
get exited(): Promise<undefined>;
sigint(): Promise<void>;
kill(): MaybePromiseLike<void>;
}
export declare class AdbNoneProtocolSubprocessService {
pty(command?: string | string[]): Promise<AdbNoneProtocolPtyProcess>;
}
input, output, exited and kill is basically same as raw mode's stdin, output, exited and kill.
command
If command parameter is undefined, the default shell will be started.
sigint
A shortcut method to write 0x03 to the PTY input, to send a SIGINT to the foreground process.