从管道读取输入时出现问题

时间:2019-01-24 16:55:01

标签: linux pipe execve

我编写了一个简单的C程序,以使用execve执行另一个程序。

exec.c:

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

int main(int argc, char** argv) {
        char path[128];
        scanf("%s", path);
        char* args[] = {path, NULL};
        char* env[] = {NULL};
        execve(path, args, env);
        printf("error\n");
        return 0;
}

我编译了它:

gcc exec.c -o exec

运行它并编写“ / bin / sh”后,它成功地运行了外壳并显示了$符号,就像正常的外壳一样,如图所示。

enter image description here

然后,我执行以下操作:我使用nc -l 12345创建了一个服务器,然后运行nc localhost 12345 | ./exec。它起作用了,但是由于某种原因我无法理解,这次没有显示$符号。我不知道原因。 (演示附加的图片)

enter image description here enter image description here

现在,这是最奇怪的事情。 当我尝试通过管道一次传递程序路径和更多输入时,似乎已执行的过程只是忽略输入并关闭。 例如:

enter image description here

但是,如果我运行以下命令,则其工作方式与通过管道nc输出时的工作方式完全相同: enter image description here

所以,总结一下我的问题:

  1. 我不明白为什么当从管道而不是标准输入中读取输入时,为什么执行的外壳不显示$提示符号。
  2. 当输入已经存在并且不等待时,为什么执行的程序不从管道读取输入?似乎仅在命令执行后管道保持打开的情况下才起作用。

1 个答案:

答案 0 :(得分:1)

就像已经提到过的AlexP一样,只有在终端输入时才显示提示符号。

第二个问题比较棘手:调用libc函数scanf时,其实现不仅会消耗管道中的/bin/sh,还会将下一个输入ls存储在其内部缓冲区中。这些内部缓冲区将被execve覆盖,因此shell无法获得任何内容。

这是您没有scanf的脚本来验证这一点:

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

int main(int argc, char** argv) {
        char path[128];

        read(0, path, 8); // consume `/bin/sh`
        path[7] = '\0';

        char* args[] = {path, NULL};
        char* env[] = {NULL};
        execve(path, args, env);
        printf("error\n");
        return 0;
}

为什么带有cat的示例首先起作用? 那(可能)是因为缓冲。试试:

(echo /bin/sh; echo ls) | stdbuf -i0 ./exec

我建议this nice Article about buffering供进一步阅读。