如何检测Console.In(标准输入)是否已被重定向?

34
我想编写一个命令行应用程序,根据输入来源(比如键盘或文件)有不同的行为。
这是否可能?在C#中最优雅的方式是什么?
4个回答

59

您可以通过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


3
以上代码是为.NET 4.5编写的,其中包括属性IsErrorRedirected、IsInputRedirected和IsOutputRedirected。 - Ron Jacobs
1
有趣的实现,不幸的是它不适用于跨平台开发。 - Lorenz Lo Sauer
isatty()函数在其他平台上也常用于类似的测试。 - Hans Passant

8
有趣的是,当管道打开时,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 我不是太确定。首先,完全有可能重定向输入和输出,而你的代码将无法检测到这一点。 - jpbochi
1
我正在使用Debian Wheezy操作系统。0 == (Console.WindowHeight + Console.WindowWidth)这个技巧对我很有效。例如,当我的应用程序通过monit启动时,总和为0。当手动从命令提示符启动时,它是一个正数。在无控制台运行时,KeyAvailable返回false,但如果我实际调用ReadKey,则会返回零。 - BrandonLWhite
我可以确认这似乎在Mac上可行。尽管Mono在Xamarin Studio中报告“警告:Mono限制:仅适用于Windows”,但我无法确定“Windows”是否拼写错误(大写W),或者该错误是说它不能在调试器中工作。 - Mads Y
这段代码不安全,Console.KeyAvailable在某些情况下当输入被重定向时可能会抛出InvalidOperationException异常--因此仅仅检查该属性就可能导致应用程序崩溃。 - gallivantor

4
Lorenz的回答是一个很好的开始,但不幸的是只能用作灵感。有更多的模式可以运行控制台应用程序。
  1. 标准运行(在控制台上,没有任何重定向)

    在控制台中一切正常。

  2. 从控制台执行带有标准输入和/或标准输出重定向的命令

    例如

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

    (在Linux中将type替换为cat)

    或者

    application.exe | grep pattern 或者 application.exe >output_file.txt进行输出重定向

    或者

    type input_file.txt | application.exe | grep pattern或者application.exe <input_file.txt >output_file.txt进行输入和输出重定向

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

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

  4. 从GUI应用程序中执行带有隐藏控制台和重定向输入/输出/错误的命令(控制台完全不可见)

  5. 从隐藏控制台执行没有重定向输入/输出/错误的命令

每种模式都有其自己的“特点”。对于第1和第2种模式,Console.WindowHeightConsole.WindowWidthWindows中按标准方式工作。在Linux中,第2种和第3种模式的返回值为零。因此,在Linux中,您无法检测到仅输入重定向。
因此,Lorenz回答中的代码不能用于检测所有情况下的重定向。当没有输出到控制台时(例如第3种模式),只有在Windows中才会抛出读取Console.WindowHeightConsole.WindowWidth时的IOException异常。
要检测输入重定向(仅限于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),因为当您错误地期望重定向但是重定向未生效时,可能会导致更多麻烦。


1
这是目前为止唯一对我有效的解决方案。使用C#的.NET Core 3.1。谢谢! - Technoguyfication

4

还适用于.NET 5和6,这真是太棒了。谢谢! - Per Lundberg

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