execve(“/ bin / sh”,0,0);在管道里

时间:2011-12-14 17:36:25

标签: c shell pipe freebsd execve

我有以下示例程序:

#include <stdio.h>

int
main(int argc, char ** argv){
    char buf[100];

    printf("Please enter your name: ");
    fflush(stdout);
    gets(buf);
    printf("Hello \"%s\"\n", buf);

    execve("/bin/sh", 0, 0);
}

我和当我在没有任何管道的情况下运行时它按预期工作并返回sh promt:

bash$ ./a.out
Please enter your name: warning: this program uses gets() which is unsafe.
testName
Hello "testName"
$ exit
bash$

但这不适用于管道,我想我知道为什么会这样,但我无法找到解决方案。示例运行如下。

bash$ echo -e "testName\npwd" | ./a.out
Please enter your name: warning: this program uses gets() which is unsafe.
Hello "testName"
bash$

我认为这与gets清空stdin这样的事实有关,即/bin/sh收到EOF并且在没有错误消息的情况下退出。

但是如何解决这个问题(如果可能的话,不修改程序,如果没有,则不删除gets),这样即使我通过管道提供输入,我也会得到一个承诺?

P.S。我在FreeBSD(4.8)机器D.S。

上运行它

3 个答案:

答案 0 :(得分:10)

您可以在不进行任何修改的情况下运行程序:

(echo -e 'testName\n'; cat ) | ./a.out

这样可以确保程序的标准输入不会在echo输出之后结束。相反,cat继续为您的程序提供输入。后续输入的来源是您的终端,因为这是cat读取的地方。

这是一个示例会话:

bash-3.2$ cc stdin_shell.c 
bash-3.2$ (echo -e 'testName\n'; cat ) | ./a.out 
Please enter your name: warning: this program uses gets(), which is unsafe.
Hello "testName"
pwd
/home/user/stackoverflow/stdin_shell_question
ls -l
total 32
-rwxr-xr-x  1 user  group  9024 Dec 14 18:53 a.out
-rw-r--r--  1 user  group   216 Dec 14 18:52 stdin_shell.c
ps -p $$
  PID TTY           TIME CMD
93759 ttys000    0:00.01 (sh)
exit

bash-3.2$

请注意,由于shell的标准输入未连接到终端,因此sh认为它不是以交互方式执行的,因此不会显示提示。但是,您可以正常输入命令。

答案 1 :(得分:3)

使用execve("/bin/sh", 0, 0);是对贝壳的残酷和不寻常的惩罚。它根本没有任何参数或环境 - 甚至没有它自己的程序名,甚至也没有像PATH或HOME这样的强制环境变量。

答案 2 :(得分:1)

不是100%肯定这个(使用的精确shell和OS可能会稍微抛出这些答案;我相信FreeBSD默认使用GNU bash作为/bin/sh?),但是

  • sh可能检测到其输入不是tty。

  • 您的sh版本可能会进入非交互模式,如果调用sh,则期望login-添加到argv[0]上为了它。设置execve ("/bin/sh", { "-sh", NULL}, NULL)可能会说服它作为登录shell运行。