如何使用EXECL从C代码执行Python脚本?

时间:2010-09-06 18:20:19

标签: c

我想知道如何使用execl(或类似的)从我的C代码执行Python(或Lua等)脚本?

以下是一些“父/子”代码,显示了我如何使用PIPES向孩子发送STREAM数据。代码可能不完美,但你明白了。请注意底部的execl

#include <sys/types.h> 
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  

#define STDIN_FILENO    0       /* Standard input.  */
#define STDOUT_FILENO   1       /* Standard output.  */
#define STDERR_FILENO   2       /* Standard error output.  */
#define MAXLINE 4096

int main(void){
 int  n, parent_child_pipe[2], child_parent_pipe[2];
 pid_t pid;
 char line[MAXLINE];

 if (pipe(parent_child_pipe) < 0 || pipe(child_parent_pipe) < 0)
  puts("Error creating pipes...\n");

 if ( (pid = fork()) < 0)
  puts("Error forking...\n");
 else if (pid > 0) { /* PARENT */
  close(parent_child_pipe[0]); 
  close(child_parent_pipe[1]);
  while (fgets(line, MAXLINE, stdin) != NULL) {
   n = strlen(line);
   if (write(parent_child_pipe[1], line, n) != n)
    puts("write error to pipe...\n");
   if ( (n = read(child_parent_pipe[0], line, MAXLINE)) < 0)
    puts("read error from pipe...\n");
   if (n == 0) {
    puts("child closed pipe...\n");
    break;
   }
   line[n] = 0; /* null terminate */
   if (fputs(line, stdout) == EOF)
    puts("fputs error...\n");
  }
  if (ferror(stdin))
   puts("fgets error on stdin...\n");
  exit(0);

 } else {  /* CHILD */
  close(parent_child_pipe[1]);
  close(child_parent_pipe[0]);
  if (parent_child_pipe[0] != STDIN_FILENO) {
   if (dup2(parent_child_pipe[0], STDIN_FILENO) != STDIN_FILENO)
    puts("dup2 error to stdin...\n");
   close(parent_child_pipe[0]);
  }
  if (child_parent_pipe[1] != STDOUT_FILENO) {
   if (dup2(child_parent_pipe[1], STDOUT_FILENO) != STDOUT_FILENO)
    puts("dup2 error to stdout...\n");
   close(child_parent_pipe[1]);
  }
  **if (execl("./child", "child", (char *) 0) < 0)**
   puts("execl error...\n");
 }
}

现在上面的“子”程序是用C语言编写的,只是通过STDIN接收流,操纵流,然后使用STDOUT将其发回。

类似的东西:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  

#define STDIN_FILENO    0       /* Standard input.  */
#define STDOUT_FILENO   1       /* Standard output.  */
#define STDERR_FILENO   2       /* Standard error output.  */
#define MAXLINE 4096

int main(void){
 int  n;
 char line[MAXLINE];

 while ( (n = read(STDIN_FILENO, line, MAXLINE)) > 0) {
  line[n] = 0;  /* null terminate */  
  n = strlen(line);
  if (write(STDOUT_FILENO, line, n) != n)
   puts("write error");
 }
 exit(0);
}

这样工作正常,但现在我希望能够用Python / Lua等任何脚本语言编写CHILD。我该怎么做?我尝试了类似的东西:

if(execl(“path to python”,“test.py”,(char *)0)&lt; 0)

但似乎HANG等待接收输入?

有人可以帮我这个吗?我假设PIPES可以与Lua / Python之类的东西交谈,可以从STDIN读取并发送回STDOUT吗?

更新:

我现在做了一些小改动,这里是Python文件:“NullFilter.py”只是ECHOS发送的内容:

#!/usr/bin/python
import sys

class NullFilter:

      def execute(self): 
            #Read data from STDIN...
            data = sys.stdin.read() 
            #Write data to STDOUT...
            sys.stdout.write(data) 
            exit(0) 
if __name__ == '__main__':
      nf = NullFilter()
      nf.execute()

现在C代码使用:

调用它
...
if (execl("/usr/bin/python","./NullFilter.py","./NullFilter.py",NULL, (char *) 0) < 0)
puts("execl error...\n");
...

当我现在运行它时,我可以在STDIN中输入文本,但必须点击CRTL-C才能看到发生了什么:结果如下:

debian@debian:~/Desktop/pipe example$ ./parent
hello
hello again
^CTraceback (most recent call last):
  File "./NullFilter.py", line 17, in <module>

nf.execute()
  File "./NullFilter.py", line 10, in execute
debian@debian:~/Desktop/pipe example$     data = sys.stdin.read() 
KeyboardInterrupt

debian@debian:~/Desktop/pipe example$ 

更新2:

好的,所以我一直在玩一点,只是将Python代码从“sys.stdin.read()”更改为“sys.stdin.readline()”,它似乎在某种程度上起作用,但根本不完美...

#!/usr/bin/python
import sys

class NullFilter:

      def execute(self): 
            #Read data from STDIN...
            data = ""             
            for line in sys.stdin.readline():
                data = data + line 
            #Write data to STDOUT...
            sys.stdout.write(data) 
            exit(0) 
if __name__ == '__main__':
      nf = NullFilter()
      nf.execute()

这是一种解决方法,但根本不完美。任何其他的想法如何让STDIN读取流UNBUFFERED?我在Python中查看了SELECT模块:

http://docs.python.org/library/select.html

我也尝试将“-u”传递给Python以使其“无缓冲”但仍然没有运气; - (

但这肯定不会那么难吗?

林顿

3 个答案:

答案 0 :(得分:4)

经过多次游戏并试图冲洗一切我意识到我需要关闭从父母到孩子的PIPE的STDOUT结束(当然写到管道之后)......

所以代码现在是:

#include <sys/types.h>  
#include <stdio.h>   
#include <stdlib.h>   
#include <string.h>   
#include <unistd.h>   

#define STDIN_FILENO    0       /* Standard input.  */ 
#define STDOUT_FILENO   1       /* Standard output.  */ 
#define STDERR_FILENO   2       /* Standard error output.  */ 
#define MAXLINE 4096 

int main(void){ 
 int  n, parent_child_pipe[2], child_parent_pipe[2]; 
 pid_t pid; 
 char line[MAXLINE]; 
 int rv;

 if (pipe(parent_child_pipe) < 0 || pipe(child_parent_pipe) < 0) 
  puts("Error creating pipes...\n"); 

 if ( (pid = fork()) < 0) 
  puts("Error forking...\n"); 
 else if (pid > 0) { /* PARENT */ 
  close(parent_child_pipe[0]);  
  close(child_parent_pipe[1]); 
  while (fgets(line, MAXLINE, stdin) != NULL) { 
   n = strlen(line); 
   if (write(parent_child_pipe[1], line, n) != n) 
    puts("write error to pipe...\n"); 
   close(parent_child_pipe[1]);
   wait(&rv); 
   if ( (n = read(child_parent_pipe[0], line, MAXLINE)) < 0) 
    puts("read error from pipe...\n"); 
   if (n == 0) { 
    puts("child closed pipe...\n"); 
    break; 
   } 
   line[n] = 0; /* null terminate */ 
   if (fputs(line, stdout) == EOF) 
    puts("fputs error...\n"); 
  } 
  if (ferror(stdin)) 
   puts("fgets error on stdin...\n"); 
  exit(0); 

 } else {  /* CHILD */ 
  close(parent_child_pipe[1]); 
  close(child_parent_pipe[0]); 
  if (parent_child_pipe[0] != STDIN_FILENO) { 
   if (dup2(parent_child_pipe[0], STDIN_FILENO) != STDIN_FILENO) 
    puts("dup2 error to stdin...\n"); 
   close(parent_child_pipe[0]); 
  } 
  if (child_parent_pipe[1] != STDOUT_FILENO) { 
   if (dup2(child_parent_pipe[1], STDOUT_FILENO) != STDOUT_FILENO) 
    puts("dup2 error to stdout...\n"); 
   close(child_parent_pipe[1]); 
  } 
  if (execl("./NullFilter.py", "./NullFilter.py", (char *) 0) < 0)
   puts("execl error...\n"); 
 } 
} 

你可以看到“close(parent_child_pipe [1]);”在写完上面的PIPE之后,这是我必须做的关键部分。这将强制将流刷新到Python脚本,Lua脚本,C代码等....

在上面你会看到我正在执行一个Python脚本“NullFilter.py”....

注意:如果您运行上面的代码,它将适用于输入/输出的一次迭代,因为Python脚本在第一次测试后关闭了管道,但是您需要构建基础......

感谢所有的帮助,我从这次练习中学到了很多东西; - )

林顿

答案 1 :(得分:1)

我相信你可以通过像这样的shebang行启动python文件来实现你想要做的事情:

#!/usr/bin/python

并确保它是可执行的,然后将脚本用作execl()

中的可执行字段

请参阅http://linux.about.com/od/commands/l/blcmdl2_execve.htm

  

文件名必须是二进制文件   可执行文件,或以。开头的脚本   一行“#!interpreter”   [arg]“。在后一种情况下,   解释器必须是有效的路径名   对于不是自己的可执行文件   一个脚本,将被调用为   interpreter [arg] filename。

在传递参数的情况下(理论上也是有效的),它看起来不像python实际上正在执行脚本并完成它应该 - 而是它打开一个交互式终端,这就是它挂起的原因 - 它希望你通过stdin进行通信。尝试“test.py”的绝对路径,看看会发生什么,因为听起来python无法找到它。

实际上,有另一种方法。没有理由你不能通过管道逐行将脚本传递给解释器并以这种方式运行。

答案 2 :(得分:0)

你是否尝试过冲洗孩子?

更新:

我尝试使用你的C代码和这两个脚本(perl和python),他们的行为就像我机器上的C子一样

#!/usr/bin/perl -w
use IO::Handle; 

while (<>)
{
        print;
        flush STDOUT;
}

并且

#!/usr/bin/python
import sys

class NullFilter:
        def execute(self): 
                while True:
                        line=sys.stdin.readline()
                        if not line: break
                        sys.stdout.write(line)
                        sys.stdout.flush()
                exit(0) 
if __name__ == '__main__':
        nf = NullFilter()
        nf.execute()