簡體   English   中英

無法使用C中的命名管道與shell腳本進行通信

[英]Can't use named pipe from C to communicate with shell script

我有一個像這樣的C程序(從這里復制):

#include <fcntl.h>

#define PATH "testpipe"
#define MESSAGE "We are not alone"

int main()
{
   int fd;

   mkfifo ( PATH, 0666 );

   fd = open ( PATH, O_WRONLY );
   write ( fd, MESSAGE, sizeof ( MESSAGE ) );
   close ( fd );

   unlink ( PATH );
   return 0;
}

和shell腳本如下:

echo < testpipe

一旦我執行了C程序,echo語句就會返回,但We are not alone 我也嘗試從命令行創建管道,而使用mknod ,它沒有任何區別。 為什么這不起作用?

編輯:

很多人都指出問題是echo而不是C ,問題是,我需要它與echoomxplayer實際上,因為我向它發出視頻控制命令,即p暫停或q退出)。 因此,我希望得到一些答案,說明如何更改C代碼以使其與echo語句一起使用,而不是相反

編輯:

我沒有包含完整的代碼,因為它使用了omxplayer並且相當大,但是有些用戶請求它,因此,這是我保持這個MWE的最小值:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define PIPEPATH "testpipe"
#define VIDEOPATH "Matrix.mkv"
#define MESSAGE "q"
#define VIDEOPLAYER "omxplayer"

int main()
{
   int fd;
   pid_t pid;
   pid_t wpid;
   int status;
   char shellCmd [ 1000 ];
   struct timespec time1, time2; //used for sleeping

   //Make pipe BEFORE forking
   mkfifo ( PIPEPATH, 0666 );

   if ( ( pid = fork () ) < 0 )
   {
      perror ( "Fork Failed\n" );
      return -1;
   }
   else if ( pid == 0 )
   { //first child keeps pipe open
      sprintf ( shellCmd, "tail -f /dev/null > %s", PIPEPATH );
      if ( system ( shellCmd ) == -1 )
      {
         printf ( "Error: %s\n", shellCmd );
         fflush(stdout);
      }
   }
   else{
      time1.tv_sec = 1L; //sleep for 1 second to let first child issue its command
      time1.tv_nsec = 0L; //Dont worry about milli seconds

      nanosleep ( &time1, &time2 );

      if ( ( pid = fork () ) < 0 )
      {
         perror ( "Fork Failed\n" );
         return -1;
      }
      else if ( pid == 0 )
      { //second child launches the movie
         sprintf ( shellCmd,  "cat %s | %s %s 2>&1 > /dev/null", PIPEPATH, VIDEOPLAYER,  VIDEOPATH );
         if ( system ( shellCmd ) == -1 )
         {
            printf ( "Error: %s\n", shellCmd );
            fflush(stdout);
         }
      }
      else
      {
         if ( ( pid = fork () ) < 0 )
         {
            perror ( "Fork Failed\n" );
            return -1;
         }
         else if ( pid == 0 )
         { //third child waits 5 seconds then quits movie
            time1.tv_sec = 5L; //sleep for 5 seconds
            time1.tv_nsec = 0L; //Dont worry about milli seconds

            nanosleep ( &time1, &time2 );

            printf ( "Sleep over, quiting movie\n");
            fflush(stdout);

            fd = open ( PIPEPATH, O_WRONLY );
            write ( fd, MESSAGE, sizeof ( MESSAGE ) );
            close ( fd );
         }
      }
   }

   //Note the first child will never exit as it is a blocking shell script
   while ( ( wpid = wait ( &status ) ) > 0 )
   {
      printf ( "Exit status of %d was %d (%s)\n", ( int ) wpid, status, ( status == 0 ) ? "accept" : "reject" );
      fflush(stdout);
   }

   unlink ( PIPEPATH );

   return 0;

正如我所指出的,程序永遠不會退出,所以只需發出一個Ctrl + C}

編輯3:

好的我正在取得進展,似乎你給管道的東西和你得到的東西不是一回事,運行這個腳本並觀察:

#include <stdio.h>
#include <fcntl.h>
#define PIPE_PATH "testpipe"

int main( int argc, char *argv[] ) {
   int fd;
   FILE *fp;
   char c;

   if ( atoi ( argv [ 1 ] ) == 1 )
   {
      printf ("Writer [%s]\n", argv[1]);

      mkfifo ( PIPE_PATH, 0666 );

      fd = open ( PIPE_PATH, O_WRONLY );
      c = getchar();
      write(fd, c, 1);

      close(fd);
   }
   else if ( atoi ( argv [ 1 ] ) == 2 )
   {
      printf ( "Reader [%s]\n", argv[1] );

      fp = fopen( PIPE_PATH, "r" );
      c = getc ( fp );
      putchar ( c );

      printf ( "\n" );
      fclose ( fp );

      unlink( PIPE_PATH );
   }

   return 0;
}

編輯4:

JF Sebastian問了一些好問題,這是對這些問題的回應。 我最終要做的是同步2個或更多的omxplayer實例在2個或更多的覆盆子pis上播放同一個電影。 omxplayer-sync嘗試實現這一點,但它不准確,它是用Python編寫的,不適合這個,它的方法是,IMO,不是一個好的。 我正在食物鏈中努力嘗試最簡單的解決方案,看看它們是否可行。 從最簡單到最難,這是我到目前為止所嘗試的(或計划嘗試)

  1. 在未來同意的時間從shell啟動omxplayer實例: FAIL
  2. 將來在約定的時間從Python腳本啟動omxplayer實例(更准確的時間): FAIL
  3. 將來在約定的時間從C程序啟動omxplayer實例(更准確的時間): FAIL
  4. 啟動並立即暫停,然后在同一時間約定的時間從Python腳本中取消omxplayer實例(更准確的時間): FAIL
  5. 啟動並立即暫停,然后在同一時間約定的時間從C程序中取消暫停(通過echo -np > namedpipe omxplayer echo -np > namedpipe omxplayer )來自C程序的omxplayer實例(更准確的時間): FAIL
  6. 啟動並立即暫停,然后取消暫停(通過write ( fd_of_named_pipe, 'p', sizeof ( 'p') ); )來自C程序的omxplayer實例(更准確的時間方式)在同一時間同意的時間: PENDING
  7. 重新實現omxplayer並調整播放速度以趕上最快的播放器: FUTURE

基本上在投入大部分時間用於理解然后修改omxplayer (7)的源代碼之前,我想看看是否使用C的本機write ( fd_of_named_pipe, 'p', sizeof ( 'p') )操作比它更快system(echo -np > namedpipe)調用,它會分叉子並調用shell來寫入命名管道(我的預感告訴我它會更快,希望更准確)。 如果這可以工作並取消所有實例在15ms內,非常棒,我將不必查看omxpleyer的源代碼。 如果沒有,作為最后的手段,我將開始修改源代碼。

使用為讀取標准輸入而制作的程序可能很有用。

echo不會這樣做,但會打印其命令行參數。 這是一個區別。

試試cat

你可以用

xargs < testpipe echo

這將傳遞testpipe的輸出(一次一行)作為echo的參數 - 並生成

We are not alone

到屏幕......

注意 - 您的評論“我想修改C代碼以使用echo”,而不考慮echo coes(特別是它不與管道交互)的方式,似乎是倒退。 你真正的問題應該是“如何從C進入omxplayer命令”?

我在這里找到了以下用於omxplayer控件的代碼片段

mkfifo /tmp/cmd

omxplayer -ohdmi mymedia.avi < /tmp/cmd

echo . > /tmp/cmd (Start omxplayer running as the command will initial wait for input via the fifo)

echo -n p > /tmp/cmd - Playback is paused

echo -n q > /tmp/cmd - Playback quits

這告訴我以下內容適合您:

omxplayer -ohdmi mymedia.avi < testpipe

您的C程序根據需要生成字符以提供omxplayer 只是不要關閉管道,保持C程序運行。 但是,當我嘗試編寫一個小測試程序來確認這一點:

#include <stdio.h>
#include <fcntl.h>
#define PATH "testpipe"

int main(void) {
    int fd;
    char c;

    mkfifo(PATH, 0666);
    fd = open( PATH, O_WRONLY);
    char keyStroke[2] = " ";

    while((c = getchar())!='q') {
        keyStroke[0] = c;
        write(fd, keyStroke, 1);
    }

    close(fd);
    unlink(PATH);
    return 0;
}

我發現上面沒有得到任何反應,直到我在發送終端點擊'q' - 換句話說,直到管道關閉。 稍微搜索一下,轉到https://stackoverflow.com/a/4060641/1967396 ,他們建議使用較低級別的“管道讀取程序”,所以我實現了:

#include <stdio.h>
#define PIPE "testpipe"

int main(void) {
    FILE *fp;
    fp = fopen(PIPE, "r");
    int c;

    while((c = getc(fp)) != EOF)
    {
        printf("%c", c);
    }

    fclose(fp);
    return 0;
}

在一個終端和另一個終端中的早期程序中運行它,我現在可以看到一個終端中的字符出現在另一個終端中。 我認為這就是你想要的(盡管我仍然必須在輸入終端的每個字符后按“輸入”,因為我使用的是getchar() ,但我相信你可以弄清楚如何解決這個問題)。

如果這有用,請告訴我......

這是你如何模仿{ sleep 10; echo -np ; } | omxplayer { sleep 10; echo -np ; } | omxplayer { sleep 10; echo -np ; } | omxplayer命令:

/* for clock_nanosleep() */
#if ! defined(_POSIX_C_SOURCE)  || _POSIX_C_SOURCE < 200112L
#define _POSIX_C_SOURCE 200112L
#endif

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <unistd.h>

#define Close(FD) do {                                          \
    const int Close_fd = (FD);                                  \
    if (close(Close_fd) == -1)                                  \
      fprintf(stderr, "%s:%d: close(" #FD ") %d: %s\n",         \
              __FILE__, __LINE__, Close_fd, strerror(errno));   \
  }while(0)

static void _report_error_and_exit(const char* msg) {
  perror(msg);
  _exit(EXIT_FAILURE);
}

static void report_error_and_exit(const char* msg) {
  perror(msg);
  exit(EXIT_FAILURE);
}

int main(void) {
  int fd[2]; /* pipe */
  pid_t pid = -1;

  if (pipe(fd) == -1)
    report_error_and_exit("pipe");

  if ((pid = fork()) == -1)
    report_error_and_exit("fork");
  else if (pid == 0)  { /* child: sleep, write */
    Close(fd[0]); /* close unused read end of the pipe */

    { /* sleep until specified time */
      struct timespec endtime = {0};
      int r = -1;

      /* set some time into the future (just as an example) */
      if (clock_gettime(CLOCK_REALTIME, &endtime) < 0)
        _report_error_and_exit("clock_gettime");
      endtime.tv_sec += 10; /* seconds */

      /* NOTE: use settable system-wide real-time clock */
      while ((r = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME,
                                  &endtime, NULL)) == EINTR)
        ; /* retry */

      if (r != 0)
        _report_error_and_exit("clock_nanosleep");
    }

    { /* write to the pipe */
      char c = 'p';
      ssize_t size = sizeof c, n = size, m = 0;
      while (n > 0) { /* until there is something to write */
        if ((m = write(fd[1], &c + (size - n), n)) > 0)
          n -= m;
        else if (errno != EINTR)
          _report_error_and_exit("write");
      }
    }
    _exit(EXIT_SUCCESS); /* child done */
  }

  /* parent: run `omxplayer < fd[0]` */
  Close(fd[1]); /* close unused write end of the pipe */

  /* redirect stdin */
  if (fd[0] != STDIN_FILENO) {
    if (dup2(fd[0], STDIN_FILENO) == -1)
      report_error_and_exit("dup2");
    else
      Close(fd[0]);
  }
  execlp("omxplayer", "omxplayer", NULL);
  _report_error_and_exit("execlp");
}

要試用它,請運行:

$ gcc *.c -lrt && ./a.out

它應該立即啟動omxplayer並在當前時間加上10秒寫入p 編寫使用絕對時間進行睡眠。

它應該可以幫助您勾選omxplayer-sync列表中的下一個項目。 但它無法解決在不同主機上同步播放多個視頻播放器的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM