在PHP中打开Linux终端命令

5
我有一台运行 Linux 的服务器,可以执行针对 12 个节点(12 台运行 Linux 的计算机)的命令。最近我在服务器上下载了 PHP,以创建可通过打开特定 PHP 文件来执行命令的网页。
我使用了 exec()、passthru()、shell_exec() 和 system()。只有 system() 返回了我的代码的一部分。我希望 PHP 的功能像 Linux 中的开放终端命令一样,但我无法弄清楚如何做到!
以下是现在发生情况的示例(Linux 直接 vs PHP):当直接使用 Linux 打开终端命令时:
user@wizard:/home/hyperwall/Desktop> /usr/local/bin/chbg -mt

我收到一个输出:
The following settings will be used:
  option = mtsu   COLOR =    IMAGE = imagehereyouknow!
  NODES  = LOCAL
and additional code to send it to 12 nodes.

现在使用PHP实现如下:
switch($_REQUEST['do'])
{ case 'test':
    echo system('/usr/local/bin/chbg -mt');
    break;
}

输出:

The following settings will be used:
  option = mtsu   COLOR =    IMAGE = imagehereyouknow!
  NODES  = LOCAL

停了!有人能解释一下正在发生什么吗?以及如何修复?只有系统会显示部分代码,而其他函数则没有显示任何内容!


1
你使用过 strace 来找出它卡在哪里了吗?以调试模式运行 Web 服务器,以将其限制为单个进程。 - Ignacio Vazquez-Abrams
3
你正在运行哪个Web服务器?Web服务器在哪个用户下运行?如果它在“nobody”或类似用户下运行,你确定该用户可以执行chbg命令吗? - Mark R.
2
“and additional code to send it to 12 nodes.” 是什么?它从哪里来的? - Nimrod007
8个回答

4

我第一时间想到的是这可能与标准输出和错误有关。一些软件会在标准输出中转储一些信息,而在标准错误中转储其他信息。当您没有将标准错误重定向到标准输出时,大多数系统调用只返回标准输出部分。这就是为什么您可以在终端中看到整个输出,但在系统调用中却看不到。尝试使用:

 /usr/local/bin/chbg -mt 2>&1  

编辑: 此外,您可以尝试一些临时解决方案。例如,将输出重定向到脚本旁边的文件中,并在执行命令后读取其内容。这样,您就可以使用exec:

exec("usr/local/bin/chbg -mt 2>&1 > chbg_out");
//Then start reading chbg_out and see is it work

编辑2 此外,其他人为什么对你不起作用也没有道理。 例如,这段用c语言编写的代码将一个字符串转储到stderr中,而另一个则在stdout中。

#include <stdio.h>
#include<stdlib.h>
int main()
{

    fputs("\nerr\nrro\nrrr\n",stderr);
    fputs("\nou\nuu\nuttt\n",stdout);
    return 0;
}

这个 PHP 脚本试图通过 exec 运行它:

<?php

exec("/tmp/ctest",&$result);

foreach ( $result as $v )
{
echo $v;
}
#output ouuuuttt
?>

看到它仍然将stdout输出。但它没有接收到stderr。 现在考虑这个:

<?php

exec("/tmp/ctest 2>&1",&$result);

foreach ( $result as $v )
{
    echo $v;
}
//output: errrrorrrouuuuttt
?>

看,这次我们得到了完整的输出结果。

这次系统:

<?php
     echo system("/tmp/ctest 2>&1");
     //output: err rro rrr ou uu uttt uttt
?>

and so on ...


很棒的答案,举例子支持得很好。 - scrowler

2
也许你的chbg -mt会将额外的代码写入stderr而不是stdout?尝试像这样在php中执行你的脚本:
/usr/local/bin/chbg -mt 2>&1

2
其他回答提供了一般性的建议。但在这种特殊情况下,似乎您正在尝试更改桌面背景。这需要考虑许多特殊因素,因为涉及到“用户上下文”:
  • 首先,您的Web服务器可能以不同的用户身份运行,因此没有权限更改您的桌面。

  • 其次,该程序可能需要一些来自您的用户上下文的环境变量。例如,X程序需要一个DISPLAY变量,ssh-agent需要SSH_AGENT_PIDSSH_AUTH_SOCK等。我对更改背景不是很了解,但我猜它涉及到D-Bus,可能需要像DBUS_SESSION_BUS_ADDRESSKONSOLE_DBUS_SERVICEKONSOLE_DBUS_SESSIONKONSOLE_DBUS_WINDOW这样的东西。可能还有很多其他的变量。请注意,其中一些变量每次登录时都会更改,因此您无法在PHP端硬编码它们。

为了测试,从您的用户会话中启动自己的Web服务器可能更简单。(即不要使用系统自带的,因为它必须在您的身份下运行。您需要在备用端口上运行它,如8080)。您手动启动的Web服务器将拥有它所需的所有“上下文”。我会提到websocketd,因为它刚刚发布,看起来很不错。
对于“生产”,您可能需要始终在用户上下文中运行守护程序,并让Web服务器与该守护程序通信,以在用户上下文中“完成任务”。

对我来说,“桌面”问题是不相关的,因为它被假定为例题的一部分。真正的问题是为什么Linux命令行和PHP执行的Linux命令之间存在不一致性。我认为你提到的第一个观点,即PHP是由一个不同于Linux控制台中通常使用的用户运行的,是最谨慎和重要的观点。为了保持一致性,最好将Linux命令作为运行PHP的用户运行,以进一步模拟PHP的系统调用行为。 - scrowler

2

PHP的system仅返回执行的最后一行:

返回值: 成功时返回命令输出的最后一行, 失败时返回FALSE。

您可能希望使用execpassthruexec具有一个可选参数,用于将输出放入数组中。 您可以将输出implode并使用它来echo它。

switch($_REQUEST['do'])
{ case 'test':
    exec('/usr/local/bin/chbg -mt', $output);
    echo implode('\n', $output); // Could use <br /> if HTML output is desired
    break;
}

1
我认为执行结果可能因用户而异。 首先,尝试在终端中直接运行您的PHP脚本 php yourScript.php
如果按预期运行,请转到Apache服务并更新以使用您自己的凭据运行。

1
您正在尝试更改当前已登录用户的背景...当他们使用桌面时。就像我在打这条消息时一样,我最小化浏览器,'哦,我的桌面背景不同了'。希望这是一些重要的东西,比如当反应堆或过热时变成红色。

无论如何,我的答案是:

不要尝试远程连接并作为各个用户运行项目。设置每个用户在自己的帐户中运行bash脚本(在自己的shell中),以定期计时器运行。比如每10分钟。让它选择相同的文件...从网络位置/somenetworkshare/backgrounds/images/current.png

然后,您可以通过更改/somenetworkshare/backgrounds/images/current.png中的图像本身来更新所有节点(从1到100万)


“一个对多个请求”的缺点是,你引入了百万倍的处理时间,降低了整体性能,而“一对多推送”方法只在必要时才会降级。” - scrowler

1
我之前写了一个可以实现这个功能的东西 -- 你可以运行命令解释器(/bin/sh),发送命令,读取响应,发送更多命令等。它使用proc_open()打开一个子进程并与其通信。

它在http://github.com/andrasq/quicklib,Quick/Proc/Process.php中。

使用它看起来会像这样(如果你有一个灵活的自动加载程序更容易, 我也在 Quicklib 中编写了一个):

include 'lib/Quick/Proc/Exception.php';
include 'lib/Quick/Proc/Exists.php';
include 'lib/Quick/Proc/Process.php';

$proc = new Quick_Proc_Process("/bin/sh");
$proc->putInput("pwd\n");
$lines = $proc->getOutputLines($nlines = 10, $timeoutSec = 0.2);
echo $lines[0];
$proc->putInput("date\n");
$lines = $proc->getOutputLines(1, 0.2);
echo $lines[0];

输出

/home/andras/quicklib
Sat Feb 21 01:50:39 EST 2015
php与进程之间的通信单位是以换行符结尾的行。所有命令都必须以换行符结尾,所有响应都以行为单位检索。不要忘记换行符,否则很难在之后识别。

这更像是你为一个脚本做广告,而不是对问题的回答。 - scrowler
是的,我回复了“我希望PHP能够像Linux中的open terminal命令一样运行”,而没有向下滚动详细阅读其余内容。我认为这可能有所帮助;没有意识到那不是问题的答案。 - Andras

0

我正在开发一个项目,该项目使用机器A上的终端A输出到机器B上的终端B,目前两者均使用Linux。我没有看到相关提及,但也许你可以在Web服务器中使用重定向,类似于以下内容:

switch($_REQUEST['do'])
 { case 'test':
    #process ID on the target (12345, 12346 etc)
    echo system('/usr/local/bin/chbg -mt > /proc/<processID>/fd/1'); 
    #OR
    #device file on the target (pts/0,tty0, etc) 
    echo system('/usr/local/bin/chbg -mt > /dev/<TTY-TYPE>/<TTYNUM>');
    break;
 }

肯定需要正确设置权限才能使其正常工作。在终端中使用命令“mesg y”也可能有所帮助...希望这可以帮到你。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接