https://github.com/asmichi/childprocess
Asmichi.ChildProcess is a .NET library that provides functionality for creating child processes. An alternative to `System.Diagnostics.Process.Start`.
https://github.com/asmichi/childprocess
child-process dotnet dotnet-core linux process windows
Last synced: about 1 year ago
JSON representation
Asmichi.ChildProcess is a .NET library that provides functionality for creating child processes. An alternative to `System.Diagnostics.Process.Start`.
- Host: GitHub
- URL: https://github.com/asmichi/childprocess
- Owner: asmichi
- License: mit
- Created: 2019-05-01T05:05:36.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2024-10-29T15:52:29.000Z (over 1 year ago)
- Last Synced: 2025-04-07T18:13:16.435Z (about 1 year ago)
- Topics: child-process, dotnet, dotnet-core, linux, process, windows
- Language: C#
- Homepage:
- Size: 479 KB
- Stars: 35
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.ja.md
- License: LICENSE
Awesome Lists containing this project
README
[English](README.md)
# Asmichi.ChildProcess
子プロセスを生成する機能を提供します。子プロセスを生成して操作することに関しては、`System.Diagnostics.Process` よりも簡単に使えます。また、デフォルト値がより安全であり、柔軟なリダイレクト制御も可能です。
[NuGet](https://www.nuget.org/packages/Asmichi.ChildProcess/) で取得することができます。
[](https://dev.azure.com/asmichi/ChildProcess/_build/latest?definitionId=5&branchName=master)
[Wiki](https://github.com/asmichi/ChildProcess/wiki) に本ライブラリのゴールとロードマップがあります。
## `System.Diagnostics.Process` との比較
- 子プロセスを生成してその出力を得ることに注力している。
- プロセスの状態を取得することはできない。
- 常駐プロセスを生成することはできない。
- より多くのリダイレクト先をサポート:
- NUL
- ファイル (追記も可能)
- パイプ
- ハンドル
- リダイレクトのデフォルト値がより安全:
- stdin は NUL
- stdout は現在の stdout
- stderr は現在の stderr
- パイプは非同期である。すなわち、非同期の読み書きは IO 完了ポートが扱う。
- 子プロセスの終了を保証する。
# ライセンス
[The MIT License](LICENSE)
# サポートされるランタイム
- .NET Core 3.1 以降
RIDs:
- `win10-x86` (未テスト)
- `win10-x64` (1809 以降; 1809 でテスト済)
- `win10-arm` (未テスト)
- `win10-arm64` (未テスト)
- `linux-x64` (Ubuntu 18.04 でテスト済)
- `linux-arm` (未テスト)
- `linux-arm64` (未テスト)
- `linux-musl-arm64` (未テスト)
- `linux-musl-x64` (Alpine 3.20 でテスト済)
- `osx-x64` (macOS 10.15 Catalina 以降; 12 でテスト済)
- `osx-arm64` (macOS 11.0 Big Sur 以降; 未テスト)
NOTE: glibc ベースの Linux では、glibc 2.27 以降と libstdc++ 3.4.25 以降が必要です。
# Known Issues
- Windows 11 24H2 より前の Windows において、 `ChildProcess.Start` 実行は、 `conhost.exe` へのプロセスハンドルを 1 つリークします。(`ChildProcessFlags.AttachToCurrentConsole` により子プロセスを現在のコンソールにアタッチできた場合を除く。).
- [Kernel32 の不具合](https://github.com/microsoft/terminal/issues/17903) に起因します。
- Windows 10 1809 (Windows Server 2019 を含む) では、 `SignalTermination` は単にプロセスツリーを強制終了します (`Kill` と同じ操作です).
- [`ClosePseudoConsole`](https://docs.microsoft.com/en-us/windows/console/closepseudoconsole) が pseudoconsole にアタッチされたプログラムを終了しないバグがあるためです。
- 11.0 より前の macOS では、シグナルによって終了したプロセスの `ExitCode` は常に `-1` になります。
- そのようなプロセスについて `waitid` が `siginfo_t.si_status` に `0` を返すバグがあるためです。
# 注意
- `ChildProcessCreationContext` や `ChildProcessFlags. DisableEnvironmentVariableInheritance` を使用して環境変数を完全に上書きする場合、 `SystemRoot` などの基本的な環境変数を含めることを推奨します。
# 制限事項
- 最大で 2^63 個のプロセスしか生成できません。
# ランタイムに関する仮定
本ライブラリは、ランタイムに関して以下の性質を仮定しています:
- Windows
- `SafeFileHandle` の内部値はファイルハンドルである。
- `SafeWaitHandle` の内部値は `WaitForSingleObject` で待つことができる。
- `SafeProcessHandle` の内部値はプロセスハンドルである。
- *nix
- `SafeFileHandle` の内部値はファイルディスクリプタである。
- `SafeProcessHandle` の内部値はプロセス ID である。
- `Socket.Handle` はソケットのファイルディスクリプタを返す。
# サンプル
追加のサンプルは [ChildProcessExample.sln](ChildProcessExample.sln) を開くか、[ChildProcess.Example](src/ChildProcess.Example/) を参照してください。
## 基本
子プロセスの出力を読み取ることができます。その際、 stdout と stderr を結合することもできます。
```cs
var si = new ChildProcessStartInfo("cmd", "/C", "echo", "foo")
{
StdOutputRedirection = OutputRedirection.OutputPipe,
// 2>&1 のような効果
StdErrorRedirection = OutputRedirection.OutputPipe,
// DisableArgumentQuoting: See ChildProcessExamplesWindows.cs for details
Flags = ChildProcessFlags.DisableArgumentQuoting,
};
using (var p = ChildProcess.Start(si))
{
using (var sr = new StreamReader(p.StandardOutput))
{
// "foo"
Console.Write(await sr.ReadToEndAsync());
}
await p.WaitForExitAsync();
// ExitCode: 0
Console.WriteLine("ExitCode: {0}", p.ExitCode);
}
```
## ファイルへのリダイレクト
子プロセスの出力をファイルにリダイレクトできます。その際、子プロセスの出力を読む必要はありません。
```cs
var tempFile = Path.GetTempFileName();
var si = new ChildProcessStartInfo("cmd", "/C", "set")
{
ExtraEnvironmentVariables = new Dictionary { { "A", "A" } },
StdOutputRedirection = OutputRedirection.File,
StdErrorRedirection = OutputRedirection.File,
StdOutputFile = tempFile,
StdErrorFile = tempFile,
// DisableArgumentQuoting: See ChildProcessExamplesWindows.cs for details
Flags = ChildProcessFlags.UseCustomCodePage | ChildProcessFlags.DisableArgumentQuoting,
CodePage = Encoding.Default.CodePage, // UTF-8 on .NET Core
};
using (var p = ChildProcess.Start(si))
{
await p.WaitForExitAsync();
}
// A=A
// ALLUSERSPROFILE=C:\ProgramData
// ...
Console.WriteLine(File.ReadAllText("env.txt"));
File.Delete(tempFile);
```
## 真のパイプ
子プロセスの出力を、ほかの子プロセスの入力にパイプできます。その際、子プロセスの出力を読む必要はありません。
```cs
// 匿名パイプを作る
using var inPipe = new AnonymousPipeServerStream(PipeDirection.In);
var si1 = new ChildProcessStartInfo("cmd", "/C", "set")
{
// 出力をパイプの書き込み側に接続する
StdOutputRedirection = OutputRedirection.Handle,
StdErrorRedirection = OutputRedirection.Handle,
StdOutputHandle = inPipe.ClientSafePipeHandle,
StdErrorHandle = inPipe.ClientSafePipeHandle,
// DisableArgumentQuoting: See ChildProcessExamplesWindows.cs for details
Flags = ChildProcessFlags.UseCustomCodePage | ChildProcessFlags.DisableArgumentQuoting,
CodePage = Encoding.Default.CodePage, // UTF-8 on .NET Core
};
var si2 = new ChildProcessStartInfo("findstr", "Windows")
{
// 入力をパイプの読み取り側に接続する
StdInputRedirection = InputRedirection.Handle,
StdInputHandle = inPipe.SafePipeHandle,
StdOutputRedirection = OutputRedirection.OutputPipe,
StdErrorRedirection = OutputRedirection.OutputPipe,
Flags = ChildProcessFlags.UseCustomCodePage,
CodePage = Encoding.Default.CodePage, // UTF-8 on .NET Core
};
using var p1 = ChildProcess.Start(si1);
using var p2 = ChildProcess.Start(si2);
// パイプハンドルのコピーを閉じる。 (そうしないと、 p2 はパイプからの読み取りでブロックし続ける。)
inPipe.DisposeLocalCopyOfClientHandle();
inPipe.Close();
using (var sr = new StreamReader(p2.StandardOutput))
{
// ...
// OS=Windows_NT
// ...
Console.Write(await sr.ReadToEndAsync());
}
await p1.WaitForExitAsync();
await p2.WaitForExitAsync();
```