【问题标题】:How to detect if Console.In (stdin) has been redirected?如何检测 Console.In (stdin) 是否已被重定向?
【发布时间】:2011-03-28 01:30:30
【问题描述】:

我想编写一个具有不同行为的控制台应用程序,具体取决于输入是来自键盘还是来自文件。

有可能吗?在 C# 中最优雅的方式是什么?

【问题讨论】:

    标签: c# console redirect stdin


    【解决方案1】:

    您可以通过 p/调用 Windows FileType() API 函数来查找。这是一个帮助类:

    using System;
    using System.Runtime.InteropServices;
    
    public static class ConsoleEx {
        public static bool IsOutputRedirected {
            get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stdout)); }
        }
        public static bool IsInputRedirected {
            get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stdin)); }
        }
        public static bool IsErrorRedirected {
            get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stderr)); }
        }
    
        // P/Invoke:
        private enum FileType { Unknown, Disk, Char, Pipe };
        private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 };
        [DllImport("kernel32.dll")]
        private static extern FileType GetFileType(IntPtr hdl);
        [DllImport("kernel32.dll")]
        private static extern IntPtr GetStdHandle(StdHandle std);
    }
    

    用法:

    bool inputRedirected = ConsoleEx.IsInputRedirected;
    

    更新:这些方法已添加到 .NET 4.5 的 Console 类中。如果没有归属,我可能会添加:(只需使用相应的方法而不是这个帮助类。

    https://msdn.microsoft.com/en-us/library/system.console.isoutputredirected.aspx https://msdn.microsoft.com/en-us/library/system.console.isinputredirected.aspx https://msdn.microsoft.com/en-us/library/system.console.iserrorredirected.aspx

    【讨论】:

    • 上面的代码适用于 .NET 4.5,现在包括属性 IsErrorRedirected、IsInputRedirected 和 IsOutputRedirected。
    • 有趣的实现,可惜不适用于跨平台开发。
    • isatty() 函数往往可以在其他平台上进行类似的测试。
    【解决方案2】:

    有趣的是,当管道打开时,System.Console.WindowHeightSystem.Console.WindowWidth 参数是,我发现这是由于代码路径中的几个ArgumentOutOfRangeException 不关心控制台大小为零。

    跨平台:在 Linux 和 Windows 上 MS dotNETMono 下的行为是相同的(我没有在 Mac 上尝试过)。

    当使用管道传输 STDIN 或 STDOUT 时,控制台大小设置为 0。因此基于 Hans 的实现,我的代码如下:

    using System;
    
    
     public static class ConsoleEx {
            public static bool IsConsoleSizeZero {
                get {
                    try {
                        return (0 == (Console.WindowHeight + Console.WindowWidth));
                    }
                    catch (Exception exc){
                        return true;
                    }
                }
            }
            public static bool IsOutputRedirected {
                get { return IsConsoleSizeZero && !Console.KeyAvailable; }
            }
            public static bool IsInputRedirected {
                get { return IsConsoleSizeZero && Console.KeyAvailable; }
            }
    }
    

    2016 年更新: 向IsConsoleSizeZero 代码添加了异常处理,以提高代码在更广泛的上下文中的可用性。

    代码似乎仍然运行良好,至少从使用 MonoDevelop / Xamarin Studio 的经验来看。

    相关

    【讨论】:

    • Console.WindowHeight 不错!不过,我不太确定Console.KeyAvailable 的使用。一方面,完全可以同时重定向输入和输出,而您的代码将无法检测到这一点。
    • 我在 Debian Wheezy 上运行。 0 == (Console.WindowHeight + Console.WindowWidth) 技巧对我有用。例如,当我的应用程序通过 monit 启动时,总​​和为 0。当从命令提示符手动启动时,它是一个正数。无控制台运行时,KeyAvailable 返回 false,但如果我实际调用 ReadKey,则返回零。
    • 我可以确认这似乎适用于 Mac。虽然 Mono 在 Xamarin Studio 中报告“警告:Mono LIMITATION: Only works on windows”。我不知道“Windows”是否拼写错误(大写 W),或者错误是说它在调试器中不起作用。
    • 这段代码不安全,Console.KeyAvailable 在某些情况下当输入被重定向时会抛出 InvalidOperationException —— 所以仅仅检查该属性可能会使应用程序崩溃
    【解决方案3】:

    Lorenz 的回答是一个好的开始,可惜只能作为一个灵感。运行控制台应用程序的模式更多。

    1. 标准运行(在控制台中,没有任何重定向)

      在控制台中一切正常。

    2. 使用标准输入和/或标准输出重定向从控制台执行重定向

      例如

      type input_file.txt | application.exe(在 Windows 中),或 application.exe <input_file.txt 用于输入重定向

      (在 Linux 中将 type 替换为 cat

      application.exe | grep patternapplication.exe >output_file.txt 用于输出重定向

      type input_file.txt | application.exe | grep patternapplication.exe <input_file.txt >output_file.txt 用于输入和输出重定向

    3. 从带有标准输出的控制台重定向执行错误输出重定向

      例如application.exe >output_file.txt 2>error_file.txt

    4. 使用隐藏控制台执行并重定向输入/输出/错误

      例如来自 GUI 应用程序(控制台根本不可见)

    5. 使用隐藏控制台执行没有输入/输出/错误重定向

    每种模式都有自己的“功能”。 Console.WindowHeightConsole.WindowWidthWindows 中以标准方式用于第一和第二模式。在 Linux 中,第 2 和第 3 模式的返回值是 0。因此,在 Linux 中,您不能仅检测输入重定向。

    因此,Lorenz answer 中的代码不能在所有情况下都用于检测重定向。读取Console.WindowHeightConsole.WindowWidth 时的IOException 仅在(例如第3 模式)无输出 且仅适用于 时抛出>Windows

    要检测输入重定向(仅在Windows中)使用这个函数:

    private static bool IsInputRedirected()
    {
      try
      {
        if (Console.KeyAvailable)
        {
          return (false);
        }
      }
      catch (InvalidOperationException)
      {
        return (true);
      }
      return (false);
    }
    

    对于所有其他重定向和操作系统...尝试尝试如何检测它们。不同的控制台属性和函数'工作'(抛出异常,或零返回值)用于不同的模式。

    Windows 7 .NET Framework 4 Client ProfileMono JIT compiler version 4.2.1 (Debian 4.2.1.102+dfsg2-7ubuntu4) 上测试。

    重要提示:

    不要在 Linux 中使用此功能进行输入重定向(检测正在运行的操作系统/平台,例如 Windows 的 Mono),因为当您错误地期望重定向并且重定向未激活时,它可能会导致更多麻烦.

    【讨论】:

    • 这是迄今为止唯一对我有用的解决方案。 .NET Core 3.1 使用 C#。谢谢!
    【解决方案4】:

    由于框架 4.5 存在属性 Console.IsInputRedirected。 8-)

    请参阅 Microsoft Docs:
    https://docs.microsoft.com/en-us/dotnet/api/system.console.isinputredirected

    【讨论】:

    • 也适用于 .NET 5 和 6,这非常好。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-05
    • 2017-07-18
    • 2023-03-29
    相关资源
    最近更新 更多