如何使用 EXECL 从 C 代码执行 Python 脚本? | 珊瑚贝

How to execute Python script from C code using EXECL?


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#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…\
);

 if ( (pid = fork()) < 0)
  puts(“Error forking…\
);
 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…\
);
   if ( (n = read(child_parent_pipe[0], line, MAXLINE)) < 0)
    puts(“read error from pipe…\
);
   if (n == 0) {
    puts(“child closed pipe…\
);
    break;
   }
   line[n] = 0; /* null terminate */
   if (fputs(line, stdout) == EOF)
    puts(“fputs error…\
);
  }
  if (ferror(stdin))
   puts(“fgets error on stdin…\
);
  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…\
);
   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…\
);
   close(child_parent_pipe[1]);
  }
  **if (execl(“./child”,“child”, (char *) 0) < 0)**
   puts(“execl error…\
);
 }
}

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

类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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(“python 的路径”, “test.py”, (char *) 0) < 0)

但它似乎只是挂起等待接收输入?

有人可以帮我解决这个问题吗?我假设 PIPES 可以与可以从 STDIN 读取并发送回 STDOUT 的 Lua / Python 之类的任何东西通信?

更新:

我现在做了一些小改动,这里是 Python 文件:”NullFilter.py”,它简单地回显了它发送的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/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 代码调用它使用:

1
2
3
4
5

if (execl(“/usr/bin/python”,“./NullFilter.py”,“./NullFilter.py”,NULL, (char *) 0) < 0)
puts(“execl error…\
);

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

1
2
3
4
5
6
7
8
9
10
11
12
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()” ,它似乎工作在一定程度上,但根本不完美……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/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()

这是一种解决方法,但一点也不完美。还有其他想法如何让标准输入读取无缓冲的流吗?我查看了 Python 中的 SELECT 模块:

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

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

但这肯定不会这么难吧?

林顿

  • 只是出于好奇,您为什么不使用 Python 提供的 API 与 C 交互?您可以使用它直接从 C 调用 Python 代码,而且非常灵活。
  • 您好,我知道 Python 中的 C API,但这不是我想要的。该应用程序实际上是一个 C 应用程序,我只是希望因为我使用 PIPES,所以我可以用 Python 或 Lua 等其他语言编写一些”用户退出”……但无论如何;-)


在玩了很多游戏并尝试刷新所有内容之后,我意识到我需要关闭从父级到子级的 PIPE 的 STDOUT 端(当然是在写入管道之后)…

所以现在的代码是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#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…\
);

 if ( (pid = fork()) < 0)
  puts(“Error forking…\
);
 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…\
);
   close(parent_child_pipe[1]);
   wait(&rv);
   if ( (n = read(child_parent_pipe[0], line, MAXLINE)) < 0)
    puts(“read error from pipe…\
);
   if (n == 0) {
    puts(“child closed pipe…\
);
    break;
   }
   line[n] = 0; /* null terminate */
   if (fputs(line, stdout) == EOF)
    puts(“fputs error…\
);
  }
  if (ferror(stdin))
   puts(“fgets error on stdin…\
);
  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…\
);
   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…\
);
   close(child_parent_pipe[1]);
  }
  if (execl(“./NullFilter.py”,“./NullFilter.py”, (char *) 0) < 0)
   puts(“execl error…\
);
 }
}

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


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

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

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

林顿


我相信您可以通过使用 shebang 行启动 python 文件来实现您想要做的事情,如下所示:

1
#!/usr/bin/python

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

中的可执行字段

见 http://linux.about.com/od/commands/l/blcmdl2_execve.htm:

Filename must be either a binary
executable, or a script starting with
a line of the form”#! interpreter
[arg]”. In the latter case, the
interpreter must be a valid pathname
for an executable which is not itself
a script, which will be invoked as
interpreter [arg] filename.

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

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

  • 您好,感谢上面的输入,是的,我确实将 #!/usr/bin/python 添加到了 Python 脚本的顶部。我会尝试您的绝对路径建议,并会尽快回复您。注意:实际上,Python 似乎确实可以找到该文件,因为在我输入一些输入并按 CTRL-C 后,我在 Python 代码中得到了一个 KeyboardInterrupt。它绝对挂在”sys.stdin.read()”上。在写入 PIPE 后,我尝试在 C 端进行 FFLUSH,但仍然没有运气;-(


你试过冲孩子吗?

更新:

我尝试使用您的 C 代码和这 2 个脚本(perl 和 python),它们的行为与我机器上的 C 孩子完全一样

1
2
3
4
5
6
7
8
#!/usr/bin/perl -w
use IO::Handle;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/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()

  • 您好,是的,我尝试对孩子进行冲洗,但它甚至没有到达那里,它挂在 “sys.stdin.read()” 上
  • 我什至尝试在写入 PIPE 后对 C 代码执行 FFLUSH,但也没有运气 ;-(
  • 您好,是的,READLINE 有效,但您只需输入 “sys.stdin.read()” 看看会发生什么……但感谢您的帮助 ;-)
  • 嗨,read() 阻塞是正常的,因为它等待 EOF,你可以通过读取它必须读取的数据量作为参数,read(5) 例如读取 5 个字节。


来源:https://www.codenong.com/3653521/

微信公众号
手机浏览(小程序)

Warning: get_headers(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed in /mydata/web/wwwshanhubei/web/wp-content/themes/shanhuke/single.php on line 57

Warning: get_headers(): Failed to enable crypto in /mydata/web/wwwshanhubei/web/wp-content/themes/shanhuke/single.php on line 57

Warning: get_headers(https://static.shanhubei.com/qrcode/qrcode_viewid_9336.jpg): failed to open stream: operation failed in /mydata/web/wwwshanhubei/web/wp-content/themes/shanhuke/single.php on line 57
0
分享到:
没有账号? 忘记密码?