【问题标题】:How to use C programming language creating a shell that can implement history capture up arrow?如何使用 C 编程语言创建一个可以实现历史捕获向上箭头的 shell?
【发布时间】:2015-03-03 04:24:01
【问题描述】:

我已将先前命令的历史记录保存在二维数组中。但我不知道如何检查向上箭头。

如何使用 C 编程语言实现此功能(在 Linux 中)?

【问题讨论】:

  • @n.m.当然,前提是您可以接受 GPL 许可。
  • Termios 库可以阻止你 (linux.die.net/man/3/termios)。我在下面的答案中插入使用示例。
  • @aruisdante 有一个 bsd 许可的替代方案,称为 editline,但是是的,我确实希望人们能够接受 gpl,除非他们特别提到其他内容。毕竟他们来这里是为了获得免费建议。

标签: c linux shell


【解决方案1】:

感谢 n.m. 的好建议。

以下是libreadline的用法示例:

// rltest.c
#include <stdio.h>
#include <stdlib.h>
#include <readline.h>
#include <history.h>

int main(void)
{
    char* input, shell_prompt[100];
    // Configure readline to auto-complete paths when the tab key is hit.
    rl_bind_key('\t', rl_complete);
    // while work is not 0 program executes the loop
    int work = 1;
    printf("Commands to use: name, ver, exit \n");
    // loop for working with commands
    while(work) {
        // Build prompt string.
        snprintf(shell_prompt, sizeof(shell_prompt), "your command $ ");
        // Display prompt and read input
        input = readline(shell_prompt);
        // Check for EOF.
        if (!input)
            break;
        // Add input to history.
        add_history(input);
        // Command analysis and execution
        if( 0 == strcmp(input, "exit") )
        {
            printf("Bye!\n");
            work = 0;
        }
        if( 0 == strcmp(input, "name") )
        {
            printf("I'm readline example\n");
        }
        if( 0 == strcmp(input, "ver") )
        {
            printf("My version is 0.1\n");
        }
        // ...
        // Free input for future use
        free(input);
    }
    return 0;
}

编译这个例子:

1) 安装 readline 库

 apt-get install libreadline-dev

2) 编译程序为

 gcc rltest.c -I/usr/include/readline -lreadline

【讨论】:

    【解决方案2】:

    试试下面的例子:

    #include <stdio.h>
    #include <termios.h>
    
    // constants for sizes
    #define LINES 5
    #define MAX_LEN 50
    
    // this is just for demonstration
    // read more about screen cleaning http://www.cplusplus.com/articles/4z18T05o/
    void ClearScreen()
    {
        printf("\033[2J");
        printf("\033[0;0f");
    }
    
    // function based on termios library
    // see more at http://stackoverflow.com/questions/7469139/what-is-equivalent-to-getch-getche-in-linux
    
    static struct termios old, new;
    
    /* Initialize new terminal i/o settings */
    void initTermios(int echo)
    {
      tcgetattr(0, &old); /* grab old terminal i/o settings */
      new = old; /* make new settings same as old settings */
      new.c_lflag &= ~ICANON; /* disable buffered i/o */
      new.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */
      tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */
    }
    
    /* Restore old terminal i/o settings */
    void resetTermios(void)
    {
      tcsetattr(0, TCSANOW, &old);
    }
    
    /* Read 1 character - echo defines echo mode */
    char getch_(int echo)
    {
      char ch;
      initTermios(echo);
      ch = getchar();
      resetTermios();
      return ch;
    }
    
    int main(void)
    {
        char strBuff[LINES][MAX_LEN] = {"The first line",
                "This is the longest line in this buffer",
                "Just a middle line",
                "Short",
                "The last line"};
        char ch; // one character input buffer
        int cnt = 0; // number of current line to be shown
        // loop for showing lines
        ClearScreen();
        do{
            // show current line
            printf("%s", strBuff[cnt]); 
            // read input (arrows. characters, etc.
            ch = getch_(0);
            // arrows detection in input
            if(ch == 27)
            {
                ch = getch_(0);
                if( ch == 91)
                {
                    ch = getch_(0);
                    if(ch == 66)  // up arrow
                    {
                        cnt++;
                        if(cnt >= LINES)
                            cnt = 0;
                    }
                    else
                        if(ch == 65)  // down arrow
                        {
                            cnt--;
                            if(cnt < 0)
                                cnt = LINES-1;
                        }
                }
            }
            // cleaning screen before next output
            ClearScreen();
    
        }while(ch != 'q'); // press 'q' for exit
    }
    

    【讨论】:

    • 终端抽象库的意义不在于取消所有硬编码的转义序列以清除屏幕等吗?
    • 屏幕清除在任何情况下都是依赖于操作系统的,但是条件编译可以帮助制作跨平台的解决方案。我其实不知道这样的库。
    • 不好的建议。这不适用于所有终端。阅读terminfoncurses
    • 例如,我刚从某人那里得到一个错误报告,他必须处理 8 位 CSI (155) 而不是 7 位 (27, 91)。此外,即使对于 ANSI 序列,这也不处理非应用模式 SS3 (27, 79)。最好将其移出硬编码领域。
    • @n.m. :这不是“坏建议”。...这只是关于 cmets 和参考的建议。我不坚持这样的解决方案,任何人都可以提出其他想法和例子。
    猜你喜欢
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    • 2018-05-17
    • 1970-01-01
    • 2017-04-11
    • 2013-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多