你应该close(fd[0]);
在dup2()
孩子之后。如果您提供绝对或相对路径,则使用;"/bin/sh"
是没有意义的。execlp()
它只会对裸文件名(程序名)进行基于路径的搜索。调用中的演员getline()
应该是不必要的;尽可能避免这样的演员阵容。您至少应该包括exit(1);
afterexeclp()
以防万一失败;诊断消息也是一个好主意。您应该close(fd[1]);
在父级循环之后向子级指示 EOF。(只是一次,如果你没有检测到错误返回也没关系malloc()
;将指针持有 NULL 的指针的地址传递给getline()
函数,然后它会尝试自己分配内存。当然,如果主程序分配内存失败,很有可能getline()
也会分配内存失败。)
这些变化导致:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
int fd[2];
pid_t child;
if (pipe(fd) != 0)
perror("pipe");
else if ((child = fork()) < 0)
perror("fork");
else if (child == 0)
{
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
execl("/bin/sh", "sh", NULL);
perror("oops");
exit(1);
}
else
{
close(fd[0]);
size_t nbytes = 100;
int bytes = 0;
char *line = (char*)malloc(nbytes+1);
while ((bytes = getline(&line, &nbytes, stdin)) != -1)
{
write(fd[1], line, bytes);
}
close(fd[1]);
}
return(0);
}
这在严格的编译标志下毫无怨言地编译:
gcc -O3 -g -std=c99 -Wall -Wextra xf.c -o xf
sh
当使用上面的代码(在没有选项的情况下调用)运行(在 Mac OS X 10.7.3 上)时-i
,事情的表现相当合理。您可以键入命令,shell 会执行它们。您可以键入“exit”,shell 退出,但是您编写的程序(我称之为xf
)在我键入新命令之前不会退出。然后它因为 SIGPIPE 信号而退出,因为它写入现在的无读取器管道。这个 shell 没有提示,因为它的标准输入不是终端(它是管道)。
当使用该-i
选项运行子 shell 时,作业控制 shell 之间似乎存在关于哪个 shell 负责终端的斗争。当我运行它时,我得到:
$ ps -f
UID PID PPID C STIME TTY TIME CMD
503 381 372 0 Wed08PM ttys001 0:00.07 -sh
503 21908 381 0 9:32PM ttys001 0:00.01 sh
$ ./xf
sh-3.2$
[1]+ Stopped(SIGTTIN) ./xf
$
$ ps -f
UID PID PPID C STIME TTY TIME CMD
503 381 372 0 Wed08PM ttys001 0:00.07 -sh
503 21908 381 0 9:32PM ttys001 0:00.01 sh
503 22000 21908 0 9:36PM ttys001 0:00.00 ./xf
503 22001 22000 0 9:36PM ttys001 0:00.00 sh -i
$ ls
awk.data osfile-keep.c pthread-2.c send.c xf
const-stuff.c perl.data pthread-3.c so.8854855.sql xf.c
fifocircle.c piped-merge-sort.c quine.c strandsort.c xf.dSYM
madump.c powa.c recv.c unwrap.c xxx.sql
makefile pthread-1.c regress.c vap.c yyy.sql
$ jobs
[1]+ Stopped(SIGTTIN) ./xf
$ fg %1
./xf
exit
$
(最初-sh
是我的终端窗口的登录 shell。在那里,我运行sh
以获得一个子 shell,并且我已经设置了提示符PS1='$ '
以使提示符与众不同。)
AFAICT,sh-3.2$
提示来自sh -i
外壳。父shell好像在读输入,把xf
程序dump到后台了,不太文明。ps -f
输出不显示命令,这ps
很麻烦。我确实设法让ls
命令在一次运行中显示在ps
列表中,它是原始 shell 的子代,而不是sh -i
run by xf
。当我xf
进入前台时,它立即退出(大概它从标准输入读取 0 字节,表示 EOF,因此getline()
返回 -1,一切都关闭了。exit
来自sh -i
; 它回应它。它从未得到任何输入因为sh
shell接受了命令而不是让xf
控制终端。这是非常混乱的。我不知道为什么会这样,但我觉得它不应该那样发生。