C语言使用fork-execv调用Python文件内函数报错如何解决
C程序通过fork+execv调用Python脚本指定函数的参数配置问题
背景说明
我想在C程序中通过fork和execv创建子进程调用Python脚本,最初测试调用/bin/ls可正常运行,测试代码如下:
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> int main() { int status; char *args[2]; args[0] = "/bin/ls"; args[1] = NULL; if ( fork() == 0 ){ printf("I am the child\n"); execv( args[0], args ); } else { printf("I am the parent\n"); wait( &status ); } return 0; }
目标Python脚本
后续需要调用如下Python文件中的Create_Buffer()函数,Python脚本路径为/opt/P01_SH/NLTK_Gutenberg/NLTK_Python_Libs.py,内容如下:
#!/usr/bin/python3 from nltk.corpus import gutenberg def Create_Buffer(): out_buf = [] pointer = id(out_buf) value_in = 21 print("I was here") print(value_in) return pointer def NLTK_Python_Libs(n): if n == 0: n = n + 1 return id(n)
错误尝试与报错
第一次修改后报错
我最初将C代码修改为如下内容:
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> int main() { int status; char * paramsList[] = { "/bin/bash", "-c", "/usr/bin/python", "/opt/P01_SH/NLTK_Gutenberg/NLTK_Python_Libs.Create_Buffer()", (char *)NULL }; if ( fork() == 0 ){ printf("I am the child\n"); execv("/usr/bin/python",paramsList); } else { printf("I am the parent\n"); wait( &status ); } return 0; }
运行后出现语法错误:
I am the parent I am the child File "string", line 1 /usr/bin/python SyntaxError: invalid syntax
第二次修改后报错
微调代码后内容如下:
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> int main() { int status; char *args[5]; args[0] = "/bin/bash"; args[1] = "-c"; args[2] = "/usr/bin/python"; args[3] = "/opt/P01_SH/NLTK_Gutenberg/NLTK_Python_Libs.Create_Buffer()"; args[4] = NULL; if ( fork() == 0 ){ printf("I am the child\n"); execv(args[0], args); } else { printf("I am the parent\n"); wait( &status ); } return 0; }
这次运行后直接进入Python交互式命令行,没有执行目标函数:
I am the parent I am the child Python 3.8.0 (default, Feb 25 2021, 22:10:10) [GCC 8.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
我尝试修改Python脚本的shebang行,替换为#!/usr/bin/python、#!/usr/bin/python3、#!/usr/bin/env python3或是直接删除,都没有解决问题。
需求说明
我需要在符合以下要求的前提下,实现C程序调用Python文件内的指定函数,支持传参:
- 可以使用任意exec系列函数,但必须使用绝对路径,禁止使用名称中带“p”的exec类函数
- 按名称调用指定函数,不能仅执行完整Python文件
C程序编译指令为:
gcc -fPIC -ggdb -o NLTK_Python.exe NLTK_Python.c -Wall -Wextra
运行环境为Ubuntu 18.04、Python 3.8。
解决方案
错误原因分析
你之前两次修改的代码都存在参数构造错误:
- 第一次错误:你给
execv传入的第一个参数是/usr/bin/python,但参数数组第一个元素是/bin/bash,参数列表和执行的程序不匹配,Python会把后续的/bin/bash等内容当成要执行的Python代码,自然报语法错误。 - 第二次错误:
bash -c后面的完整命令需要拼接成单个字符串,不能拆分。你把/usr/bin/python和后面的函数调用拆成了两个参数,bash只会把/usr/bin/python当成要执行的命令,后面的路径会被当成Python的位置参数,相当于直接执行python,所以进入了交互式界面。
正确实现方式
要调用指定Python文件中的函数,可以利用Python的-c参数执行一段导入模块+调用函数的代码片段,两种实现方案如下:
方案1:直接调用python,不经过bash
不需要额外调用bash,直接构造符合Python参数规则的数组即可,代码如下:
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> int main() { int status; // 构造Python参数:参数0是python本身,参数1是-c,参数2是要执行的Python代码串,最后以NULL结尾 char *args[] = { "/usr/bin/python3", "-c", "import sys; sys.path.insert(0, '/opt/P01_SH/NLTK_Gutenberg'); from NLTK_Python_Libs import Create_Buffer; Create_Buffer()", NULL }; if (fork() == 0) { printf("I am the child\n"); execv(args[0], args); // execv执行成功不会返回,走到这里说明执行失败 perror("execv failed"); _exit(1); } else { printf("I am the parent\n"); wait(&status); } return 0; }
如果需要给函数传参,可以直接在代码串里拼接参数,或者通过sys.argv获取:
比如要给Create_Buffer传整数参数100,可以把参数数组改成:
char *args[] = { "/usr/bin/python3", "-c", "import sys; sys.path.insert(0, '/opt/P01_SH/NLTK_Gutenberg'); from NLTK_Python_Libs import Create_Buffer; Create_Buffer(int(sys.argv[1]))", "100", NULL };
方案2:经过bash调用
如果需要通过bash执行,要把完整的Python调用命令拼接成单个字符串传给bash -c:
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> int main() { int status; char *args[] = { "/bin/bash", "-c", "/usr/bin/python3 -c 'import sys; sys.path.insert(0, \"/opt/P01_SH/NLTK_Gutenberg\"); from NLTK_Python_Libs import Create_Buffer; Create_Buffer()'", NULL }; if (fork() == 0) { printf("I am the child\n"); execv(args[0], args); perror("execv failed"); _exit(1); } else { printf("I am the parent\n"); wait(&status); } return 0; }
注意事项
- 必须先把Python脚本所在的目录加入
sys.path,否则Python无法找到对应的模块导入 - exec系列函数执行成功后不会返回,所以建议在execv后面加错误打印和退出逻辑,方便排查问题
- 所有路径都使用绝对路径,符合要求,没有用到带p的exec函数
内容的提问来源于stack exchange,提问作者RTC222
相关产品推荐
相关产品推荐

