[英]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
,问题是,我需要它与echo
( omxplayer
实际上,因为我向它发出视频控制命令,即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,不是一个好的。 我正在食物链中努力尝试最简单的解决方案,看看它们是否可行。 从最简单到最难,这是我到目前为止所尝试的(或计划尝试)
omxplayer
实例: FAIL omxplayer
实例(更准确的时间): FAIL omxplayer
实例(更准确的时间): FAIL omxplayer
实例(更准确的时间): FAIL echo -np > namedpipe
omxplayer
echo -np > namedpipe
omxplayer
)来自C程序的omxplayer
实例(更准确的时间): FAIL write ( fd_of_named_pipe, 'p', sizeof ( 'p') );
)来自C程序的omxplayer
实例(更准确的时间方式)在同一时间同意的时间: PENDING 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.