在 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 程序。使用 execfork 执行管道而不是调用 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) 确实使某些类型的错误处理变得更加困难——您无法完全了解启动子进程后发生的情况。

它有自己的优点和缺点。实现很可能在里面调用了forkexec,所以为了使用fork/exec而放弃它是没有意义的。事实上,如果你只想运行一个命令,而不关心它的input/output、退出状态等,调用system会很方便。

另一方面,如果你想处理 input/output 重定向,或者有额外的 fds 命令,或者什么来获得准确的退出状态,或者向子进程发送信号等等,你需要自己做 fork/exec。

请注意 system 的 return 值和它用于 运行 命令的命令处理器都是实现定义的,因此如果您正在使用便携式 shell, 你可能想避免调用它。