0

私はしばらくこれにこだわっています。次のような C プログラムがあるとします。このプログラムに文字列を送信し、その後制御を取得できるようにしたいと考えています。私が行った場合:


--> 猫のマイファイル | myprogram
または
--> echo "0123" | myprogram
または
--> myprogram < myfile 出力
を取得します (myfile には「0123」が含まれています)
30 31 32 33

-n オプションを使用すると、segfault が発生します
--> echo -n mystring | ./test
zsh: done echo -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

編集:

以下は私が思いついたラッパーです。子実行ファイルの出力が正しく表示されないことを除いて、私が望むすべてを行います。

ラッパーなし:

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

3 に答える 3

0

1 つのヌル文字を受け入れるようにプログラムを変更してから続行することができます...動作する可能性があります。

10行目を次のように置き換えます

while (TRUE)
{
    x = getchar();
    if (x == '\n')
        break;
    if (x == '\0')
    {
        if (seen)
            break;
        seen = TRUE;
    }
...
于 2010-07-20T13:35:29.867 に答える
0

プログラムの動作を変更できない場合、名前付きパイプを使用してこれを達成することはできないと思います。本質的に名前付きパイプは同じなので、リダイレクトを使用して標準入力からの出力を提供します。

この場合、常に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);

言うまでもなく、この 2 番目のソリューションは Linux 用であり、移植性はありません。

于 2012-01-22T19:02:59.520 に答える
0

この例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

これが役立つことを願っていますか?

于 2010-07-20T09:17:24.603 に答える