我在使用 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) 进行测试
我看到了与您描述的相同的行为
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
,但我认为可以遵循类似的过程。