{"id":21297113,"url":"https://github.com/johnny-mh/rxjs-shell","last_synced_at":"2025-07-11T18:32:21.011Z","repository":{"id":33113819,"uuid":"148969176","full_name":"johnny-mh/rxjs-shell","owner":"johnny-mh","description":"rxjs operators for execute shell command with ease","archived":false,"fork":false,"pushed_at":"2023-07-19T06:38:31.000Z","size":1297,"stargazers_count":13,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-03T18:00:02.768Z","etag":null,"topics":["operator","rxjs","shell"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/johnny-mh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-09-16T06:18:07.000Z","updated_at":"2023-09-05T04:44:38.000Z","dependencies_parsed_at":"2023-07-25T00:46:00.975Z","dependency_job_id":null,"html_url":"https://github.com/johnny-mh/rxjs-shell","commit_stats":{"total_commits":104,"total_committers":4,"mean_commits":26.0,"dds":0.125,"last_synced_commit":"3a7030291ff10527c5d44824cb0751cd0c052802"},"previous_names":["johnny-mh/rxjs-shell-operators"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/johnny-mh/rxjs-shell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnny-mh%2Frxjs-shell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnny-mh%2Frxjs-shell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnny-mh%2Frxjs-shell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnny-mh%2Frxjs-shell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johnny-mh","download_url":"https://codeload.github.com/johnny-mh/rxjs-shell/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnny-mh%2Frxjs-shell/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264870539,"owners_count":23676260,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["operator","rxjs","shell"],"created_at":"2024-11-21T14:33:11.791Z","updated_at":"2025-07-11T18:32:20.522Z","avatar_url":"https://github.com/johnny-mh.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rxjs-shell\n\n[![PR Build](https://github.com/johnny-mh/rxjs-shell/actions/workflows/pull_request.yml/badge.svg)](https://github.com/johnny-mh/rxjs-shell/actions/workflows/pull_request.yml)\n\nrxjs operators for execute shell command with ease.\n\n## Features\n\n- Wrap nodejs asynchronous process creation methods to rxjs Observable.\n- Kill child process when unsubscribed.\n- Use subject to communicate with child process.\n\n## Functions\n\n### exec(command[, options][, proccallback]) → Observable\\\u003c{stdout: string | Buffer, stderr: string | Buffer}\\\u003e\n\n- `options` interface is same with nodejs `exec` method\n- `procCallback` you can pass function. \u003ccode\u003e[ChildProcess](https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html#class-childprocess)\u003c/code\u003e will be passed first argument.\n\n```typescript\nimport {exec} from 'rxjs-shell';\n\nexec('echo Hello World').subscribe(output =\u003e {\n  console.log(output.stdout.toString('utf8')); // Hello World\\n\n});\n\n\n// using `procCallback`\nexec('cat -', undefined, proc =\u003e {\n  proc.stdin?.write('Hello World');\n  proc.stdin?.end(); // it may cause endless process if you don't handle right.\n}).subscribe(output =\u003e { /* ... */ })\n```\n\n### execFile(file[, args][, options]) → Observable\\\u003c{stdout: string | Buffer, stderr: string | Buffer}\\\u003e\n\n- `options` interface is same with nodejs `execFile` method\n\n```typescript\nimport {existSync} from 'fs';\nimport {execFile} from 'rxjs-shell';\nexecFile('./touchFile.sh').subscribe(() =\u003e {\n  console.log(existSync('touched.txt')); // true\n});\n```\n\n### spawn(command[, args][, options][, procCallback]) → Observable\\\u003c{type: 'stdout' | 'stderr', chunk: Buffer}\\\u003e\n\n- `spawn` emits `stdout`, `stderr`'s buffer from command execution.\n- `options` interface is same with nodejs `spawn` method\n- `procCallback` you can pass function. `ChildProcessWithoutNullStreams` will be passed first argument.\n\n```typescript\nimport {spawn} from 'rxjs-shell';\n\nspawn('git clone http://github.com/johnny-mh/rxjs-shell-operators')\n  .pipe(tap(chunk =\u003e process.stdout.write(String(chunk.chunk))))\n  .subscribe();\n\n// using `procCallback`\nspawn('cat', ['-'], undefined, proc =\u003e {\n  proc.stdin.write('hello world');\n  proc.stdin.end(); // caution\n}).subscribe(output =\u003e { /* ... */ });\n```\n\n### fork(modulePath[, args][, options]) → Observable\\\u003cSerializable\\\u003e\n\n- same with `spawn` but have own `options` interface that extend nodejs's `fork` options to communicate with child process.\n\n```typescript\nimport {Subject} from 'rxjs';\nimport {fork} from 'rxjs-shell';\n\nconst send = new Subject\u003cstring\u003e();\n\nfork('echo.js', undefined, {send}).subscribe(msgFromChildProc =\u003e\n  console.log(msgFromChildProc)\n);\n\nsend.next('message to child process');\n```\n\n## Operators\n\n### trim(encoding = 'utf8')\n\n- trim child process output\n\n```typescript\nimport {exec, trim} from 'rxjs-shell';\n\nexec('echo Hello').subscribe(output =\u003e console.log(output.stdout.toString())); // Hello\\n\n\nexec('echo Hello')\n  .pipe(trim())\n  .subscribe(output =\u003e console.log(output.stdout.toString())); // Hello\n```\n\n### throwIf(pattern: string | RegExp)\n\n- manually throw error if contents of `stdout` or `stderr` is matching supplied pattern\n\n```typescript\nimport {throwIf} from 'rxjs-shell';\n\nexec('echo Hello').pipe(throwIf(/Hello/)).subscribe(); // ERROR\n```\n\n### throwIfStdout(pattern: string | RegExp)\n\n- manually throw error if contents of `stdout` is matching supplied pattern\n\n```typescript\nimport {throwIfStdout} from 'rxjs-shell';\n\nexec('echo Hello').pipe(throwIfStdout(/Hello/)).subscribe(); // ERROR\nexec('\u003e\u00262 echo Hello').pipe(throwIfStdout(/Hello/)).subscribe(); // OK\n```\n\n### throwIfStderr(pattern: string | RegExp)\n\n- manually throw error if contents of `stderr` is matching supplied pattern\n\n```typescript\nimport {throwIfStderr} from 'rxjs-shell';\n\nexec('echo Hello').pipe(throwIfStderr(/Hello/)).subscribe(); // OK\nexec('\u003e\u00262 echo Hello').pipe(throwIfStderr(/Hello/)).subscribe(); // ERR\n```\n\n### execWithStdin(command)\n\n- executes a command with a string event as stdin input\n\n```typescript\nof('Hello World')\n  .pipe(execWithStdin('cat -'))\n  .subscribe(output =\u003e {\n    expect(String(output.stdout).trim()).to.equal('Hello World');\n  });\n```\n\n## Utility Methods\n\n### spawnEnd(spawnObservable: Observable\u003cany\u003e) → Subject\\\u003c{stdout: Buffer, stderr: Buffer}\\\u003e\n\n- `spawn` emit each buffer from child process. if you want to connect other operator to this stream. use `spawnEnd` method.\n\n```typescript\nimport {spawn, spawnEnd} from 'rxjs-shell';\n\nspawn('webpack', ['-p'])\n  .pipe(outputChunk =\u003e {\n    /* each child process's output buffer */\n  })\n  .subscribe();\n\nspawnEnd(spawn('webpack', ['-p']))\n  .pipe(webpackOutput =\u003e {\n    /* do something */\n  })\n  .subscribe();\n```\n\n### listenTerminating(fn: () =\u003e any)\n\n- invoke callbacks when one of signals that below is emitted.\n  - `SIGINT`\n  - `SIGBREAK` (for windows)\n\nbasically each operators are listen that. if user pressed `^C` below stream is unsubscribe immediatly.\n\n```typescript\nexec('curl ...')\n  .pipe(concatMap(() =\u003e exec('curl ...')))\n  .subscribe();\n```\n\nbut if operators are not tied of one stream. whole process does not terminate. in this case. you can use `listenTerminating`.\n\n```typescript\nimport {exec, listenTerminating} from 'rxjs-shell';\n\n// terminate process\nlistenTerminating(code =\u003e process.exit(code));\nasync () =\u003e {\n  // user pressing ^C while curl is running\n  await exec('curl ...').toPromise();\n\n  // execute despite of pressing ^C. needs `listenTerminating`\n  await exec('curl -X POST ...').toPromise();\n};\n```\n\n## isSpawnChunk(obj: any): obj is SpawnChunk\n\n## isExecOutput(obj: any): obj is ExecOutput\n\n## Error Handling\n\n```typescript\nimport {ShellError, spawn} from 'rxjs-shell';\n\nspawn('git clone http://github.com/johnny-mh/rxjs-shell-operators')\n  .pipe(tap(chunk =\u003e process.stdout.write(String(chunk.chunk))))\n  .subscribe({\n    catch(err) {\n      if (!(err instanceof ShellError)) {\n        throw err;\n      }\n\n      console.log(err.originError);\n      console.log(err.stdout);\n      console.log(err.stderr);\n      console.log(err.toAnnotatedString()); // print annotated errors\n    },\n  });\n```\n\n## FAQ\n\n### Operator does not throw script error\n\nSome shell script doesn't completed with Non-Zero code. they just emitting error message to `stderr` or `stdout` 😢. If so. hard to throw `ShellError` because of `err` is `null`. You can use `throwIf`, `throwIfStdout`, `throwIfStderr` operator manually throwing specific scripts.\n\n```typescript\nexec('sh a.sh')\n  .pipe(concatMap(() =\u003e exec('sh b.sh').pipe(throwIf(/ERROR:/))))\n  .subscribe();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnny-mh%2Frxjs-shell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnny-mh%2Frxjs-shell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnny-mh%2Frxjs-shell/lists"}