首页 > 解决方案 > Windows 和 Linux 的 Process.Start() 区别

问题描述

Process.Start()在 .NET Core 2.2 项目中遇到了一些差异。您可以在此要点中找到完整的源代码:https ://gist.github.com/jchristn/5a2a301baedeed787a2e57cd528e46d6

我有一种启动流程的方法:

private static void ExecuteShell(
    string filename, 
    string args, 
    bool useShellExecute,
    bool redirectStdOut,
    bool redirectStdErr,
    out int returnCode, 
    out string consoleOutput)
{
    returnCode = 0;
    consoleOutput = null;

    if (String.IsNullOrEmpty(filename)) throw new ArgumentNullException(nameof(filename));

    // fileName, i.e. "cmd.exe"
    // args, i.e.     "/c dir /w"

    Process process = new Process();
    process.StartInfo.FileName = filename;
    process.StartInfo.Arguments = args;
    process.StartInfo.UseShellExecute = useShellExecute;
    process.StartInfo.RedirectStandardOutput = redirectStdOut;
    process.StartInfo.RedirectStandardError = redirectStdErr;
    process.Start();
    if (process.StartInfo.RedirectStandardOutput) consoleOutput = process.StandardOutput.ReadToEnd();
    process.WaitForExit();
    returnCode = process.ExitCode;
}

调用者看起来像这样:

while (true)
{
    try
    {
        Console.WriteLine("");
        Console.WriteLine("Example: cmd.exe /c dir /w");
        Console.Write("Command: ");
        string userInput = Console.ReadLine();
        if (String.IsNullOrEmpty(userInput)) break;

        string[] parts = userInput.Split(new char[] { ' ' }, 2);

        string filename = parts[0];
        string arg = null;
        if (parts.Length > 1) arg = parts[1];

        bool useShellExecute = InputBoolean("  Use shell execute  : ", false);
        bool redirectStdOut =  InputBoolean("  Redirect stdout    : ", false);
        bool redirectStdErr =  InputBoolean("  Redirect stderr    : ", false);

        int returnCode;
        string consoleOutput;

        ExecuteShell(
            filename,
            arg,
            useShellExecute,
            redirectStdOut,
            redirectStdErr,
            out returnCode, out consoleOutput);

        Console.WriteLine("Return code    : " + returnCode);
        Console.WriteLine("Console output : " + consoleOutput);
    }
    catch (Exception e)
    {
        Console.WriteLine("Exception: " + Environment.NewLine + SerializeJson(e, true));
    }
}

重现我遇到的更大问题的最简单方法是这个。假设文件系统上有一个文件,我想要type testfile > testfile2,即管道到另一个文件。

在 Windows 上,如果我使用cmd.exe /c type testfile > testfile2它效果很好(三个布尔值设置为false)。

IE

C:\Code\ExecuteShell\ExecuteShell\bin\Debug\netcoreapp2.2>dotnet ExecuteShell.dll

Example: cmd.exe /c dir /w
Command: cmd.exe /c type testfile > testfile2
  Use shell execute  :  [y/N]?
  Redirect stdout    :  [y/N]?
  Redirect stderr    :  [y/N]?
Return code    : 0
Console output :

Example: cmd.exe /c dir /w
Command:
C:\Code\ExecuteShell\ExecuteShell\bin\Debug\netcoreapp2.2>dir
 Volume in drive C is OS
 Volume Serial Number is 72E2-466A

 Directory of C:\Code\ExecuteShell\ExecuteShell\bin\Debug\netcoreapp2.2

... portions removed ...
04/09/2020  05:41 PM                15 testfile
04/09/2020  05:41 PM                15 testfile2

当我在 Ubuntu 14.04 上尝试这个时,它失败了。

~/code/ExecuteShell/ExecuteShell/bin/Debug/netcoreapp2.2/publish$ dotnet ExecuteShell.dll

Example: cmd.exe /c dir /w
Command: cat testfile > testfile2
  Use shell execute  :  [y/N]?
  Redirect stdout    :  [y/N]?
  Redirect stderr    :  [y/N]?
Hello, world!

cat: >: No such file or directory
cat: testfile2: No such file or directory
Return code    : 1
Console output :

如果我尝试使用useShellExecuteset to true,我会遇到这个奇怪的xdg-open问题:

~/code/ExecuteShell/ExecuteShell/bin/Debug/netcoreapp2.2/publish$ dotnet ExecuteShell.dll

Example: cmd.exe /c dir /w
Command: cat testfile > testfile2
  Use shell execute  :  [y/N]? y
  Redirect stdout    :  [y/N]?
  Redirect stderr    :  [y/N]?
xdg-open: unexpected argument 'testfile'
Try 'xdg-open --help' for more information.
Return code    : 1
Console output :

知道如何进行这项工作吗?

标签: c#linuxwindows.net-core

解决方案


在这种情况下,您会将“shell”一词在其 Unix 上下文(命令行解释器)中的使用与它的 Windows 和C# 使用混淆:

在此上下文中,“shell”一词 (UseShellExecute) 指的是图形 shell(类似于 Windows shell)而不是命令 shell(例如 bash 或 sh),它允许用户启动图形应用程序或打开文档。

所以useShellExecute实际上意味着你可以给程序一个某种类型的文档,并期望它被合适的程序打开。就是这样xdg-open做的,所以这可能就是 C# 调用它的原因。

在您的情况下,您要运行的命令是sh -c 'cat testfile > testfile2'. 这相当于你的cmd调用。但是,如果您现在这样做,您的代码将无法工作,因为您在空格上进行了拆分。所以你最终会得到参数sh, -c, 'cat, testfile, >, 和testfile2'。不同cmd的是,它负责自己的参数处理,sh不会将其命令与空格连接起来,这是行不通的。

如果您将其作为参数数组传递,您希望您的参数为sh,-ccat testfile > testfile2; 也就是说,您要传递给 shell 的整个字符串应该是一个完整的参数。


推荐阅读