尝试在 C shell 中实现管道挂起并且未运行命令

2023-11-30

我正在尝试运行这个命令ps -j | more。我认为我已经正确设置了管道,但由于某种原因它只是挂起:

enter image description here

我正在调用一个正在运行的 forkps -j和第二个运行的叉子more并用管道将它们连接起来。

由于某种原因,这仍然没有按预期工作。

代码如下:

#define BUF_SIZE 100

int main() {

    pid_t pid1, pid2;
    int save_stdin, save_stdout;
    int fd[2];

    char *argv1[] = { "/bin/ps", "-j", NULL };
    char *argv2[] = { "/bin/more", NULL };

    char prompt[] = "Press enter: ";
    char buffer[BUF_SIZE];

    write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);

    ssize_t readIn = readIn = read(STDIN_FILENO, buffer, BUF_SIZE);
    buffer[readIn - 1] = '\0';

    printf("readIn: %d\n", readIn);

    pipe(fd);

    pid1 = fork();
    if (pid1 == 0) { // child
        close(fd[0]);
        save_stdout = dup(1);
        dup2(fd[1], STDOUT_FILENO);
        execvp(argv1[0], argv1);
        close(fd[1]);
    } else { // parent
        pid2 = fork();
        if(pid2 == 0) {
            save_stdin = dup(0);
            dup2(fd[0], STDIN_FILENO);
            execvp(argv2[0], argv2);
            close(fd[0]);
        } else {

        }
    }

    dup2(save_stdin, 0);
    dup2(save_stdout, 1);
    close(save_stdin);
    close(save_stdout);

    int i = 1;
    do {
        wait(NULL);
    } while (i-- > 0);


    exit(0);

}

非常感谢任何帮助!

EDIT:

我试过之后关闭管道dup2()但之前execvp(),但它仍然挂起:

pipe(fd);

pid1 = fork();
if (pid1 == 0) { // child
    dup2(fd[1], STDOUT_FILENO);
    close(fd[1]);
    close(fd[0]);
    int res1 = execvp(argv1[0], argv1);
    printf("exec1: %d\n", res1);
} else { // parent
    pid2 = fork();
    if(pid2 == 0) {
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        close(fd[1]);
        int res2 = execvp(argv2[0], argv2);
        printf("exec2: %d\n", res2);
    } else {

    }
}

close(fd[1]);
close(fd[0]);
printf("finished\n");

int i = 1;
do {
    wait(NULL);
} while (i-- > 0);

您没有关闭子级中管道的足够文件描述符,或者父级中的任何管道的文件描述符。

经验法则: 如果你dup2()将管道的一端连接到标准输入或标准输出,关闭两者 返回的原始文件描述符pipe()尽快地。 特别是,您应该在使用任何之前关闭它们exec*()函数族。

如果您使用以下任一方式重复描述符,则该规则也适用dup() or fcntl() with F_DUPFD

这是正确关闭管道的工作代码。我删除了命令的路径部分,因为(a)它们对于我的机器来说是错误的,并且(b)使用没有意义execvp()如果您指定命令的路径。我报告了大多数错误(但我不检查read() and write()来电。我还报告了孩子们的退出状况。如果孩子们未能执行命令,则报告并退出。您使用“save_stdin”等的代码进一步混淆了事情,因为变量在父级中未初始化,这是序列所在的位置:

dup2(save_stdin, 0);
dup2(save_stdout, 1);
close(save_stdin);
close(save_stdout);

被处决。你和我都不知道那两个是什么dup2()调用确实如此——但它们很可能失败了,或者它们对您的标准 I/O 通道做了一些奇怪的事情。

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

#define BUF_SIZE 100

int main(void)
{
    pid_t pid1, pid2;
    int fd[2];

    char *argv1[] = { "ps", "-j", NULL };   // Remove PATH because using execvp()
    char *argv2[] = { "more", NULL };       // Remove PATH because using execvp()

    char prompt[] = "Press enter: ";
    char buffer[BUF_SIZE];

    write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);

    ssize_t readIn = readIn = read(STDIN_FILENO, buffer, BUF_SIZE);
    buffer[readIn - 1] = '\0';

    printf("readIn: %zd\n", readIn);

    if (pipe(fd) < 0)
    {
        fprintf(stderr, "failed to create pipe\n");
        exit(EXIT_FAILURE);
    }

    if ((pid1 = fork()) < 0)
    {
        fprintf(stderr, "failed to fork - 1\n");
        exit(EXIT_FAILURE);
    }

    if (pid1 == 0)   // child
    {
        dup2(fd[1], STDOUT_FILENO);
        close(fd[0]);
        close(fd[1]);
        execvp(argv1[0], argv1);
        fprintf(stderr, "failed to execute %s\n", argv1[0]);
        exit(EXIT_FAILURE);
    }

    if ((pid2 = fork()) < 0)
    {
        fprintf(stderr, "failed to fork - 2\n");
        exit(EXIT_FAILURE);
    }

    if (pid2 == 0)
    {
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        close(fd[1]);
        execvp(argv2[0], argv2);
        fprintf(stderr, "failed to execute %s\n", argv2[0]);
        exit(EXIT_FAILURE);
    }

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

    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        fprintf(stderr, "%d: child %d exited with status 0x%.4X\n",
                (int)getpid(), corpse, (unsigned)status);

    return 0;
}

该计划是pipe41(对输出进行一些小的编辑,以删除那些实际上不需要让您了解我在其他窗口中所做的事情的内容):

$ ./pipe41
Press enter: 
readIn: 1
USER              PID  PPID  PGID   SESS JOBC STAT   TT       TIME COMMAND
jonathanleffler  6618  6617  6618      0    1 S    s000    0:00.11 -bash
jonathanleffler  6629  6628  6629      0    1 S+   s001    0:00.04 -bash
jonathanleffler  6660  6645  6660      0    1 S+   s002    0:00.04 -bash
jonathanleffler  6716  6695  6716      0    1 S+   s003    0:00.04 -bash
jonathanleffler  6776  6746  6776      0    1 S+   s004    0:00.04 -bash
jonathanleffler  6800  6771  6800      0    1 S+   s005    0:00.04 -bash
jonathanleffler  7487  7486  7487      0    1 S    s006    0:00.04 -bash
jonathanleffler  9558  9557  9558      0    1 S    s007    0:00.06 -bash
jonathanleffler 10375  9558 10375      0    1 S+   s007    0:00.01 ./pipe41
jonathanleffler 10377 10375 10375      0    1 S+   s007    0:00.00 more
(END)10375: child 10376 exited with status 0x0000
10375: child 10377 exited with status 0x0000
$
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

尝试在 C shell 中实现管道挂起并且未运行命令 的相关文章

  • copy_from_user() 错误:目标大小太小

    我正在为内核模块编写 ioctl 处理程序 我想从用户空间复制数据 当我编译禁用优化的代码时 O0 gflags 编译器返回以下错误 include linux thread info h 136 17 error call to bad
  • 来自 double 的 static_cast 可以优化分配给 double 吗?

    我偶然发现了一个我认为不必要的功能 并且通常让我感到害怕 float coerceToFloat double x volatile float y static cast
  • C++中类成员函数相互调用有什么好处?

    我是 C 新手 我发现下面的编程风格对我来说很有趣 我在这里写了一个简化版本 include
  • CMake(Ninja 后端)使用 /MT 编译

    我有一个类似的问题CMake 使用 MT 而不是 MD 进行编译 https stackoverflow com questions 14172856 cmake compile with mt instead of md但有一些差异 我正
  • 如何以编程方式删除受信任的根证书颁发机构中的证书?

    我需要能够从组织中的每台电脑中删除特定的证书 是的 我可以逐个座位 但我要到周四才能完成 而且我没有人力逐个座位 是否有使用 C 的编程方式来执行此操作 我认为你不需要编写任何 C 看看certmgr exe del http msdn m
  • Visual Studio 2013 调试器显示 std::string 的奇怪值

    我有一个大型的 cmake 生成的解决方案 其中包含许多项目 由于某种原因 我无法查看字符串的内容 因为根据调试器 Bx Buf含有一些垃圾 text c str 正确返回 Hello 该问题不仅仅发生在本地字符串上 返回的函数std st
  • 如何在 C# 中以编程方式将行添加到 DataGrid?

    正如标题所述 我正在尝试使用 C 以编程方式将行添加到 DataGrid 但我似乎无法使其工作 这是我到目前为止所拥有的 I have a DataGrid declared as dg in the XAML foreach string
  • Windows Phone 7 - ScrollViewer 值已更改

    我一直在寻找解决方案 但无法找到正确的解决方案 我的网格宽度为 960 并且有ScrollViewer在里面 现在我想知道滚动时滚动的值 水平偏移 我找到的所有解决方案都是针对 wpf silverlight 的 它对我不起作用 Edit
  • DateTime.ParseExact - 为什么 yy 变成 2015 而不是 1915

    为什么 NET 假定以下年份是 2015 年 而不是 1915 年 var d DateTime ParseExact 20 11 15 dd MM yy new CultureInfo en GB 我想 它会尝试接近 但其背后是否有合理的
  • 使用任一默认捕获模式时,这是通过复制捕获还是 (*this) 通过引用捕获?是一样的吗?

    当我看到以下工作时我有点困惑 struct A void g void f g 但后来我发现this https stackoverflow com a 16323119 5825294答案非常详细地解释了它是如何工作的 本质上 它归结为t
  • MPI - 发送和接收列

    我需要从一个进程发送矩阵列并从另一个进程接收它 我尝试运行以下程序 但得到了一个奇怪的结果 至少我这么认为 仅复制矩阵的第一个元素 某些矩阵元素会发生意外变化 include
  • C++网络序列化[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一种将 C 数据包序列化为网络流的解决方案 我在这里看到很多帖子提到人们 ACE 谷歌协议缓
  • 具有多个父项的 Qt 树模型

    我想构建一棵树 其中一个元素可以引用另一个元素 我想要构建的树是 像这样的东西 A B C D E F P this is a pointer to C D first child of C E second child of C I fo
  • 让 Windows 尝试读取文件

    我正在对 Windows 文件系统进行某种封装 当用户请求打开文件时 Windows 调用我的驱动程序来提供数据 在正常操作中 驱动程序返回缓存的文件内容 但是 在某些情况下 实际文件没有缓存 我需要从网络下载它 问题是是否有可能让 Win
  • 为什么我可以在另一个函数中定义一个函数?

    请参阅下面的代码 我在另一个函数中定义了一个函数 void test1 void void test2 void printf test2 n printf test1 n int main void test1 return 0 这个用法
  • 在多线程环境中捕获信号

    我有一个大型程序 需要尽可能具有弹性 并且有大量线程 我需要捕获所有信号SIGBUS SIGSEGV 并在必要时重新初始化有问题的线程 或者禁用该线程以继续减少功能 我的第一个想法是做一个setjump 然后设置信号处理程序 可以记录问题
  • 尝试后终于没有被调用

    由于某种原因 在我的控制台应用程序中 我无法运行我的finally 块 我编写这段代码是为了测试finally块是如何工作的 所以它非常简单 static void Main int i 0 try int j 1 i Generate a
  • 如何配置 qt Creator 以显示 C++ 代码而不是反汇编程序?

    昨天我做了很多事情 比如更新 GCC Clang 和重新安装 Qt Creator 今天 在逐步调试我的代码时 调试器显示的是反汇编代码 而不是我编写的 C 代码 紧迫F10 or F11 调试器正在进入汇编代码而不是 cpp nor h我
  • 使用通用存储库模式和流畅的 nHibernate

    我目前正在开发一个中型应用程序 它将访问不同站点上的 2 个或更多 SQL 数据库等 我正在考虑使用类似的东西 http mikehadlow blogspot com 2008 03 using irepository pattern w
  • java有类似C#的属性吗? [复制]

    这个问题在这里已经有答案了 C 属性 我的意思是 get 和 set 方法 是一个非常有用的功能 java 也有类似 C 的属性吗 我的意思是我们如何在 java 中实现类似以下 C 代码的内容 public string Name get

随机推荐