Отправить вход в программу и вернуть контроль - PullRequest
0 голосов
/ 20 июля 2010

Я застрял на этом некоторое время.Допустим, у меня есть программа на Си, подобная следующей.Я хочу иметь возможность отправить этой программе некоторую строку и получить контроль после этого.Если я сделаю:

-> кот мой файл |MyProgramили же-> эхо "0123" |MyProgramили же-> моя программа <мой файлЯ получаю вывод (myfile содержит "0123")30 31 32 33 </p>

При использовании опции -n возникает ошибка-> echo -n mystring |./тестовое заданиеzsh: готово эхо -n "0123" |zsh: ошибка сегментации ./test

Я также пытался использовать именованный канал, но он тоже не работал.

Я хотел бы иметь возможность сделать что-то вроде cat myfile |myprogram и верните элемент управления, чтобы я мог печатать другие символы.

  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

EDIT:

Ниже приведена обертка, которую я придумал.Он делает все, что я хочу, за исключением того, что вывод дочернего exec-ed файла отображается неправильно.

Без оболочки:

$ 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);
    }
  }
}

Ответы [ 3 ]

0 голосов
/ 20 июля 2010

Вы можете изменить свою программу так, чтобы она принимала 1 нулевой символ, а затем продолжить ... это может работать:

заменить строку 10 чем-то вроде

while (TRUE)
{
    x = getchar();
    if (x == '\n')
        break;
    if (x == '\0')
    {
        if (seen)
            break;
        seen = TRUE;
    }
...
0 голосов
/ 22 января 2012

Я не думаю, что это возможно, используя именованные каналы, если вы не можете изменить поведение программы. Так как по сути именованные каналы ничем не отличаются, то выдача вывода из стандартного ввода с перенаправлением.

Я также не думаю, что это возможно, если вы используете конвейер или свойства перенаправления оболочки, поскольку в этом случае в вашу программу всегда отправляется 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 и не переносимо.

0 голосов
/ 20 июля 2010

В этом примере я использую 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

Надеюсьэто помогает?

...