如何像Linux命令"top"一样进行控制台输入?

5

因此,Linux中的top命令具有类似实时循环的控制台输出(没有花哨的东西),但它使用非阻塞控制台输入,这不会在命令行中显示键入的字符。它是如何实现的?是否有任何库可用,他们使用线程吗?我需要编写一个使用相同风格的Linux应用程序(通过ssh使用),但我不知道如何处理输入(在单独的线程中使用cin不是解决方案,top使用了其他方法)。


2
你是怎么解决的?如果有答案帮到你了,请接受它。如果你自己找到了解决方案,你也可以发布一个答案。这样,如果有人将来有同样(或类似)的问题,你的问题可能会对他们有很大的帮助。 - Chad
@Chad:在“问题已解决”的评论发布时,还没有答案。我最初发布了一条评论,后来将其替换为答案。 - pmg
@Chad:而且这更具有社交性,因为原帖作者期望我们为他投入时间,当然是免费的,但他本人似乎对帮助未来的访问者不感兴趣。 - Sebastian Mach
当我发表评论时,还没有答案。由于这条评论,我知道了是哪个库导致了问题,因此问题得以解决。 - user1873947
2个回答

7
一种解决方案是使用 curses 的实现。 我不知道 top 是如何做到的。

4
ldd /usr/bin/top 显示 libncurses.so.5 => /lib64/libncurses.so.5。因此,top 使用了 ncurses 库。 - Nikos C.

3

另一个选项,不使用curses:

#include <stdio.h>
//#include <unistd.h>
#include <termios.h>
//#include <sys/ioctl.h>
//#include <sys/time.h>
//#include <sys/types.h>
#include <fcntl.h>

//------------------------------------------------------------------------------
// getkey() returns the next char in the stdin buffer if available, otherwise
//          it returns -1 immediately.
//
int getkey(void)
{
    char ch;
    int error;
    struct termios oldAttr, newAttr;
    int oldFlags, newFlags;
    struct timeval tv;
    int fd = fileno(stdin);
    tcgetattr(fd, &oldAttr);
    newAttr = oldAttr;
    oldFlags = fcntl(fd, F_GETFL, 0);

    newAttr.c_iflag = 0; /* input mode */
    newAttr.c_oflag = 0; /* output mode */
    newAttr.c_lflag &= ~ICANON; /* line settings */
    newAttr.c_cc[VMIN] = 1; /* minimum chars to wait for */
    newAttr.c_cc[VTIME] = 1; /* minimum wait time */

    // Set stdin to nonblocking, noncanonical input
    fcntl(fd, F_SETFL, O_NONBLOCK);
    error=tcsetattr(fd, TCSANOW, &newAttr);

    tv.tv_sec = 0;
    tv.tv_usec = 10000; // small 0.01 msec delay
    select(1, NULL, NULL, NULL, &tv);

    if (error == 0)
        error=(read(fd, &ch, 1) != 1); // get char from stdin

    // Restore original settings
    error |= tcsetattr(fd, TCSANOW, &oldAttr);
    fcntl(fd, F_SETFL, oldFlags);

    return (error ? -1 : (int) ch);
}

int main()
{
    int c,n=0;
    printf("Hello, world!\nPress any key to exit. I'll wait for 4 keypresses.\n\n");
    while (n<4)
    {
        //printf("."); // uncomment this to print a dot on each loop iteration
        c = getkey();
        if (c >= 0)
        {
            printf("You pressed '%c'\n", c);
            ++n;
        }
    }
}

很抱歉我不能完全为此负责,因为我多年前从网络上找到了它。我认为我已经稍微改进了一下,但它大部分仍然与我找到它时相同。不幸的是,我没有添加注释表明我在哪里找到它。


@user1873947 - 没有特殊的库,只使用了C标准库和CRT。我在我的答案中添加了#includes头文件,并注释掉了那些我认为函数本身实际上不需要的头文件,但仍保留它们以防我错了。 - phonetagger
@user1873947 - 我不知道...在我的CentOS 5.8上使用GCC 4.7.2可以正常工作。我相信我是直接从之前使用的Solaris盒子移植过来的,但那已经是三年前的事了。编写一个简单的main()测试这个并不太困难。 - phonetagger
它不可移植:/ 它确实在Unix上运行,但在Windows上会出现致命错误: termios.h:没有那个文件或目录| - user1873947
它可以工作,但字符仍会被回显到控制台。是否有任何标志可用于禁用回显? - Spidey
1
@Spidey - 我认为你可以使用 newAttr.c_lflag &= ~(ICANON | ECHO); 替换 newAttr.c_lflag &= ~ICANON;,但你需要改变 getkey() 函数的整个策略,因为 (按照当前的实现) 它保存并无条件地恢复 stdio 终端模式,这将在返回之前重新打开 echo。这个链接展示了如何请求密码(不显示字符)而不使用 curses/ncurses:http://forums.fedoraforum.org/showpost.php?s=da285de869a78cf3e28b7196c6457fc3&p=1516255&postcount=4 - phonetagger
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接