【问题标题】:Send input to a program and get control back将输入发送到程序并重新获得控制权
【发布时间】:2010-07-20 09:10:56
【问题描述】:

我已经坚持了一段时间了。 假设我有一个如下所示的 C 程序。我希望能够向该程序发送一些字符串并在此之后获得控制权。 如果我这样做:


--> cat myfile |我的程序

--> echo "0123" |我的程序

--> 我的程序 我得到了输出(我的文件包含“0123”)
30 31 32 33

使用 -n 选项会引发段错误
--> echo -n mystring | 。/测试
zsh: 完成 echo -n "0123" |
zsh: 分段错误./test

我也尝试过使用命名管道,但也没有用。

我希望能够做类似的事情 猫我的文件 |我的程序 并取回控件,以便我可以输入其他字符。

  1 #include  <stdlib.h>
  2 #include  <stdio.h>
  3 
  4 int main (int argc, char *argv[]) {
  6   int i = 0, j;
  7   unsigned char buf[512];
  8   unsigned char x;
  9 
 10   while ((x = getchar()) != '\n') {
 11     buf[i] = x;
 12     i++;
 13   }
 14 
 16   for (j = 0; j < i; j++) {
 17     printf("%x ", buf[j]);
 18   }
 19   printf ( "\n" );
 20 
 21   return EXIT_SUCCESS;
 22 }  // end of function main

编辑:

下面是我想出的包装器。 它做了我想做的一切,除了子执行文件的输出没有正确显示。

没有包装器:

$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
2+2
4

使用包装器:

$ ./wrapper bc
2+2
enter
4

删除行

dup2(pipefd[0], 0);  // Set the read end of the pipe as stdin.

使子标准输出正确显示,但当然会破坏包装器。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <assert.h>

int main(int argc, char const *argv[]) {

  int cpid;
  int pipefd[2];


  if (pipe(pipefd) == -1) { perror("pipe.\n"); exit(errno); }


  cpid = fork();
  if (cpid == -1) { perror("fork."); exit(errno); }


  if (cpid) {
    // Parent --------------------------------------------------------

    int buf_size = 8192;
    char buf[buf_size];
    size_t file;

    // Close the unused read end of the pipe.
    close(pipefd[0]);

    // Leave a bit of time to the child to display its initial input.
    sleep(2);

    while (1) {
      gets(buf);

      if (strcmp("enter", buf) == 0) {
        write(pipefd[1], "\n", 1);

      } else if (-1 != (file = open(buf, O_RDONLY))) {
        // Dump the output of the file to the child's stdin.
        char c;
        while(read(file, &c, 1) != 0) {
          switch(c) {
            case '\n':
              printf("(skipped \\n)");
              break;
            default:
              printf("%c", c);
              write(pipefd[1], &c, 1);
          }; 
        }
        printf("\n");

      } else {
        // Dump input to the child's stdin, without trailing '\n'.
        for (int i = 0; (buf[i] != 0); i++) {
          write(pipefd[1], buf + i, 1);
        }
      }
    }

    // Wait for the child to exit.
    printf("Waiting for child to exit.\n");
    wait(NULL);

  } else {
    // Child ---------------------------------------------------------

    // Close the unused write end of the pipe.
    close(pipefd[1]);
    // Set the read end of the pipe as stdin.
    dup2(pipefd[0], 0);  // Set the read end of the pipe as stdin.

    char** program_arguments = (char**)(argv + 1);
    if (execvp(argv[1], program_arguments) < 0) {
      perror("execvp.\n");
      exit(errno);
    }
  }
}

【问题讨论】:

  • echo -n 方法的段错误是因为您的第 10 行查找“\n”并将继续读取 RAM 直到找到。
  • “在那之后获得控制权”是什么意思?你的意思是程序必须在没有等待它完成的情况下进行读取(以及计算和输出)? (这样您以后所做的任何事情都可能与 myprogram 的输出混合在一起)...在这种情况下,您可以分叉(而 segfault 是由程序中的错误给出的,如前所述)
  • 是的。但我不想要这种行为。当我说我想要控制权时,我的意思是我希望我的程序保持在 while(getchar() != '\n') 如果我给 "mystring\n" 作为输入它应该继续并终止,但是如果我只是给出“mystring”,程序仍应等待我的输入和一个 '\n' 字符。 (这就是我尝试使用 echo 的 -n 选项的原因。)
  • 这里的不同行为是由于 bc.您可以使用“-i”标志运行 bc 以强制交互模式。然后很可能你会看到相同的输出。

标签: c shell pipe cat


【解决方案1】:

如果你不能修改程序的行为,我认为使用命名管道是不可能实现的。因为本质上命名管道没有什么不同,所以从标准输入中给出带有重定向的输出。

我也认为如果你使用 shell 的管道或重定向属性是不可能的,因为在这种情况下总是会向你的程序发送一个 EOF,你不能忽略 EOF,因为你不能修改程序。

一种可能的解决方案是使用包装器。包装器将首先读取准备好的输入,将它们发送到您的程序,在准备好的输入完成后,包装器切换到标准输入。实际程序只是不断消耗输入,它不知道数据的实际来源。

唯一的缺点是,您不能通过管道或重定向提供准备好的输入,您必须提供文件名。 (我不确定命名管道是否有效。)原因很明显,如果您将准备好的输入从标准输入提供给包装器,那么包装器也存在同样的问题。通过这种方式,您只是将问题委托给包装器,您可以设计任何您想要的方式。

C 中可能的实现(从我使用过的类似包装器修改而来,未经广泛测试):

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>

int main(int argc, char * argv[]) {
    char c;
    char **pargs ;

    char buf[20];
    int n;

    int pipe_fd[2];
    int pid;

    pargs = argv+2;
    if (pipe(pipe_fd) < 0) {
            perror("pipe failed");
            exit(errno);
    }
    if ((pid=fork()) < 0) {
            perror ("Fork failed");
            exit(errno);
    }
    if (! pid) {
            close(pipe_fd[1]);
            dup2(pipe_fd[0],0);
            close(pipe_fd[0]);
            if (execvp(argv[2],pargs) < 0) {
                    perror("Exec failed");
                    exit(errno);
            }
    } else {
            size_t filedesc = open(argv[1],O_RDONLY);
            while((n = read(filedesc, buf, 100)) > 0)
                    write (pipe_fd[1], buf, n);
            while((n = read(0, buf, 100)) > 0)
                    write (pipe_fd[1], buf, n);
    }
}

你可以用这个包装器运行你的程序:

./wrapper input.txt myprog possible command line arguments

您可以将您的初始输入放入input.txt

一个更简单的解决方案是重新打开标准输入。但是,如果您只是尝试像打开文件一样打开它,它就不起作用。您应该打开终端流并将其复制到应用程序的标准输入。您可以使用以下方法(再次使用包装器):

size_t tty = open("/dev/tty",O_RDONLY);
dup2(tty,0);

更不用说第二个解决方案适用于 Linux 并且不可移植。

【讨论】:

  • 我终于回到了这个。包装器几乎可以实现我想要做的事情。查看编辑。
【解决方案2】:

在这个例子中我使用tail -f,而不是你的C程序

 mkfifo /tmp/pipe   # Open Pipe
 tail -f /tmp/pipe &    #  Start your program and put it into the background

现在您还可以向后台运行的程序发送数据

 echo "foobar" > /tmp/pipe

我希望这会有所帮助?

【讨论】:

  • 编辑:感谢您的回答!这适用于 tail 和 -f(跟随)选项,但如果您删除它,则不会。所以它也不适用于 myprogram。我怀疑 tail -f 在收到 EOF 或字符串结尾字符或类似内容后不会停止;但我的程序会。
  • 实际上,我认为您的程序不会在字符串末尾终止(除非通过 segfaulting),因为 getchar 仅针对 EOF 进行测试(甚至不保证会成功)并且程序仅测试反对换行。
  • 启动程序从命名管道获取输入并使用包装器来编写管道效果很好。唯一的问题是输入(在包装器中)和输出是分开的。最好能够将它们交错,就像我直接与程序交互一样。
【解决方案3】:

您可以修改您的程序以接受 1 个空字符,然后继续...它可能会起作用:

将第 10 行替换为类似

while (TRUE)
{
    x = getchar();
    if (x == '\n')
        break;
    if (x == '\0')
    {
        if (seen)
            break;
        seen = TRUE;
    }
...

【讨论】:

  • 如果您无法编辑正在使用的程序,那么您就是 SOL。我建议你使用tail。
猜你喜欢
  • 2021-04-01
  • 2020-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-02
  • 1970-01-01
  • 2019-01-06
  • 1970-01-01
相关资源
最近更新 更多