我有一个这样的C程序(复制自here):

#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,问题是,我需要它与echo一起工作(实际上,当我向它发出视频控制命令时,即omxplayer表示暂停或p表示退出)。因此,我希望得到一些答案,说明如何更改C代码使其与q语句一起工作,反之亦然
编辑:
我没有包括完整的代码,因为它使用echo并且相当大,但是一些用户请求它,因此,这里是尽可能少的,我可以保持这个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:
塞巴斯蒂安问了一些好问题,这是对这些问题的回应。我最终要做的是同步两个或多个omxplayer实例,在两个或多个raspberry pis上播放相同的电影。omxplayer-sync试图实现这一点,但它并不准确,它是用不适合这一点的Python编写的,而且它的方法,IMO,不是一个好方法。我在食物链上努力寻找最简单的解决方案,看看它们是否可行。从最简单到最困难,以下是我迄今为止所做的努力(或计划)
在约定的时间从shell中同时启动omxplayer实例:失败
在将来约定的时间从Python脚本启动omxplayer实例(更精确的时间顺序):失败
在未来约定的时间从C程序启动omxplayer实例(甚至更精确的时间):同时失败
启动并立即暂停,然后在将来约定的时间从Python脚本中取消暂停omxplayer实例(更准确的时间顺序):同时失败
启动并立即暂停,然后在约定的时间取消暂停(通过omxplayerecho -n p > namedpipe来自C程序的实例(更准确的时间顺序),同时:失败
启动并立即暂停,然后在约定的时间取消暂停(通过omxplayerwrite ( fd_of_named_pipe, 'p', sizeof ( 'p') );来自C程序的实例(更准确的时间顺序),同时:挂起
重新实现omxplayer并调整播放速度以赶上最快的播放器:未来
基本上,在我投入大量精力去理解并修改omxplayer(7)的源代码之前,我想看看使用C的原生omxplayer操作是否比它的write ( fd_of_named_pipe, 'p', sizeof ( 'p') )调用更快,后者派生一个子对象并调用shell来写入命名管道(我的直觉告诉我这将更快,希望更准确)。如果这能工作并将所有实例取消暂停到15毫秒以内,太棒了,我就不必查看system(echo -n p > namedpipe)的源代码了。如果没有,作为最后手段,我将开始修改源代码。

最佳答案

下面是如何模拟{ sleep 10; echo -n p ; } | 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列表中的下一项。但这并不能解决不同主机上多个视频播放器同步播放的问题。

关于c - 无法使用C中的命名管道与Shell脚本进行通信,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20155872/

10-09 08:36