🐜 subprocess.h
一个用于启动进程并与之交互的简单单头文件解决方案, 适用于 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_found 或 subprocess_error_permission_denied)。在 POSIX
平台上,检查 errno 以获取特定于平台的失败原因;在 Windows 上,
检查 GetLastError()。
向进程的标准输入写入
要向子进程的标准输入写入,您调用 subprocess_stdin
以获取用于写入的 FILE 句柄,传递一个之前创建的进程,
如下所示:
FILE* p_stdin = subprocess_stdin(&process);
fputs("Hello, world!", p_stdin);
必须注意,在对 subprocess_join
或 subprocess_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_destroy 和 subprocess_join,并且由
subprocess_join(&process, &process_return) 填充的返回码保证为 非零。
异步读取
如果你希望在调用 subprocess_join
之前能够从进程中读取数据,则不能使用 subprocess_stdout 或 subprocess_stderr,因为
该库支持的各种操作系统不允许这样做。
相反,你必须先调用 subprocess_create 并指定
subprocess_option_enable_async 选项 - 该选项启用异步读取。
然后,你必须使用 subprocess_read_stdout 和 subprocess_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_create 的 options 参数包含
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 平台上,若需要该行为时,才必须 设置。
常见问题
为什么当 environment 为 NULL 时,我的进程不会继承父进程的环境?
subprocess_create 与 Windows 的 CreateProcessA 存在细微差异,即
当 environment 设置为 NULL 时,它将以空环境启动进程。用户应使用 subprocess_option_inherit_environment
选项来继承父进程的环境。这样做是为了确保在启动进程时使用最安全的默认设置。
为什么我生成的子进程没有互联网进程?
如果你启动一个需要互联网访问的进程,则必须在创建时使用
subprocess_option_inherit_environment 选项。子进程
必须继承父进程的环境,因为环境隐式地
包含了父进程的权限(访问互联网),而子进程
需要这些权限。
待办
当前的待办事项列表:
- 添加 设置子进程环境变量 的能力,如 @graphitemaster 所建议。
- 添加指定当父进程 被终止时子进程是否应随之终止的能力。
AI 使用
允许在此仓库的提交中明确使用 AI 工具。存在一个标记为 pre-ai 的发布版本,该版本表示最后一次未使用 AI 工具的发布。
许可证
这是已发布到公共领域的自由且无限制的软件。
任何人都可以自由地复制、修改、发布、使用、编译、销售或 分发本软件,无论是以源代码形式还是编译后的 二进制形式,用于任何目的,商业或非商业,并可通过任何 手段。
在承认版权法的司法管辖区中,本软件的作者 将本软件的所有版权利益奉献给 公共领域。我们做出此奉献是为了 广大公众的利益,并损害我们的继承人和 继任者的利益。我们意图此奉献为一种明确的 永久放弃本软件在版权法下所有现有和未来权利的行为。
本软件按“原样”提供,不提供任何形式的 明示或暗示的保证,包括但不限于对 适销性、特定用途适用性和不侵权的保证。 在任何情况下,作者均不对任何索赔、损害或 其他责任负责,无论是基于合同、侵权或其他行为, 均源于、出于或与软件或软件的使用或 其他交易有关。
有关更多信息,请参阅 http://unlicense.org/