在C中调用系统函数会导致它修复ANSI颜色吗?

huangapple go评论162阅读模式
英文:

Calling to system function in C causes it to fix ANSI colors?

问题

我已经在命令行中制作工具有一段时间了。然而,当我回顾一些旧项目时,有一件事引起了我的注意:

// 我绝对需要这个,但我不知道为什么!!!!
system("Title Something");

因此,我决定进行调查。当我将它移除并在模拟环境中再次运行工具时(具体来说是在VSCodes模拟控制台中),一切似乎都很正常。然而,当我尝试在cmd中运行它时,我发现我之前在代码中使用的所有颜色都失效了。

所以我打开了一个临时项目,就像这样:

#include <stdio.h>
int main(){
  printf("3[1;31mI am red!3[0m");
}

当我运行它时,我得到了:

C:\tests\ANSI-test>hello
←[1;31mI am red!←[0m

这与我之前使用我的其他工具时得到的情况相似。因此,我重新添加了看似随机的 system("Title Something"); 行(确保我包括了 stdlib.h):

#include <stdio.h>
#include <stdlib.h>
int main(){
  system("Title Something");
  printf("3[1;31mI am red!3[0m");
}

然后,颜色又正常工作了!:

C:\tests\ANSI-test>hello
I am red!                                                   (假装这是红色的)

我不确定到底是什么原因导致了这个情况,我有一些猜测:

  • system 函数初始化了与控制台有关的某些内容,从而触发了 ANSI 颜色的工作
  • system 以某种方式调用了 WinAPI,并告诉它在终端内部使用颜色

我尝试搜索这个问题,但除了这个链接之外,我没有找到与之相关的内容:

https://stackoverflow.com/questions/16755142/how-to-make-win32-console-recognize-ansi-vt100-escape-sequences-in-c#:~:text=Starting%20from%20Windows%2010%20TH2,they%20have%20to%20be%20enabled)。

英文:

I've been making tools for the command line for a little while now. However, one thing that stuck out to me when I was going over some old projects was this:

// I ABSOLUTELY NEED THIS BUT IDK WHY!!!!
system(&quot;Title Something&quot;);

So I decided to investigate. When I removed it and ran the tool again in a simulated environment (more specifically VSCodes simulated console) everything seemed fine. However when I tried running it in cmd I saw that all of the colors that I had previously used in my code where broken.

So I pulled up a temporary projects like this one:

#include &lt;stdio.h&gt;
int main(){
  printf(&quot;\033[1;31mI am red!\033[0m&quot;);
}

And when I ran it I got:

C:\tests\ANSI-test&gt;hello
←[1;31mI am red!←[0m

Which was similar to what I had previously gotten with my other tool. And so I added back the seemingly random system(&quot;Title Something&quot;); line (making sure I included stdlib.h):

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
int main(){
  system(&quot;Title Something&quot;);
  printf(&quot;\033[1;31mI am red!\033[0m&quot;);
}

And I got color working again!:

C:\tests\ANSI-test&gt;hello
I am red!                                                   (pretend this is red)

I'm not sure exactly what causes this, I have a couple of speculations:

  • the system function initializes something to do with the console, which triggers the working of ANSI colors
  • system somehow calls to WinAPI and tells it to use colors inside the terminal

I did try searching for this but I didn't really find anything related to it other than this:

https://stackoverflow.com/questions/16755142/how-to-make-win32-console-recognize-ansi-vt100-escape-sequences-in-c#:~:text=Starting%20from%20Windows%2010%20TH2,they%20have%20to%20be%20enabled).

答案1

得分: 3

有趣的发现。根据我所能了解的情况,system() 似乎设置了 ENABLE_VIRTUAL_TERMINAL_PROCESSING 标志(我不确定它是在分叉之前还是之后执行的,但仔细考虑后,我认为这并不重要,因为它是为整个控制台句柄设置的)。我觉得这很有趣,因为这似乎不是该标志的默认设置(新的 cmd.exe 启动时禁用它,所以从技术上讲是自选的)。我真的不确定为什么会有这种行为意图,但你可以通过运行以下扩展示例来查看这种情况,使用 0-2 个参数:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;windows.h&gt;
int main()
{
    // Get handle of current stdout
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD dwMode = 0;

    if (__argc == 2) {
        printf("Running with system call\n");
        system("Title Something");
    }
    else if (__argc == 3) {
        printf("Running with console flag set\n");
        GetConsoleMode(hOut, &dwMode);
        dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        SetConsoleMode(hOut, dwMode);
    }

    // Check if the flag is set before printing
    GetConsoleMode(hOut, &dwMode);
    if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
        printf("Virtual terminal processing is enabled!\n");
    }
    printf("3[1;31mI am red!3[0m");
}

从中你也可以看出如何避免使用 system() 来设置虚拟控制台标志,而是直接设置它。不幸的是,这种特定行为并没有很好地文档化,但我希望这能帮助澄清这个问题。

英文:

Interesting find. From what I am able to gather, system() seems to set the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag (I am not sure if it does this before or after forking, but thinking about it more, I don't think this would matter since its setting it for the entire console handle). I find it interesting because this does not seem to be the default for that flag (new cmd.exe launch with it disabled, so its technically opt-in). Im really not sure why this behavior would be intended, but you can see that it is the case by running the following expanded example with 0-2 args:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;windows.h&gt;
int main()
{
    // Get handle of current stdout
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD dwMode = 0;

    if (__argc == 2) {
        printf(&quot;Running with system call\n&quot;);
        system(&quot;Title Something&quot;);
    }
    else if (__argc == 3) {
        printf(&quot;Running with console flag set\n&quot;);
        GetConsoleMode(hOut, &amp;dwMode);
        dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        SetConsoleMode(hOut, dwMode);
    }

    // Check if the flag is set before printing
    GetConsoleMode(hOut, &amp;dwMode);
    if (dwMode &amp; ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
        printf(&quot;Virtual terminal processing is enabled!\n&quot;);
    }
    printf(&quot;3[1;31mI am red!3[0m&quot;);
}

I think from that you can also see how to avoid using system() to kludge the virtual console flag and just directly set it. Its unfortunate that this particular behavior is not well documented but I hope this at least helps clear it up.

答案2

得分: 1

我认为这是一个非常有趣的问题,实际上我在查找时并没有找到提供很好见解的内容。然而,可能值得一提的是:

CPP-Reference上,他们说:

> 使用参数command调用主机环境的命令处理器。返回一个实现定义的值(通常是调用程序返回的值)。

因此,似乎system()函数的调用完全是“实现定义的”。

请注意,他们在下面的示例中说“可能的输出”:

#include &lt;stdlib.h&gt;
     
int main(void) {
    system(&quot;date +%A&quot;);
    system(&quot;gcc --version&quot;);
}
    
可能的输出:
    
Wednesday
gcc (GCC) 11.2.0
...

cplusplus.com上,他们提供了类似的声明:

> 调用命令的效果取决于系统和库的实现,可能会导致程序以非标准方式运行或终止。

因此,我得出结论:

system()函数调用操作系统的shell,而操作系统的shell可能具有其自己的初始化例程

关于Windows控制台环境的初始化,行为可能受到各种因素的影响,包括Windows的具体版本和命令提示符的配置设置

我认为关于系统函数的初始化过程并没有很好的文档记录。希望这有所帮助(尽管我自己也不完全确定)。

英文:

I think that is a really interesting question and I actually didn't find anything that gives great insights while I was digging. However, there might be some things worth mentioning:

On CPP-Reference they say the following:

> Calls the host environment's command processor with the parameter
> command. Returns an implementation-defined value (usually the value
> that the invoked program returns).

So it seems that the invocation of the system() function is entirely implementation-defined.

Note how they say &quot;possible output&quot; in the example below:

#include &lt;stdlib.h&gt;
 
int main(void) {
    system(&quot;date +%A&quot;);
    system(&quot;gcc --version&quot;);
}

Possible output:

Wednesday
gcc (GCC) 11.2.0
...

On cplusplus.com they give a similar statement:

> The effects of invoking a command depend on the system and library
> implementation, and may cause a program to behave in a non-standard
> manner or to terminate.

So I conclude:

The system()-function invokes the shell of the operating system, which in turn may have its own initialization routines.

Regarding the initialization of the console environment in Windows, the behavior can be influenced by various factors, including the specific version of Windows and the configuration settings of the command prompt.

I don't think that the initialization process is well-documented in relation to the system function. I hope this helps (even though I'm not entirely sure myself).

huangapple
  • 本文由 发表于 2023年7月13日 00:04:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76672506.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定