我注意到如果我输入:
ls --color=auto
或者
ls --color=auto | cat
ls --color=auto > >(cat)
我没有看到相同的输出。因此我假设程序有能力知道其 STDOUT 是否通过管道传输到某个东西。问题是它如何知道?
env
我使用和检查了 ENV 变量env | cat
,但得到的结果相同。答案在其他地方。我搞不清楚在哪里。
由此问题我发现我可以使用isatty()
谁提供这个函数?它是 shell 还是内核的一部分?通过进一步研究,我发现这个函数是 POSIX 标准的一部分。
现在我知道我有两个进程可以通过几种机制在它们之间进行通信:
- 标准输入/标准输出/标准错误
- 退出代码
- 环境变量
- 系统调用
Bash 和 ls 都是程序。因此,它们只能使用列出的机制来交换信息。
这背后真正的问题是如何isatty()
从 bash 获取信息?
答案1
该--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)));
这背后真正的问题是 isatty() 如何从 bash 获取信息?
isatty()
检查传递给它的文件描述符,以查看文件描述符是否代表 TTY(终端设备)。isatty() 的具体工作方式可能因系统而异。下面是达尔文实现如果你有兴趣的话,可以参阅 Apple OSX:
#include <termios.h>
#include <unistd.h>
int
isatty(fd)
int fd;
{
struct termios t;
return(tcgetattr(fd, &t) != -1);
}
当您运行 时ls --color=auto
,您的 shell (bash) 将使用 shell 自己的标准输入、输出和错误作为“ls”进程的 stdin/out/err 来启动“ls”程序。如果您以交互方式运行,则 shell 的标准输出可能是一个终端,而 ls 的标准输出可能也是一个终端。当 ls 调用 isatty() 来测试其标准输出是否为终端时,它可能会成功。
当你运行类似的程序时ls --color=auto | cat
,你的 shell 会做三件事:
- 创建管道。
cat
将其标准输入设置为管道来启动。ls
将其标准输出设置为管道来启动。
管道不是终端,当ls
测试它的标准输出是否是tty时,测试会失败。