【问题标题】:Why Process.Start produces ansi escape codes为什么 Process.Start 会产生 ansi 转义码
【发布时间】:2021-10-16 22:40:27
【问题描述】:

我在使用 System.Diagnostics.Process 时遇到了一个奇怪的问题(在 linux 系统上)。

每次启动进程时都会输出 ANSI 转义序列。
序列是<ESC>[?1h<ESC>=(DECCKM, DECKPAM),但这不是被调用程序产生的,我在这里使用了pwd,但是序列也是用任何其他程序创建的。

序列似乎是在进程之外生成的,因为标准和错误输出流无法获取它。

奇怪的是,它只发生在我启动虚拟主机之后!

我构建了一个最小的代码示例。

using System;
using System.Diagnostics;
using System.Threading;

using Microsoft.Extensions.Hosting;

class Program
{
    static int count;
    static void Main(string[] args)
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

        execTest();

        Host.CreateDefaultBuilder().Build().RunAsync(cancellationTokenSource.Token);
        Thread.Sleep(2000);

        execTest();
        cancellationTokenSource.Cancel();
        Thread.Sleep(2000);

        execTest();
    }

    private static void execTest()
    {
        for (var i = 0; i < 3; i++)
        {
            executeCommand("pwd");
            Console.WriteLine($"Exectest {++count}");
        }
    }

    static void executeCommand(string cmd)
    {
        var process = new Process();
        process.StartInfo.FileName = cmd;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardError = true;
        process.Start();
        process.WaitForExit();
    }
}

要查看效果,您需要特殊的日志记录、管道或重定向来禁用效果。
当 xterm 解释转义序列时,它们是不可见的。

因此我使用script -c myTestProg output.log; cat -A output.log

在输出中您可以看到,前三个 ExecuteTests 按预期工作,但 Exectest 4 到 9 产生了这个意外输出。

Exectest 1
Exectest 2
Exectest 3
^[[?1h^[=^[[40m^[[32minfo^[[39m^[[22m^[[49m: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
^[[40m^[[32minfo^[[39m^[[22m^[[49m: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
^[[40m^[[32minfo^[[39m^[[22m^[[49m: Microsoft.Hosting.Lifetime[0]
      Content root path: /home/jeb/xx/csharp-test
^[[?1h^[=Exectest 4
^[[?1h^[=Exectest 5
^[[?1h^[=Exectest 6
^[[40m^[[32minfo^[[39m^[[22m^[[49m: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
^[[?1h^[=Exectest 7
^[[?1h^[=Exectest 8
^[[?1h^[=Exectest 9

我的主要问题是我的程序使用 Microsoft.Hosting 并每秒调用一个进程,这导致我的日志文件泛滥。 即使我通过

禁用主机的输出日志记录
.ConfigureLogging(loggingBuilder =>
{
  loggingBuilder.ClearProviders();
});

问题依然存在。

导致输出的原因以及如何避免或抑制输出?
System.Diagnostics.Process 和 Microsoft.Extensions.Hosting 之间的关系在哪里?

PS:序列是在Process.Start()之后产生,调用程序结束,但是在调用WaitForExit之前,用一些Thread.Sleep(100)测试

【问题讨论】:

标签: c# rest process output


【解决方案1】:

我看到你用netcoreapp3.1 描述的相同行为。

然而,从net5.0 开始,似乎所有 调用Console.WriteLine 在Linux 上首先写入DECCKM|DECPAM 序列,无需先Host.CreateDefaultBuilder()

我无法找到为什么旧运行时版本仅在 Host.CreateDefaultBuilder() 之后触发该行为 - 可能与 ILoggerFactory 的设置有关。

这些转义启用 DEC VT100 Cursor key modeKeypad Application Mode

我想这样做是为了让没有专用光标 / PgUp / PgDn 键的键盘可以使用小键盘。

在 ubuntu 1804 上,您可以使用 infocmp -i 查看为当前终端配置的转义序列:

smkx: {DEC+CKM}{DECPAM}

阻止这种情况发生的一个简单方法是降低终端的能力级别

export TERM=ansi

netcore 应用将读取终端功能并跳过不受支持的发射序列。

更有针对性的方法是从当前终端定义中删除违规序列:

infocmp | sed 's/smkx=\\E\[?1h\\E=,//g' | sed 's/$TERM/nosmkx/g' > ~/infocmp.mod
TERM=nosmkx; export TERM
tic -o ~ ~/infocmp.mod
TERMINFO=~/n/nosmkx; export TERMINFO

有些系统使用TERMCAP 而不是TERMINFO,但我认为可以在那里遵循类似的过程。

【讨论】:

  • 谢谢,两种解决方案都有效。但是,我仍然不明白为什么微软认为这是一个好主意
  • 据我所知,它与VT-100 兼容。我敢肯定有人会为 .net5.0 中默认启用此功能而感到兴奋!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-08
  • 2016-07-23
  • 1970-01-01
  • 2021-04-17
  • 1970-01-01
  • 1970-01-01
  • 2021-07-24
相关资源
最近更新 更多