isatty() 如何从终端获取信息?

now*_*wox 5 linux bash

我注意到如果我输入:

ls --color=auto
Run Code Online (Sandbox Code Playgroud)

或者

ls --color=auto | cat
ls --color=auto > >(cat)
Run Code Online (Sandbox Code Playgroud)

我没有看到相同的输出。因此我假设一个程序有能力知道它的 STDOUT 是否通过管道传输到某个东西。问题是它是怎么知道的?

我查了ENV VAR提供envenv | cat,但我得到的是相同的。答案在别处。我想不通在哪里。

从这个问题我发现我可以使用isatty()谁提供这个功能?它是外壳还是内核的一部分?通过进一步我看到这个函数是 POSIX 标准的一部分。

现在我知道我有两个进程可以通过几种机制在它们之间进行通信:

  • 标准输入/标准输出/STDERR
  • 退出代码
  • 环境变量
  • 系统调用

Bash 和 ls 都是程序。因此他们只能使用列出的机制来交换信息。

这背后的真正问题是如何isatty()从 bash 获取信息?

Ken*_*ter 7

--color选项是 GNUls程序的一个特性。GNUls使用该isatty()函数来测试进程的标准输出是否为 TTY。部分相关源代码可以在这里看到:

    case COLOR_OPTION:
      {
        int i;
        if (optarg)
          i = XARGMATCH ("--color", optarg, color_args, color_types);
        else
          /* Using --color with no argument is equivalent to using
             --color=always.  */
          i = color_always;

        print_with_color = (i == color_always
                            || (i == color_if_tty
                                && isatty (STDOUT_FILENO)));
Run Code Online (Sandbox Code Playgroud)

这背后的真正问题是 isatty() 如何从 bash 获取信息?

isatty()检查传递给它的文件描述符以查看文件描述符是否代表 TTY(终端设备)。isatty() 的确切工作方式可能因系统而异。如果您有兴趣,这里有一个来自 Apple OSX的Darwin 实现

#include <termios.h>
#include <unistd.h>

int
isatty(fd)
    int fd;
{
    struct termios t;

    return(tcgetattr(fd, &t) != -1);
}
Run Code Online (Sandbox Code Playgroud)

当您运行 时ls --color=auto,您的外壳程序 (bash) 使用外壳程序自己的标准输入、输出和错误作为“ls”进程的 stdin/out/err 启动“ls”程序。如果您以交互方式运行,那么您的 shell 的标准输出可能是一个终端,那么 ls 的标准输出可能是一个终端。当 ls 调用 isatty() 来测试它的标准输出是否是终端时,它可能会成功。

当你运行类似的东西时ls --color=auto | cat,你的 shell 会做三件事:

  1. 创建管道。
  2. 启动cat时将其标准输入设置为管道。
  3. 启动ls时将其标准输出设置为管道。

管道不是终端。当ls测试其标准输出是否为 tty 时,测试将失败。