为什么 Process.Start 会产生 ansi 转义码

问题描述 投票:0回答:1

我在使用 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

在输出中您可以看到,前三个 ExecuteTest 按预期工作,但 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 ansi-escape
1个回答
4
投票

我看到了与您描述的相同的行为

netcoreapp3.1

但是,从

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

我找不到为什么较旧的运行时版本仅在

Host.CreateDefaultBuilder()
之后才触发该行为 - 也许与
ILoggerFactory
的设置有关。

这些转义符启用 DEC VT100

Cursor key mode
Keypad 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
,但我认为可以遵循类似的过程。

© www.soinside.com 2019 - 2024. All rights reserved.