为了简化Thomas Dickey's 和Chris Dodd's 的答案,选择使用哪个描述符来引用终端的典型代码是
int ttyfd;
/* Check standard error, output, and input, in that order. */
if (isatty(fileno(stderr)))
ttyfd = fileno(stderr);
else
if (isatty(fileno(stdout)))
ttyfd = fileno(stdout);
else
if (isatty(fileno(stdin)))
ttyfd = fileno(stdin);
else
ttyfd = -1; /* No terminal; redirecting to/from files. */
如果您的应用程序坚持访问控制终端(用户用来执行此过程的终端),如果有,您可以使用以下new_terminal_descriptor() 函数。为简单起见,我将其嵌入到示例程序中:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
int new_terminal_descriptor(void)
{
/* Technically, the size of this buffer should be
* MAX( L_ctermid + 1, sysconf(_SC_TTY_NAME_MAX) )
* but 256 is a safe size in practice. */
char buffer[256], *path;
int fd;
if (isatty(fileno(stderr)))
if (!ttyname_r(fileno(stderr), buffer, sizeof buffer)) {
do {
fd = open(path, O_RDWR | O_NOCTTY);
} while (fd == -1 && errno == EINTR);
if (fd != -1)
return fd;
}
if (isatty(fileno(stdout)))
if (!ttyname_r(fileno(stdout), buffer, sizeof buffer)) {
do {
fd = open(path, O_RDWR | O_NOCTTY);
} while (fd == -1 && errno == EINTR);
if (fd != -1)
return fd;
}
if (isatty(fileno(stdin)))
if (!ttyname_r(fileno(stdin), buffer, sizeof buffer)) {
do {
fd = open(path, O_RDWR | O_NOCTTY);
} while (fd == -1 && errno == EINTR);
if (fd != -1)
return fd;
}
buffer[0] = '\0';
path = ctermid(buffer);
if (path && *path) {
do {
fd = open(path, O_RDWR | O_NOCTTY);
} while (fd == -1 && errno == EINTR);
if (fd != -1)
return fd;
}
/* No terminal. */
errno = ENOTTY;
return -1;
}
static void wrstr(const int fd, const char *const msg)
{
const char *p = msg;
const char *const q = msg + ((msg) ? strlen(msg) : 0);
while (p < q) {
ssize_t n = write(fd, p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1)
return;
else
if (errno != EINTR)
return;
}
}
int main(void)
{
int ttyfd;
ttyfd = new_terminal_descriptor();
if (ttyfd == -1)
return EXIT_FAILURE;
/* Let's close the standard streams,
* just to show we're not using them
* for anything anymore. */
fclose(stdin);
fclose(stdout);
fclose(stderr);
/* Print a hello message directly to the terminal. */
wrstr(ttyfd, "\033[1;32mHello!\033[0m\n");
return EXIT_SUCCESS;
}
wrstr() 函数只是一个辅助函数,它立即将指定的字符串写入指定的文件描述符,而不进行缓冲。该字符串包含 ANSI 颜色代码,因此如果成功,它将向终端打印浅绿色 Hello!,即使标准流已关闭。
如果将上面的内容保存为example.c,则可以使用例如编译它
gcc -Wall -Wextra -O2 example.c -o example
并使用运行
./example
因为new_terminal_descriptor() 使用ctermid() 函数来获取控制终端的名称(路径)作为最后的手段——这并不常见,但我想在这里展示如果你决定它很容易做到是必要的——即使所有流都被重定向,它也会将 hello 消息打印到终端:
./example </dev/null >/dev/null 2>/dev/null
最后,如果您想知道,这些都不是“特别的”。我不是在谈论 控制台终端,它是许多 Linux 发行版作为图形环境的替代品提供的基于文本的控制台界面,也是大多数 Linux 服务器提供的唯一本地界面。以上所有都只使用普通的 POSIX 伪终端接口,并且使用例如可以正常工作。 xterm 或任何其他普通终端仿真器(或 Linux 控制台),适用于所有 POSIXy 系统——Linux、Mac OS X 和 BSD 变体。