如何通过jQuery AJAX调用PHP执行Shell脚本运行Python程序
问题现象
需要通过前端按钮触发AJAX请求,经PHP调用Shell脚本最终执行Python数据处理+数据库写入逻辑:
- 直接在服务器命令行执行Shell脚本时,Python脚本运行正常,可正常写入数据库
- 前端点击按钮后AJAX返回成功,弹出
done提示,但Python脚本未实际执行,数据库无对应写入记录
现有实现代码
前端按钮代码
<input type="button" style="background-color:#00BFFF;padding:3px;border: none;" id="btnnew2" class="button" name="pc73" value="Get The Latest" />
前端jQuery逻辑
$(document).ready(function(){ $('.button').click(function(){ if(!confirm('Are you sure?')){ e.preventDefault(); return false; } else{ //$('#loading').show(); $.ajax({ url : 'testrun.php', type : 'POST', success : function (result) { alert("done"); }, error : function () { alert("error"); } }); } }); });
PHP调用代码(testrun.php)
<?php $old_path = getcwd(); chdir('/home/scripts/'); $output = shell_exec("./testscript.sh"); chdir($old_path); echo $output; ?>
Shell执行脚本(testscript.sh)
#!/bin/bash cd /home/scripts/ export PATH="/root/anaconda3/envs/py36/bin:/usr/share/Modules/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin" export TEMP=/home/svradmin/tmp python3 testupdate.py
Python业务脚本(testupdate.py)
import pandas as pd from fbprophet import Prophet import altair as alt import numpy as np import statistics as st import mysql.connector as sql from sqlalchemy import create_engine import pymysql pymysql.install_as_MySQLdb() alt.renderers.enable('notebook') from pandas import DataFrame from datetime import datetime import os os.environ["TF_CPP_MIN_LOG_LEVEL"]="3" sql_transfer_Query = "insert into updatetest(text) values ('executed');" print(sql_transfer_Query) #Write to Final Alarm Table db_connection_write = sql.connect(host='localhost', database='test', user='XXXX', password='XXX',charset='utf8') cursor = db_connection_write.cursor() cursor.execute(sql_transfer_Query) db_connection_write.commit() if (db_connection_write.is_connected()): cursor.close() db_connection_write.close() print("MySQL connection is closed") ##exit()
故障修复方案
按以下优先级逐一排查修复即可解决问题:
修复前端JS显性错误
现有jQuery的click回调中使用了事件对象e但未声明参数,会直接触发JS报错,将回调定义从$('.button').click(function(){修改为$('.button').click(function(e){即可。
同时建议在AJAX成功回调中临时加alert(result)或console.log(result),直接查看PHP返回的脚本执行输出,方便定位报错。解决Web服务运行用户权限问题(核心故障原因)
命令行执行脚本时使用的是当前登录的服务器用户(多为root或高权限普通用户),但PHP执行shell_exec时使用的是Web服务的运行用户(常见为www-data/apache/nginx,根据部署的Web服务类型决定),该用户默认无相关资源访问权限:- 给Web用户开放脚本、目录权限:执行
chmod +x /home/scripts/testscript.sh给Shell脚本加执行权限,再给Web用户分配/home/scripts/、/home/svradmin/tmp目录的读写执行权限 - 不要将Python依赖环境放在
/root目录下:当前用的conda环境在/root/anaconda3路径下,普通用户无权限访问/root目录,会直接导致Python启动失败,将py36虚拟环境迁移到/opt/等所有用户可访问的公共路径即可。
- 给Web用户开放脚本、目录权限:执行
所有调用路径改用绝对路径,不依赖环境变量
Web服务执行脚本时加载的环境变量和命令行登录的环境变量完全不同,相对路径、依赖PATH的调用都会找不到对应文件:- 修改PHP中Shell调用逻辑,用绝对路径调用bash,同时捕获错误输出方便调试:
$output = shell_exec("/bin/bash /home/scripts/testscript.sh 2>&1"); - 修改Shell脚本中Python调用路径,直接写虚拟环境中Python的绝对路径,不要只写
python3,例如修改为/opt/anaconda3/envs/py36/bin/python3 /home/scripts/testupdate.py,同时删除Shell中冗余的PATH、TEMP导出逻辑,避免环境变量冲突。
- 修改PHP中Shell调用逻辑,用绝对路径调用bash,同时捕获错误输出方便调试:
移除Python脚本中不兼容非交互环境的配置
脚本中alt.renderers.enable('notebook')是专门给Jupyter Notebook交互环境用的配置,在后台命令行/PHP触发的非交互场景下执行会直接抛异常中断脚本,非Notebook环境下这行配置无实际作用,直接注释或删除即可。
内容的提问来源于stack exchange,提问作者Codec737

