1

我的程序如下:

//... init fd[2] as pipe ...
if (child==0){
        close(fd[1]);
        dup2(fd[0], 0);
        execlp("/bin/sh","sh",NULL);
} else {
        close(fd[0]);
        char *line; int nbytes=100; int bytes=0;
        line=(char*) malloc(nbytes+1);
        while ( (bytes = getline((char **)&line,&nbytes,stdin))!= -1 ){
            write(fd[1],line, bytes);
        }
}

这运行正常,但是当我尝试替换exec("/bin/sh","sh",NULL)exec("/bin/sh","sh","-i",NULL)强制交互式 shell 时,我的程序在执行第一个命令后停止。

我是管道新手,所以请帮助我理解原因并让交互式外壳工作......我也觉得我的代码读取行并传递给子管道有点奇怪..有没有更好的方法来实现同样的行为?

4

1 回答 1

1

你应该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 -irun by xf。当我xf进入前台时,它立即退出(大概它从标准输入读取 0 字节,表示 EOF,因此getline()返回 -1,一切都关闭了。exit来自sh -i; 它回应它。它从未得到任何输入因为shshell接受了命令而不是让xf控制终端。这是非常混乱的。我不知道为什么会这样,但我觉得它不应该那样发生。

于 2012-04-27T04:44:56.110 回答