讓我在Peter Schneider's comment上展開一點。不要重新發明輪子。您的用戶會對解決方案(以及接口)感到高興,他們已經知道如何配置和使用其他程序。 UNIX的做事方式是做一件事,做得很好。確實顯示輸出頁面的程序稱爲頁面。曾經有more,但現在less是一個更好的選擇。
尋呼機將通過其標準輸入流接收顯示內容,然後以任何奇特的方式呈現給用戶。您應該將尋呼機程序的選擇留給用戶。這樣做的一個好方法是檢查環境變量PAGER
以獲得作爲尋呼機執行的命令。如果您正在這樣做,請注意環境不總是可信的。如果你的程序是stuid()
ed,那麼從一個環境變量中執行代碼是一個非常糟糕的主意,所以你最好在這種情況下忽略它。有些人喜歡提供更加特殊的MYPROGRAM_PAGER
變量,只有在沒有設置的情況下才會檢查更一般的PAGER
變量。
你應該檢查的另一件事是標準輸出isatty()
。如果不是,只需將所有輸出一次性轉儲出去。由於它將被重定向到一個文件,它也不會溢出屏幕(所以尋呼機不會有任何好處),用戶也不會願意甚至不能進入(所以尋呼機實際上會變差)。
生成的C程序應該看起來相當簡單。
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int
produce_output(FILE *const stream, const unsigned lines);
int
main(const int argc, const char *const *const argv)
{
unsigned lines;
if (argc != 2)
{
fprintf(stderr, "error: wrong number of arguments\n");
return EXIT_FAILURE;
}
else
{
char * endptr;
long val = strtol(argv[1], &endptr, 0);
if (*endptr != '\0' || val < 0)
{
fprintf(stderr, "error: not a non-negative integer: %s\n", argv[1]);
return EXIT_FAILURE;
}
lines = val;
}
if (isatty(fileno(stdin)) && isatty(fileno(stdout)))
{
const char * pager_command = NULL;
FILE * pager_pipe = NULL;
const char *const env_vars[] = {"MYPROG_PAGER", "PAGER", NULL};
unsigned i;
for (i = 0; env_vars[i] != NULL; ++i)
{
if ((pager_command = secure_getenv(env_vars[i])) != NULL)
break;
}
if (pager_command == NULL)
pager_command = "less -XRF"; /* sane default */
pager_pipe = popen(pager_command, "w");
if (pager_pipe == NULL)
{
fprintf(stderr,
"error: popen(\"%s\", \"w\"): %s\n",
pager_command, strerror(errno));
return EXIT_FAILURE;
}
/* Write everything to the pager at once. */
if (produce_output(pager_pipe, lines) < 0)
fprintf(stderr, "warning: I/O error: %s\n", strerror(errno));
if (pclose(pager_pipe) < 0)
{
fprintf(stderr, "error: pclose(): %s\n", strerror(errno));
return EXIT_FAILURE;
}
}
else
{
/* Write everything to stdout at once. */
if (produce_output(stdout, lines) < 0)
fprintf(stderr, "warning: I/O error: %s\n", strerror(errno));
}
return EXIT_SUCCESS;
}
int
produce_output(FILE *const stream, const unsigned lines)
{
unsigned i;
for (i = 1; i <= lines; ++i)
{
const char * suffix;
switch(i)
{
case 1: suffix = "st"; break;
case 2: suffix = "nd"; break;
case 3: suffix = "rd"; break;
default: suffix = "th"; break;
}
if (fprintf(stream, "This is the %d%s line of output.\n", i, suffix) < 0)
return -1;
}
if (fflush(stream) < 0)
return -1;
return lines;
}
secure_getenv
是GNU擴展爲getenv
封裝檢查環境是否可以信任你。
如果你想讓你的程序更健壯,你可以實現你自己的普通尋呼機,並在外部工具不可用時使用它作爲後備。在上面的例子中,我簡單地回到了一個硬編碼的默認值。
要測試您的程序,請考慮以下情況。
./myprog 10 # short amount of output
./myprog 1000 # large amount of output
PAGER='less -M' ./myprog 100 # use 'less -M' as pager
MYPROG_PAGER='cat' ./myprog 100 # use 'cat' as a "pager"
MYPROG_PAGER='cat' PAGER='less -M' ./myprog 100 # use 'cat' as a "pager"
./myprog 100 | cat # output is not at TTY
./myprog 100 > /dev/full # output is a bad file
sudo chown root:root myprog
sudo chmod u+s myprog
./myprog 100 # use default pager
PAGER='silly' ./myprog 100 # must ignore 'silly' in setuid'ed program
我在這個問題上花費相當長的一段細節,因爲什麼迄今爲止最讓我生氣是如果的程序與用戶交互不佳。不幸的是,許多編程教程都以用戶交互的可怕示例開始,可能是因爲作者從來沒有真正使用命令行工具。跨許多(如果不是全部)命令行工具的一致的良好用戶體驗是我認爲可用計算機最重要的方面。
你可以嘗試找出其他人做了什麼:http://www.greenwoodsoftware.com/less/download.html –