Laradock生态下, 如何使用PHP函数exec()或shell_exec()执行某些系统命令?

诉求

由于项目需要调用一个java编写的算法接口, 所以就在workspace中安装了java8环境并将此算法代码通过javac成功编译后实测在命令行中可以直接运行java main得到结果. 这样在项目业务中就可以直接使用诸如:exec('java main', $res)来获取调用java算法的结果. 可实际情况却并不顺利, 问题在下面描述.

环境信息如下:

Component Version
PHP 7.3.15
Swoole 4.5.2
LaravelS 3.7.8
Laravel Framework 5.8.31
Java jdk 1.8.0_212

在环境的基础上新增了java8环境/laradock/workspace/Dockerfile新增如下:

.
.
.

###########################################################################
# Java8 jdk-8u212
###########################################################################

USER laradock
# 解压jdk8包至指定目录
ADD ./jdk8/jdk-8u212-linux-x64.tar.gz /home/laradock/.java
# 添加算法所需jar包
COPY ./jdk8/jars/fastjson-1.2.70.jar /home/laradock/.java/jdk1.8.0_212/lib
# 设置laradock用户的环境变量
RUN echo "" >> ~/.bashrc && \
    echo 'export JAVA_HOME="$HOME/.java/jdk1.8.0_212"' >> ~/.bashrc && \
    echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> ~/.bashrc && \
    echo 'export CLASSPATH="$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/fastjson-1.2.70.jar"' >> ~/.bashrc

# ENV JAVA_HOME $JAVA_HOME
# ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/fastjson-1.2.70.jar

# Add JAVA binaries to root's .bashrc
USER root
# 将.java所有权更改为laradock(注意此时的用户是root)并添加root用户的环境变量
RUN chown -R laradock:laradock /home/laradock/.java && \
    echo "" >> ~/.bashrc && \
    echo 'export JAVA_HOME="/home/laradock/.java/jdk1.8.0_212"' >> ~/.bashrc && \
    echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> ~/.bashrc && \
    echo 'export CLASSPATH="$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/fastjson-1.2.70.jar"' >> ~/.bashrc

# Add PATH For Java
ENV PATH $PATH:/home/laradock/.java/jdk1.8.0_212/bin

修改Dockerfile后重新构建容器workspace(docker-compose up -d --build --force-recreate workspace)

下图为命令行执行php脚本打印的结果:
Laradock生态下, 如何使用PHP函数exec()或shell_exec()执行某些系统命令?
Laradock生态下, 如何使用PHP函数exec()或shell_exec()执行某些系统命令?

困惑

而在实际项目运行时得到的结果却是:

为了方便描述问题, 这里采用了在浏览器控制台直接打印出结果

Laradock生态下, 如何使用PHP函数exec()或shell_exec()执行某些系统命令?
Laradock生态下, 如何使用PHP函数exec()或shell_exec()执行某些系统命令?
我们再对比下命令行的环境变量 $PATH:
Laradock生态下, 如何使用PHP函数exec()或shell_exec()执行某些系统命令?
以及php的路径:
Laradock生态下, 如何使用PHP函数exec()或shell_exec()执行某些系统命令?
从上图中可以看出, 命令行下的用户是laradock, 和控制台打印的用户是同一个, 然而php命令的路径却不一样? 环境变量也完全不一样, 这是为何….? 这个问题困扰了我很久, 也google了很多资料, 还是不知道如何解决…:persevere: :persevere: :persevere:

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 14

@_mars 你的理解有误。2>&1 的意思是将错误重定向到标准输出,并不代表将输出格式化成单行输出,所以你要明白,这是两回事。当然,你需要同时获取到标准输出以及错误输出信息的时候,重定向操作是必须的。

所以,你想获取 exec() 所有的输出,还是必须通过我前面说的操作去做,因为你并不能确定,程序输出中,没有存在换行,多行输出的可能。

3年前 评论

@_mars 可以给你举个形象的例子代码,你就能体会到两者写法的区别:

<?php
$cmd = 'ls /';
exec($cmd, $output);
print_r($output);

echo PHP_EOL;

$result = exec($cmd);
print_r($result);

echo PHP_EOL;
3年前 评论

其实通过php调用shell命令做法不怎么好,开了这些函数,也会带来安全隐患。 可以要求java方,提供RPC、HTTP之类的接口吗。

3年前 评论

@Brad-Wen java方暂时不接受任何修改, 他只提供了源码 :sob:

3年前 评论

你看那个报错,我大概能猜出报错原因,你想想,你用命令行能执行成功的话,因为你是root用户,但是如果你是以网页访问用户去访问,这时候php-fpm的执行用户可能是nobody之类的,那么你的java执行文件,对于nobody用户之类的来说,是没有权限的,也是不可见的,所以可能就报错找不到,当然,具体是不是这么回事,还得你结合我的描述去确认,因为我能看到的你的信息有限。

3年前 评论

@Brad-Wen 在命令行和php脚本中同时使用whoami打印出的用户是同一个都是laradock用户, 而且which php在命令行得到的php路径是/usr/bin/php然而控制台打印的php路径确是/usr/local/bin/php

3年前 评论

@_mars 因为上下文环境的问题,我建议你在脚本,所有都应该写绝对路径,这样就能避免此类问题,降低你排查问题的难度。例如:你应该是使用 /usr/bin/php 而不是 php

3年前 评论

@_mars 另外,为了确认是否权限问题引起的。有两个方法可尝试,当然,不建议你在生产环境做此操作,因为这是一个危险行为,只建议你在测试环境做调试。 方法一:php-fpm 使用 root 用户来运行。 方法二:给涉及到的命令、或文件,赋予 777 权限,例如你提到的 java、php 等命令,来确保执行权限充足。

3年前 评论

@Brad-Wen 首先感谢大佬的耐心回答. 上述所说的这些方法我都测试过

首先是命令的路径, 项目中是使用的绝对路径(其实根据文中的描述应该能看出来问题在于控制台打印的环境变量中并没有java)

file

其次是权限问题, 在/laradock/workerspace/Dockerfile中其实是有更改所有者操作的chown -R laradock:laradock /home/laradock/.java.

file file

3年前 评论

@_mars 刚去查了一下手册,exec 函数,返回的是输出结果的最后一行,我在想,会不会忽略了一些关键性的报错信息,你可以尝试一下获取全部输出。例如:

$cmd = 'java xxxxx';
exec($cmd, $output);
print_r($output);
3年前 评论

@Brad-Wen 你看文中的命令都是以2>&1结尾的, 其实就是打印错误信息的(如果不这么写, 那么命令有问题的话exec()返回的结果是空字符串). 你的写法是赋值到Array $output中. 结果参照浏览器控制台截图:
file
file

3年前 评论

请求方式不一样,最终走的是php-fpm,我试过了,如果在php-fpm里面安装java环境需要注意用户和环境变量。

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!