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

我有以下示例程序:

#include  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$ 

我认为这与以下事实有关:以/bin/sh接收EOF并且在没有错误消息的情况下退出的方式获取空闲stdin。

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

PS我在FreeBSD(4.8)机器DS上运行它

你可以运行你的程序而不需要像这样的任何修改:

 (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认为它不是以交互方式执行的,因此不会显示提示。 但是,您可以正常键入命令。

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

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

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

要么

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