C#: 运行Shell命令问题

3

我正在尝试让PHP解析器运行一个页面,然后将结果返回给我的服务器,但是当我通过我的代码运行该命令时,它没有返回任何结果。我知道该命令是正确的,因为如果我手动使用相同的路径运行它,它可以正常工作。以下是我的代码:

var p = new Process
{
      StartInfo = new ProcessStartInfo("C:\\xampp\\php\\php.exe", path)
      {
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false,
            CreateNoWindow = true
      }
};
var output = new StringWriter();
var error = new StringWriter();
p.OutputDataReceived += (sender, args) => output.WriteLine(args.Data);
p.ErrorDataReceived += (sender, args) => error.WriteLine(args.Data);
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
p.WaitForExit();
if (p.ExitCode != 0)
{
      throw new Exception(string.Format(
          "PHP failed with the following output:{0}{1}",
      /* {0} */ Environment.NewLine,
      /* {1} */ error.GetStringBuilder().ToString()));
}
var res = output.GetStringBuilder().ToString();
Console.WriteLine(res);

编辑: 使用当前代码,它会在没有输出的情况下抛出异常。


也许如果您详细解释您尝试做什么,我们可以找到更好的解决方案。 - friol
当你说“它没有返回任何东西”时,是指在进程运行后 res 是空的,还是它根本没有返回? - Atif Aziz
抱歉,返回一个空字符串。 - The.Anti.9
你尝试读取标准错误了吗?也许有一些输出在那里等着呢? - Atif Aziz
6个回答

5

设置工作目录路径

var p = new Process
            {
                StartInfo = new ProcessStartInfo("php", path)
                {
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    WorkingDirectory = workingDir
                }
            };

在执行p.Start()时抛出异常,错误信息为:“系统找不到指定的文件”。 - The.Anti.9
你能详细说明一下你遇到的错误吗?如果找不到php.exe,那么尝试使用完整路径,例如:StartInfo = new ProcessStartInfo("C:\xampp\php\php.exe", path)。 - Vikram Sudhini

5
问题在于您在程序完成之前就阅读了输出。需要等待程序退出以处理输出。否则,您将在解析完成之前处理输出。添加以下行。
p.Start();
p.WaitForExit();  // New line

编辑: 发帖者表示仍然存在问题。

尝试去掉命令中的CMD部分,直接运行PHP命令。为了调试方便,允许创建一个窗口以便查看运行命令时可能出现的错误信息。


嗯,我尝试删除cmd部分,但是它会给我一个异常,说“文件名、目录名或卷标语法不正确”。 - The.Anti.9

2
最可靠的调用进程并捕获其输出或错误的方法是尝试以下方式:
var p = new Process {
    StartInfo = new ProcessStartInfo("php", path) {
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        UseShellExecute = false,
        CreateNoWindow = true
    }
};
var output = new StringWriter();
var error = new StringWriter();
p.OutputDataReceived += (sender, args) => output.WriteLine(args.Data);
p.ErrorDataReceived += (sender, args) => error.WriteLine(args.Data);
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
p.WaitForExit();
if (p.ExitCode != 0) {
    throw new Exception(string.Format(
        "PHP failed with the following output:{0}{1}",
        /* {0} */ Environment.NewLine,
        /* {1} */ error.GetStringBuilder().ToString()));
}
var res = output.GetStringBuilder().ToString();
Console.WriteLine(res);

好的,我试过了,我不得不编辑命令部分。将其更改为"cmd","/c php " + path。然后我得到了以下错误输出:"‘php’ 不是内部或外部命令,也不是可运行的程序或批处理文件。"但奇怪的是,如果我打开命令提示符并运行它,php命令是有效的... - The.Anti.9
尝试将php可执行文件的完整绝对路径作为ProcessStartupInfo构造函数的第一个参数进行指定。使用“cmd”是过度设计。 - Atif Aziz
好的,我尝试了这个,它抛出了异常并输出了空白。 - The.Anti.9

1

ReadToEnd() 只会读取流中当前时刻的所有内容。考虑到您在 Start() 之后立即调用此方法,流很可能是空的。您有几个选择:

  • 改为调用 ReadLine(),但这只会读取一行
  • p.WaitForExit() 然后再调用 ReadToEnd(),但我不确定这是否会保持流处于打开状态。如果可以的话,这是您最好的选择。

1
你需要在 p.Start() 之后添加 p.WaitForExit(); 否则当你尝试读取输出时它还没有准备好。

0

你为什么要通过cmd运行php?难道不能直接执行吗?

根据我的经验,通过.NET执行另一个进程是最糟糕的方式。在Unix下使用fork()会好得多。我遇到了很多问题,所以我的建议是你强烈考虑使用Process.BeginOutputReadLine()并采用异步读取的方式。如果你需要捕获大量输出,ReadToEnd和ReadToLine可能会无限期地挂起。

我可以贴一个例子,但它相当冗长。


我想尝试一下,但在我看来,MSDN上的示例相当差,你能否请发一下你的示例? - The.Anti.9
我编写的类正在发挥作用。在此期间,要点是一个类,它将Process.OutputDataReceived设置为一个类静态方法,该方法将字符串连接到一个不断增长的私有类字符串中。执行进程完成后,您可以通过公共属性访问该字符串。您可以对stderr和stdout执行相同的操作。实际上,这就是Process应该具备的功能。 :) - Colin Burnett

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