ITADN
sheredom/subprocess.h
sheredom/subprocess.h · 文件 下载 ZIP
文件最后提交记录最后更新时间
README.md
以下内容由 AI 翻译,如有问题请点此提交 issue 反馈

🐜 subprocess.h

Actions Status Build Status Sponsor

一个用于启动进程并与之交互的简单单头文件解决方案, 适用于 C/C++。

用法

只需在代码中 #include "subprocess.h"

当前支持的平台为 Linux、macOS 和 Windows。

当前支持的编译器为 gcc、clang、MSVC 的 cl.exe 以及 clang-cl.exe。

设计

Subprocess 是一个单头文件跨平台库,允许用户启动 子进程,与进程的 stdin、stdout 和 stderr 进行交互,并 等待其完成。

启动进程

要启动一个进程,请像这样调用 subprocess_create

const char *command_line[] = {"echo", "Hello, world!", NULL};
struct subprocess_s subprocess;
int result = subprocess_create(command_line, subprocess_option_search_user_path,
                               &subprocess);
if (0 != result) {
  // an error occurred!
}

您为命令行指定一个字符串数组 - 使用 一个 NULL 元素终止该数组。示例使用 subprocess_option_search_user_path,因此 可以通过用户的 PATH 找到 echo 可执行文件;或者传递 可执行文件的绝对路径或相对路径。在 Windows 上,命令行字符串 被解释为 UTF-8 并传递给 Unicode 进程创建 API。

如果进程创建成功,则从 subprocess_create 返回 0。如果进程创建失败,则返回非零 subprocess_error_e 值(例如, subprocess_error_not_foundsubprocess_error_permission_denied)。在 POSIX 平台上,检查 errno 以获取特定于平台的失败原因;在 Windows 上, 检查 GetLastError()

向进程的标准输入写入

要向子进程的标准输入写入,您调用 subprocess_stdin 以获取用于写入的 FILE 句柄,传递一个之前创建的进程, 如下所示:

FILE* p_stdin = subprocess_stdin(&process);
fputs("Hello, world!", p_stdin);

必须注意,在对 subprocess_joinsubprocess_destroy 的任何调用之后,不要向 stdin 写入。

从进程的标准输出读取

要从子进程的标准输出读取,你调用 subprocess_stdout 以获取用于读取的 FILE 句柄,传入一个先前创建的进程,如下 所示:

FILE* p_stdout = subprocess_stdout(&process);
char hello_world[32];
fgets(hello_world, 32, p_stdout);

必须注意,在任何对 subprocess_destroy 的调用之后,都不要从 stdout 读取。

从进程的标准错误读取

要从子进程的标准错误读取,你调用 subprocess_stderr 以获取用于读取的 FILE 句柄,传入一个之前创建的进程,如下所示:

FILE* p_stderr = subprocess_stderr(&process);
char hello_world[32];
fgets(hello_world, 32, p_stderr);

必须注意,在任何对 subprocess_destroy 的调用之后,都不要从 stderr 读取。

等待进程

要等待之前创建的进程执行完毕,请像这样调用 subprocess_join

int process_return;
int result = subprocess_join(&process, &process_return);
if (0 != result) {
  // an error occurred!
}

子进程的返回码通过第二个参数返回(在上面的示例中存储到 process_return)。如果你不关心进程的返回码,此参数可以是 NULL

如果子进程遇到未处理的异常,返回码将始终填充一个_非零_值。

销毁进程

要销毁之前创建的进程,你像这样调用 subprocess_destroy

int result = subprocess_destroy(&process);
if (0 != result) {
  // an error occurred!
}

请注意,你可以在进程完成执行之前将其销毁——例如,这允许你生成一个比父进程执行时间更长的进程。

终止进程

要终止一个(可能已挂起的)先前创建的进程,你调用 subprocess_terminate 如下:

int result = subprocess_terminate(&process);
if (0 != result) {
  // an error occurred!
}

请注意,在调用 subprocess_terminate 之后,你仍然可以调用 subprocess_destroysubprocess_join,并且由 subprocess_join(&process, &process_return) 填充的返回码保证为 非零

异步读取

如果你希望在调用 subprocess_join 之前能够从进程中读取数据,则不能使用 subprocess_stdoutsubprocess_stderr,因为 该库支持的各种操作系统不允许这样做。

相反,你必须先调用 subprocess_create 并指定 subprocess_option_enable_async 选项 - 该选项启用异步读取。

然后,你必须使用 subprocess_read_stdoutsubprocess_read_stderr 辅助函数来从任一管道进行读取。请注意,如果没有数据准备好读取,这些调用 可能 会阻塞。

使用自定义进程环境

subprocess_create_ex 入口点包含一个额外的参数 environment。该参数是一个由 FOO=BAR 对组成的数组,并以 NULL 条目结尾:

const char *command_line[] = {"echo", "Hello, world!", NULL};
const char *environment[] = {"FOO=BAR", "HAZ=BAZ", NULL};
struct subprocess_s subprocess;
int result = subprocess_create_ex(command_line, subprocess_option_search_user_path,
                                  environment, NULL, &subprocess);
if (0 != result) {
  // an error occurred!
}

这允许你为生成的子进程指定自定义环境。第四个 参数允许你可选地指定子进程的当前工作目录; 传入 NULL 以继承父进程的当前工作目录。在 Windows 上, 自定义环境条目和当前工作目录字符串被 解释为 UTF-8。

但请注意,你不能在自定义环境中指定 subprocess_option_inherit_environment。 如果你想将某些自定义环境与父进程环境合并, 那么作为用户,你需要查询你想传递给子进程的原始 父进程变量,并在生成的 进程中指定它们 environment

生成无窗口的进程

如果 subprocess_createoptions 参数包含 subprocess_option_no_window,则如果平台支持,该进程 将以无可见窗口的方式启动。

const char *command_line[] = {"echo", "Hello, world!", NULL};
struct subprocess_s subprocess;
int result = subprocess_create(command_line,
                               subprocess_option_no_window |
                                   subprocess_option_search_user_path,
                               &subprocess);
if (0 != result) {
  // an error occurred!
}

此选项目前仅在 Windows 平台上,若需要该行为时,才必须 设置。

常见问题

为什么当 environmentNULL 时,我的进程不会继承父进程的环境?

subprocess_create 与 Windows 的 CreateProcessA 存在细微差异,即 当 environment 设置为 NULL 时,它将以空环境启动进程。用户应使用 subprocess_option_inherit_environment 选项来继承父进程的环境。这样做是为了确保在启动进程时使用最安全的默认设置。

为什么我生成的子进程没有互联网进程?

如果你启动一个需要互联网访问的进程,则必须在创建时使用 subprocess_option_inherit_environment 选项。子进程 必须继承父进程的环境,因为环境隐式地 包含了父进程的权限(访问互联网),而子进程 需要这些权限。

待办

当前的待办事项列表:

AI 使用

允许在此仓库的提交中明确使用 AI 工具。存在一个标记为 pre-ai 的发布版本,该版本表示最后一次未使用 AI 工具的发布。

许可证

这是已发布到公共领域的自由且无限制的软件。

任何人都可以自由地复制、修改、发布、使用、编译、销售或 分发本软件,无论是以源代码形式还是编译后的 二进制形式,用于任何目的,商业或非商业,并可通过任何 手段。

在承认版权法的司法管辖区中,本软件的作者 将本软件的所有版权利益奉献给 公共领域。我们做出此奉献是为了 广大公众的利益,并损害我们的继承人和 继任者的利益。我们意图此奉献为一种明确的 永久放弃本软件在版权法下所有现有和未来权利的行为。

本软件按“原样”提供,不提供任何形式的 明示或暗示的保证,包括但不限于对 适销性、特定用途适用性和不侵权的保证。 在任何情况下,作者均不对任何索赔、损害或 其他责任负责,无论是基于合同、侵权或其他行为, 均源于、出于或与软件或软件的使用或 其他交易有关。

有关更多信息,请参阅 http://unlicense.org/