在 shell 程序中调用 system() 是不好的做法吗?
Is it bad practice to call system() in a shell program?
我正在编写自定义 shell command language interpreter and I see that other shells use calls to system() 来执行管道或高级 shell 程序。使用 exec
和 fork
执行管道而不是调用 system()
不是更好吗?例如,如果是救援 shell 或其他一些情况,那么您可能无法访问资源 system()
。
/* With the standard output plumbing sorted, execute Nth command */
static void exec_nth_command(int ncmds, char ***cmds) {
assert(ncmds >= 1);
if (ncmds > 1) {
pid_t pid;
Pipe input;
if (pipe(input) != 0)
err_sysexit("Failed to create pipe");
if ((pid = fork()) < 0)
err_sysexit("Failed to fork");
if (pid == 0) {
/* Child */
exec_pipe_command(ncmds - 1, cmds, input);
}
/* Fix standard input to read end of pipe */
dup2(input[0], 0);
close(input[0]);
close(input[1]);
}
execvp(cmds[ncmds - 1][0], cmds[ncmds - 1]);
err_sysexit("Failed to exec %s", cmds[ncmds - 1][0]);
/*NOTREACHED*/
}
/* Given pipe, plumb it to standard output, then execute Nth command */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output) {
assert(ncmds >= 1);
/* Fix stdout to write end of pipe */
dup2(output[1], 1);
close(output[0]);
close(output[1]);
exec_nth_command(ncmds, cmds);
}
/* Execute the N commands in the pipeline */
void exec_pipeline(int ncmds, char ***cmds) {
assert(ncmds >= 1);
pid_t pid;
if ((pid = fork()) < 0)
err_syswarn("Failed to fork");
if (pid != 0)
return;
exec_nth_command(ncmds, cmds);
}
正如您从上面的代码中看到的,我从不在我的 shell 中调用 system()
但这是有充分理由的吗?
由于您正在实施新的 shell,因此在内部使用 system(3)
会显得很奇怪,因为 system(3)
使用系统默认值 shell,这几乎肯定是不同的shell 来自您正在实施的那个。
此外,system(3)
确实使某些类型的错误处理变得更加困难——您无法完全了解启动子进程后发生的情况。
它有自己的优点和缺点。实现很可能在里面调用了fork
和exec
,所以为了使用fork/exec而放弃它是没有意义的。事实上,如果你只想运行一个命令,而不关心它的input/output、退出状态等,调用system
会很方便。
另一方面,如果你想处理 input/output 重定向,或者有额外的 fds 命令,或者什么来获得准确的退出状态,或者向子进程发送信号等等,你需要自己做 fork/exec。
请注意 system
的 return 值和它用于 运行 命令的命令处理器都是实现定义的,因此如果您正在使用便携式 shell, 你可能想避免调用它。
我正在编写自定义 shell command language interpreter and I see that other shells use calls to system() 来执行管道或高级 shell 程序。使用 exec
和 fork
执行管道而不是调用 system()
不是更好吗?例如,如果是救援 shell 或其他一些情况,那么您可能无法访问资源 system()
。
/* With the standard output plumbing sorted, execute Nth command */
static void exec_nth_command(int ncmds, char ***cmds) {
assert(ncmds >= 1);
if (ncmds > 1) {
pid_t pid;
Pipe input;
if (pipe(input) != 0)
err_sysexit("Failed to create pipe");
if ((pid = fork()) < 0)
err_sysexit("Failed to fork");
if (pid == 0) {
/* Child */
exec_pipe_command(ncmds - 1, cmds, input);
}
/* Fix standard input to read end of pipe */
dup2(input[0], 0);
close(input[0]);
close(input[1]);
}
execvp(cmds[ncmds - 1][0], cmds[ncmds - 1]);
err_sysexit("Failed to exec %s", cmds[ncmds - 1][0]);
/*NOTREACHED*/
}
/* Given pipe, plumb it to standard output, then execute Nth command */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output) {
assert(ncmds >= 1);
/* Fix stdout to write end of pipe */
dup2(output[1], 1);
close(output[0]);
close(output[1]);
exec_nth_command(ncmds, cmds);
}
/* Execute the N commands in the pipeline */
void exec_pipeline(int ncmds, char ***cmds) {
assert(ncmds >= 1);
pid_t pid;
if ((pid = fork()) < 0)
err_syswarn("Failed to fork");
if (pid != 0)
return;
exec_nth_command(ncmds, cmds);
}
正如您从上面的代码中看到的,我从不在我的 shell 中调用 system()
但这是有充分理由的吗?
由于您正在实施新的 shell,因此在内部使用 system(3)
会显得很奇怪,因为 system(3)
使用系统默认值 shell,这几乎肯定是不同的shell 来自您正在实施的那个。
此外,system(3)
确实使某些类型的错误处理变得更加困难——您无法完全了解启动子进程后发生的情况。
它有自己的优点和缺点。实现很可能在里面调用了fork
和exec
,所以为了使用fork/exec而放弃它是没有意义的。事实上,如果你只想运行一个命令,而不关心它的input/output、退出状态等,调用system
会很方便。
另一方面,如果你想处理 input/output 重定向,或者有额外的 fds 命令,或者什么来获得准确的退出状态,或者向子进程发送信号等等,你需要自己做 fork/exec。
请注意 system
的 return 值和它用于 运行 命令的命令处理器都是实现定义的,因此如果您正在使用便携式 shell, 你可能想避免调用它。