PHP exec()调用Python脚本时json.load()失效问题
我帮你梳理下这个问题——单独跑正常但通过PHP调用就卡在json.load(),还没错误输出,这种情况基本都是环境/权限/路径的差异导致的,给你几个具体的排查和解决方向:
1. 先把路径问题彻底解决
你单独运行脚本时,当前工作目录是你手动启动的目录,但PHP的exec()调用时,工作目录大概率是Web服务器的运行目录(比如Apache的/var/www/html或者Nginx对应的站点目录)。如果你的find_files用的是相对路径,那PHP调用时根本找不到那些JSON临时文件,后续的open(file)和json.load()自然会出问题,但因为没捕获异常,错误信息又没输出出来。
解决办法:
- 把脚本里所有涉及文件/目录的路径都改成绝对路径,比如用
os.path.abspath("your_temp_dir")来生成绝对路径; - 或者在PHP调用脚本时,通过命令行参数传递绝对路径给Python脚本,比如:
然后在Python脚本里用exec("python3 /path/to/your_script.py /absolute/path/to/json_dir 2>&1 > output.txt");sys.argv[1]接收这个绝对路径。
2. 强制捕获并输出所有异常
你现在的代码没有异常处理,而且PHP调用时stdout和stderr可能没有被正确重定向,导致错误信息没写到output.txt里。给处理JSON的那段代码加上完整的异常捕获:
首先在脚本开头导入sys:
import sys import os import json
然后修改循环部分:
for file in json_files: base = os.path.basename(file) id = os.path.splitext(base)[0] try: # 用with语句自动管理文件句柄,更安全 with open(file, 'r', encoding='utf-8') as opened_file: print(f"正在处理文件: {file}") json_data[id] = json.load(opened_file) print(f"成功加载文件: {file}") except Exception as e: # 把错误输出到stderr,确保能被重定向捕获 print(f"处理文件{file}出错: {str(e)}", file=sys.stderr) # 也可以直接抛出异常让脚本终止,方便定位 raise
同时,PHP调用时要把stderr重定向到输出文件:
exec("python3 /path/to/your_script.py 2>&1 > output.txt");
这样不管是正常输出还是错误信息,都会写到output.txt里,你就能看到到底是JSON格式错了,还是文件找不到,或者权限问题。
3. 检查Python环境差异
你手动运行的Python和PHP调用的Python可能不是同一个版本!比如你手动用的是python3.10,但PHP调用的是系统默认的python2.7,那json模块的行为会有差异,甚至可能直接报错。
在脚本开头加一行输出Python版本:
print(f"当前Python版本: {sys.version}")
然后看output.txt里的版本是不是你预期的。如果版本不对,就在exec()里用Python的绝对路径调用,比如:
exec("/usr/bin/python3.10 /path/to/your_script.py 2>&1 > output.txt");
(绝对路径可以通过which python3命令在终端里查到)
4. 检查文件权限
PHP运行时用的是Web服务器的用户(比如www-data、apache),这个用户有没有读取你的JSON临时文件的权限?你手动运行时用的是自己的用户,权限没问题,但Web用户可能没有访问那个临时目录的权限。
解决办法:
- 在生成临时JSON文件的时候,设置合适的权限,比如:
os.chmod(temp_json_file, 0o644) # 让其他用户也能读取 - 或者把临时文件存到Web用户有权限的目录,比如
/tmp(这个目录一般所有用户都能读写)。
5. 确认JSON文件的完整性
会不会是PHP调用时,生成JSON临时文件的过程还没结束,Python脚本就开始读取了?比如你的Python脚本是在文件还在被写入的时候执行json.load(),导致JSON格式不完整,从而卡住。
可以在Python脚本里加个简单的判断,比如等待文件大小稳定后再读取:
import time def wait_file_stable(file_path, wait_time=1): prev_size = -1 while True: current_size = os.path.getsize(file_path) if current_size == prev_size: break prev_size = current_size time.sleep(wait_time) # 在读取文件前调用 wait_file_stable(file) with open(file, 'r', encoding='utf-8') as opened_file: json_data[id] = json.load(opened_file)
按照这些步骤排查,应该能很快找到问题所在!
内容的提问来源于stack exchange,提问作者olivierhsta

