【问题标题】:write Error: Broken Pipe in C using execlp使用 execlp 在 C 中写入错误:Broken Pipe
【发布时间】:2020-05-12 07:44:58
【问题描述】:

我在创建从命令行获取参数的简单 C 程序时遇到问题,最后一个参数是文件的路径。程序在给定文件上运行cat 命令,然后在 cat 的结果上运行 tr。 Tr 从命令行获取参数(最后一个参数除外)。我收到错误: 缺少操作数。 写入错误:断管。 我不确定错误在哪里......

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define WRITE_END 1
#define READ_END 0

int main(int argc, char* argv[]){
    if(argc < 2){
        printf("\nPROVIDE AN ARGUMENT\n");
        return 1;
    }
    const char * file = argv[argc - 1];
    char ** args = calloc(argc - 2, sizeof(char*));
    for( int i = 1; i<argc-2; i++){
        args[i - 1 ] = argv[i];
    }

    int fd[2];
    pipe(fd);
    pid_t child;

    if((child = fork()) == -1)return 2;

    if(child == 0){
        dup2(fd[WRITE_END], STDOUT_FILENO);
        close(fd[READ_END]);
        close(fd[WRITE_END]);
        execlp("cat", "cat", file, (char*)NULL);
        exit(1);
    }
    else{
        dup2(fd[READ_END], STDIN_FILENO);
        close(fd[WRITE_END]);
        close(fd[READ_END]);
        execlp("tr", "tr", *args, (char*)NULL);
        exit(1);
    }

    close(fd[0]);
    close(fd[1]);
    wait(0);
    wait(0);

    return 0;
}

【问题讨论】:

  • dup(fd[0]); 这并不像您认为的那样。您可能想研究man dup2
  • 你的args 变量搞砸了;您实际上是将char * 的数组复制到char 的数组中。当您真正想要的是 execvp 时,这似乎是使 execlp 工作的错误尝试。
  • 现在看来是这个if(child == 0){ dup2(fd[WRITE_END], STDOUT_FILENO); close(fd[READ_END]); close(fd[WRITE_END]); execlp("cat", "cat", file, (char*)NULL); exit(1); } else{ dup2(STDIN_FILENO, fd[READ_END]); close(fd[WRITE_END]); close(fd[READ_END]); execlp("tr", "tr", *args, (char*)NULL); exit(1); } 我认为管道仍然存在一些问题。这对我来说真的是一个模糊的话题:((
  • 在 cmets 中阅读代码真的很难。请编辑您的问题以包含新版本。
  • 你处理管道的方式没有问题。问题在于 args 的分配和复制以及 exec 函数的使用。请参阅下面的答案。

标签: c unix


【解决方案1】:

这里有一些问题使您无法使其正常工作。首先,正如 Nate Eldredge 在评论中提到的,分配和复制变量 args 的最后一个参数存在问题。其次,您对 execlp 的使用有一个小问题,因为参数应该包含一个与运行程序名称相对应的额外参数(与作为可执行文件打开的文件不同,很多人对此感到困惑)。第三,正如 Nate 所说,需要在父进程对应的 if-else 分支(“else”分支)中调用 execvp。它的第二个参数需要是一个指向字符的指针数组,最后一个是NULL

所以一次拿这些。首先,您需要为args 分配argc 插槽,以便以您想要的方式使用它:

char ** args = calloc(argc, sizeof(char*));
memcpy(args, argv, sizeof(char*)*(argc -1));

第一行分配了一个与 arg 大小相同的字符指针数组。列表。第二行将argv 中除最后一个指针之外的所有指针复制到args 中的相应位置,并将最后一个保留为NULLcalloc 将存储初始化为零,您需要最后一个指针在args 是一个空指针,如果你要将它传递给 execvp,你会这样做)。请注意,您并没有复制 argv 下的所有存储空间,只是复制第一维中的指针(请记住:argv[0] 是指针,argv[0][0] 是程序名称中的第一个字符)。

请注意,您可以使用 closedup。我不知道为什么有人反对这一点,除非他们忘记了分配文件描述符总是采用未使用的最低编号的描述符。这就是最初在 UNIX 中使用的描述符表最重要的一点。

接下来,使用“cat”覆盖fork 创建的子进程的execlp 调用缺少参数。应该是:

execlp("cat", "cat", file, (char*)NULL);

其中额外的"cat" 是cat 以argv[0] 形式输入main() 时将收到的值。您可能已经注意到,这看起来像是您可以在使用 exec__ 函数运行的程序的名称上撒谎,而且您可以(但您不能完全隐藏已完成)。

最后,第二个 execlp 调用。您不能像在命令行上输入一个大字符串一样传递参数:任何形式的 exec 都不使用 shell 来调用另一个程序,也不会为您解析命令行。此外,您尝试连接参数字符串的方式(显然,如果我已经正确阅读了您的意图)也不正确(请参阅上面关于args 分配和memcpy 调用的cmets)。您必须分解单个参数并将它们传递给它。因此,如果您有一个指向字符的指针数组,最后一个是NULL,就像在我为分配和复制数据指示的更改之后您将在args 中拥有的那样,那么您可以将args 传递给@987654345 @:

execvp("tr", args);

这些不是大错误,很多人在开始操作参数列表和使用 fork 和 exec 函数时都会犯这些错误。很多人在尝试在父进程和子进程之间使用管道时会犯错误,但您似乎已经正确理解了这部分。

最后一件事:exec__ 调用的下游执行行只有在执行用新程序实际替换正在运行的程序时出错时才会执行。例如,“cat”或“tr”的命令行错误不会导致 exec__ 失败。诸如缺少执行作为第一个参数给出的文件的权限或缺少文件 等错误将导致 exec__ 函数失败。除非 exec 返回错误,否则 exec 调用的下游不会在执行它的进程中执行(成功的 exec 永远不会返回)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-04
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-18
    相关资源
    最近更新 更多