Skip to content

Commit

Permalink
feat: support subshells
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Jan 28, 2024
1 parent 7640372 commit 310c190
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 6 deletions.
41 changes: 41 additions & 0 deletions mod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,47 @@ Deno.test("piping in command", async () => {
}
});

Deno.test("subshells", async () => {
{
const result = await $`(echo 1 && echo 2) | cat -`.text();
assertEquals(result, "1\n2");
}
{
const result = await $`(echo 1 && echo 2) && echo 3`.text();
assertEquals(result, "1\n2\n3");
}
{
const result = await $`(echo 1 && echo 2) || echo 3`.text();
assertEquals(result, "1\n2");
}
{
const result = await $`echo 1 && (echo 2 || echo 3)`.text();
assertEquals(result, "1\n2");
}
{
const result = await $`echo 1 && (echo 2 && echo 3) || echo 4`.text();
assertEquals(result, "1\n2\n3");
}
{
const result = await $`echo 1 && (echo 2 || echo 3) && echo 4`.text();
assertEquals(result, "1\n2\n4");
}
// exiting shouldn't exit the parent
{
const result = await $`echo 1 && (echo 2 && exit 0 && echo 3) && echo 4`.noThrow().text();
assertEquals(result, "1\n2\n4");
}
{
const result = await $`echo 1 && (echo 2 && exit 1 && echo 3) || echo 4`.noThrow().text();
assertEquals(result, "1\n2\n4");
}
// shouldn't change the environment either
{
const result = await $`echo 1 && (echo 2 && export VAR=5 && echo $VAR) && echo $VAR`.text();
assertEquals(result, "1\n2\n5\n");
}
});

Deno.test("output redirects", async () => {
await withTempDir(async (tempDir) => {
// absolute
Expand Down
21 changes: 15 additions & 6 deletions src/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export interface Command {
redirect: Redirect | undefined;
}

export type CommandInner = SimpleCommand | TaggedSequentialList;
export type CommandInner = SimpleCommand | Subshell;

export interface SimpleCommand {
kind: "simple";
Expand Down Expand Up @@ -81,8 +81,8 @@ export interface Quoted {
value: WordPart[];
}

export interface TaggedSequentialList extends SequentialList {
kind: "sequentialList";
export interface Subshell extends SequentialList {
kind: "subshell";
}

export interface PipeSequence {
Expand Down Expand Up @@ -873,9 +873,12 @@ function executeCommandInner(command: CommandInner, context: Context): Promise<E
switch (command.kind) {
case "simple":
return executeSimpleCommand(command, context);
case "sequentialList":
default:
throw new Error(`Not implemented: ${command.kind}`);
case "subshell":
return executeSubshell(command, context);
default: {
const _assertNever: never = command;
throw new Error(`Not implemented: ${(command as CommandInner).kind}`);
}
}
}

Expand Down Expand Up @@ -1011,6 +1014,12 @@ async function executeCommandArgs(commandArgs: string[], context: Context): Prom
}
}

async function executeSubshell(subshell: Subshell, context: Context): Promise<ExecuteResult> {
const result = await executeSequentialList(subshell, context);
// sub shells do not change the environment or cause an exit
return { code: result.code, changes: [] };
}

async function pipeReaderToWritable(reader: Reader, writable: WritableStream<Uint8Array>, signal: AbortSignal) {
const abortedPromise = new Promise<void>((resolve) => {
signal.addEventListener("abort", listener);
Expand Down

0 comments on commit 310c190

Please sign in to comment.