使用对 system() 的调用而不是您的编程语言的函数有缺点吗?
Are there downsides to using calls to system() instead of your programming language's functions?
我正在用 C 编程为嵌入式设备创建一些 API。此嵌入式设备运行 Linux 的变体。我对 C 不是很熟悉 - 我更熟悉 shell scripting/bash.
考虑到这一点,当涉及到检查目录是否存在或获取磁盘使用情况等事情时,我发现调用 system
或 popen
并执行会更容易我的命令,然后解析输出。作为开发人员,它对我来说更快。
进行这些 system
和 popen
调用而不是找出如何在 C 中完成这些事情然后利用 C 的函数是否有缺点?
即使它将在 Linux 上运行,您也不能依赖于您打算使用的所有命令的可用性,因此缺点很明显:您可以不依赖于可用的给定工具。
还需要考虑的一件事是解析来自 c 的 shell 命令的输出非常困难。
最后,您不能仅仅因为不安全就让您的程序随机调用系统实用程序或 shell 脚本。
归根结底,在 Linux 系统上,几乎所有东西都在某个时刻调用 C 函数。
谈谈评论中的一些论点以及我自己的想法:
- 从 c 程序执行 shell 命令,尤其是在使用用户参数时,是一个潜在的漏洞。例如,如果您允许用户使用参数 "foo; rm -rf *" 调用您的程序;根据您调用 shell 的方式,如果您想创建用户提供的目录,您可以有效地调用 "mkdir foo; rm -rf *"。这可能是大事,也可能不是大事,具体取决于您对用户的信任程度等。
- 执行 shell 命令会导致您无法避免的潜在竞争条件,使用链接在一起的直接系统调用可能更容易处理
- 解析命令的输出意味着在C中处理更多的字符串操作,不够有趣。
- 如果您真的更喜欢 bash,您最好的选择可能是实现一些小程序来包装目标库的 C API 的离散部分。无论如何,这是 UNIX 方式 (tm)。
编辑以解决 Pimgd 的评论:请注意,竞争条件令人头疼,其中很多不一定是您的特定用例的问题,但至少在权衡 pro 时值得考虑's/con的。无论如何,我正在考虑的特定竞争条件实例包括(可能还有其他 类):
- 安全类型竞争条件。如果您创建一个包含一组命令的临时文件然后执行它,则有可能在创建和执行之间有人进入并修改该文件。 (实际上这个主题有多种变体,例如 setting/resetting 符号链接等)。根据您的上述评论,这几乎超出了您的项目范围,但对于其他应用程序来说是需要注意的事情。
多进程竞争条件。我能想到的最简单的例子是如果你的程序的两个实例想要设置一个配置文件并且同时 运行ning 会发生什么。某些 OS 调用具有一定程度的原子性保证,但如果调用一系列 shell 命令来写入文件,则会失去很多原子性。 (请注意,即使您是从一个单一的 C 应用程序而不是一系列 shell 命令执行此操作,您仍然需要做一些额外的事情来防止实例 1 覆盖实例 2 的更改,但您至少不太可能到 运行 到你最终混合更改的情况。例如考虑:
FILE *fp = fopen("config.txt","wt");
fprintf(fp,"Config value 1: %s",config[0]);
fprintf(fp,"Config value 2: %s",config[1]);
fflush(fp);
fclose(fp);
对比
system("echo Config value 1: `df | awk '{print }'` > config.txt");
system("echo Config value 2: `ps | grep foo | awk '{print }'` >> config.txt");
其中,如果程序的两个实例 运行 接近准确时间,您可以得到例如在后一种情况下,配置文件中配置值 2 的 2 个实例。
缺点:
- 如果有用户输入是不安全的
- 很难解析输出
- 它将依赖于外部的东西,例如 shell 脚本。
- 繁重的进程 - 系统 fork() 并并行启动其他程序
优点:
- 易于实施
- 依赖于程序输出解析可能非常容易。示例
system("ls -1");
总结-
这取决于你需要做什么,但一般情况下负面多于正面。
对于更熟悉 shell 脚本的您来说,这样做可能是一种有效的方法:
使用 shell 脚本作为执行所有高级计算的基础。此脚本可以使用一个(或多个)命令来解决 API;只提供这些命令,编写小C程序。
例如:
#!/bin/bash
valueA=$(APIhelper -q valueA)
valueB=$(APIhelper -q valueB)
process() { … }
result=$(process "$valueA" "$valueB")
APIhelper -s result "$result"
这部分如果懂shell脚本应该就清楚了。
现在你只需要编写二进制文件APIhelper
(请根据上下文选择更合适的名称;-)。
可以是这样的C程序:
int main(int argc, char *argv[]) {
if (argc > 1) {
if (strstr(argv[1], "-q") == 0) {
if (argc > 2) {
char *variableName = argv[2];
someType result = makeYourApiCall(variableName);
printf("%O\n", result); # use a fitting format instead of %O!
} else {
fprintf(stderr, "-q without variable name\n");
exit(1);
}
else if (strstr(argv[1], "-s") == 0) {
…
} else {
fprintf(stderr, "option not understood: %s\n", argv[1]);
exit(1);
}
} else {
fprintf(stderr, "missing option\n");
exit(1);
}
return 0;
}
这样,只有最基本的 API 处理是在 C 程序中完成的,您可以坚持使用您最擅长的代码:脚本。此外,通过这种方式,您可以轻松调试 API,因为可以从命令行交互式调用此工具 APIhelper
。
我正在用 C 编程为嵌入式设备创建一些 API。此嵌入式设备运行 Linux 的变体。我对 C 不是很熟悉 - 我更熟悉 shell scripting/bash.
考虑到这一点,当涉及到检查目录是否存在或获取磁盘使用情况等事情时,我发现调用 system
或 popen
并执行会更容易我的命令,然后解析输出。作为开发人员,它对我来说更快。
进行这些 system
和 popen
调用而不是找出如何在 C 中完成这些事情然后利用 C 的函数是否有缺点?
即使它将在 Linux 上运行,您也不能依赖于您打算使用的所有命令的可用性,因此缺点很明显:您可以不依赖于可用的给定工具。
还需要考虑的一件事是解析来自 c 的 shell 命令的输出非常困难。
最后,您不能仅仅因为不安全就让您的程序随机调用系统实用程序或 shell 脚本。
归根结底,在 Linux 系统上,几乎所有东西都在某个时刻调用 C 函数。
谈谈评论中的一些论点以及我自己的想法:
- 从 c 程序执行 shell 命令,尤其是在使用用户参数时,是一个潜在的漏洞。例如,如果您允许用户使用参数 "foo; rm -rf *" 调用您的程序;根据您调用 shell 的方式,如果您想创建用户提供的目录,您可以有效地调用 "mkdir foo; rm -rf *"。这可能是大事,也可能不是大事,具体取决于您对用户的信任程度等。
- 执行 shell 命令会导致您无法避免的潜在竞争条件,使用链接在一起的直接系统调用可能更容易处理
- 解析命令的输出意味着在C中处理更多的字符串操作,不够有趣。
- 如果您真的更喜欢 bash,您最好的选择可能是实现一些小程序来包装目标库的 C API 的离散部分。无论如何,这是 UNIX 方式 (tm)。
编辑以解决 Pimgd 的评论:请注意,竞争条件令人头疼,其中很多不一定是您的特定用例的问题,但至少在权衡 pro 时值得考虑's/con的。无论如何,我正在考虑的特定竞争条件实例包括(可能还有其他 类):
- 安全类型竞争条件。如果您创建一个包含一组命令的临时文件然后执行它,则有可能在创建和执行之间有人进入并修改该文件。 (实际上这个主题有多种变体,例如 setting/resetting 符号链接等)。根据您的上述评论,这几乎超出了您的项目范围,但对于其他应用程序来说是需要注意的事情。
多进程竞争条件。我能想到的最简单的例子是如果你的程序的两个实例想要设置一个配置文件并且同时 运行ning 会发生什么。某些 OS 调用具有一定程度的原子性保证,但如果调用一系列 shell 命令来写入文件,则会失去很多原子性。 (请注意,即使您是从一个单一的 C 应用程序而不是一系列 shell 命令执行此操作,您仍然需要做一些额外的事情来防止实例 1 覆盖实例 2 的更改,但您至少不太可能到 运行 到你最终混合更改的情况。例如考虑:
FILE *fp = fopen("config.txt","wt");
fprintf(fp,"Config value 1: %s",config[0]);
fprintf(fp,"Config value 2: %s",config[1]);
fflush(fp);
fclose(fp);
对比
system("echo Config value 1: `df | awk '{print }'` > config.txt");
system("echo Config value 2: `ps | grep foo | awk '{print }'` >> config.txt");
其中,如果程序的两个实例 运行 接近准确时间,您可以得到例如在后一种情况下,配置文件中配置值 2 的 2 个实例。
缺点:
- 如果有用户输入是不安全的
- 很难解析输出
- 它将依赖于外部的东西,例如 shell 脚本。
- 繁重的进程 - 系统 fork() 并并行启动其他程序
优点:
- 易于实施
- 依赖于程序输出解析可能非常容易。示例
system("ls -1");
总结-
这取决于你需要做什么,但一般情况下负面多于正面。
对于更熟悉 shell 脚本的您来说,这样做可能是一种有效的方法:
使用 shell 脚本作为执行所有高级计算的基础。此脚本可以使用一个(或多个)命令来解决 API;只提供这些命令,编写小C程序。
例如:
#!/bin/bash
valueA=$(APIhelper -q valueA)
valueB=$(APIhelper -q valueB)
process() { … }
result=$(process "$valueA" "$valueB")
APIhelper -s result "$result"
这部分如果懂shell脚本应该就清楚了。
现在你只需要编写二进制文件APIhelper
(请根据上下文选择更合适的名称;-)。
可以是这样的C程序:
int main(int argc, char *argv[]) {
if (argc > 1) {
if (strstr(argv[1], "-q") == 0) {
if (argc > 2) {
char *variableName = argv[2];
someType result = makeYourApiCall(variableName);
printf("%O\n", result); # use a fitting format instead of %O!
} else {
fprintf(stderr, "-q without variable name\n");
exit(1);
}
else if (strstr(argv[1], "-s") == 0) {
…
} else {
fprintf(stderr, "option not understood: %s\n", argv[1]);
exit(1);
}
} else {
fprintf(stderr, "missing option\n");
exit(1);
}
return 0;
}
这样,只有最基本的 API 处理是在 C 程序中完成的,您可以坚持使用您最擅长的代码:脚本。此外,通过这种方式,您可以轻松调试 API,因为可以从命令行交互式调用此工具 APIhelper
。