Redis 7.0.5 源码阅读笔记:基于linenoise 的 redis-client 命令行实现

Redis162 字

redis-client 提供了类似于与 mysql 的交互式命令行工具,我们输入命令,redis-client 会将我们的命令发送给 redis-server 然后返回取到的值。

linenoise 是非常轻量级的命令行处理工具,除了 redis 还有很著名的开源软件在使用 linenoise 其中就包括 MongoDB 和 Android 都将 Linenoise 作为命令行解析工具,那么今天我们就来解锁这个开源的命令行处理工具,也许某一天在你的项目里会派上用场。

其使用方法我们可以参考这篇文章:https://zhuanlan.zhihu.com/p/103845625

其源代码被 redis 放在 deps 目录下。

redis 对齐的调用封装在 src/redis-cli.c 中的 repl 函数中.

static void repl(void) {
    sds historyfile = NULL;
    int history = 0;
    char *line;
    int argc;
    sds *argv;

    /* There is no need to initialize redis HELP when we are in lua debugger mode.
     * It has its own HELP and commands (COMMAND or COMMAND DOCS will fail and got nothing).
     * We will initialize the redis HELP after the Lua debugging session ended.*/
    if (!config.eval_ldb) {
        /* Initialize the help using the results of the COMMAND command. */
        cliInitHelp();
    }

    config.interactive = 1;
    linenoiseSetMultiLine(1);
    linenoiseSetCompletionCallback(completionCallback);
    linenoiseSetHintsCallback(hintsCallback);
    linenoiseSetFreeHintsCallback(freeHintsCallback);

    /* Only use history and load the rc file when stdin is a tty. */
    if (isatty(fileno(stdin))) {
        historyfile = getDotfilePath(REDIS_CLI_HISTFILE_ENV,REDIS_CLI_HISTFILE_DEFAULT);
        //keep in-memory history always regardless if history file can be determined
        history = 1;
        if (historyfile != NULL) {
            linenoiseHistoryLoad(historyfile);
        }
        cliLoadPreferences();
    }

    cliRefreshPrompt();
    while((line = linenoise(context ? config.prompt : "not connected> ")) != NULL) {
        if (line[0] != '\0') {
            long repeat = 1;
            int skipargs = 0;
            char *endptr = NULL;

            argv = cliSplitArgs(line,&argc);
            if (argv == NULL) {
                printf("Invalid argument(s)\n");
                fflush(stdout);
                if (history) linenoiseHistoryAdd(line);
                if (historyfile) linenoiseHistorySave(historyfile);
                linenoiseFree(line);
                continue;
            } else if (argc == 0) {
                sdsfreesplitres(argv,argc);
                linenoiseFree(line);
                continue;
            }

            /* check if we have a repeat command option and
             * need to skip the first arg */
            errno = 0;
            repeat = strtol(argv[0], &endptr, 10);
            if (argc > 1 && *endptr == '\0') {
                if (errno == ERANGE || errno == EINVAL || repeat <= 0) {
                    fputs("Invalid redis-cli repeat command option value.\n", stdout);
                    sdsfreesplitres(argv, argc);
                    linenoiseFree(line);
                    continue;
                }
                skipargs = 1;
            } else {
                repeat = 1;
            }

            if (!isSensitiveCommand(argc - skipargs, argv + skipargs)) {
                if (history) linenoiseHistoryAdd(line);
                if (historyfile) linenoiseHistorySave(historyfile);
            }

            if (strcasecmp(argv[0],"quit") == 0 ||
                strcasecmp(argv[0],"exit") == 0)
            {
                exit(0);
            } else if (argv[0][0] == ':') {
                cliSetPreferences(argv,argc,1);
                sdsfreesplitres(argv,argc);
                linenoiseFree(line);
                continue;
            } else if (strcasecmp(argv[0],"restart") == 0) {
                if (config.eval) {
                    config.eval_ldb = 1;
                    config.output = OUTPUT_RAW;
                    sdsfreesplitres(argv,argc);
                    linenoiseFree(line);
                    return; /* Return to evalMode to restart the session. */
                } else {
                    printf("Use 'restart' only in Lua debugging mode.\n");
                    fflush(stdout);
                }
            } else if (argc == 3 && !strcasecmp(argv[0],"connect")) {
                sdsfree(config.conn_info.hostip);
                config.conn_info.hostip = sdsnew(argv[1]);
                config.conn_info.hostport = atoi(argv[2]);
                cliRefreshPrompt();
                cliConnect(CC_FORCE);
            } else if (argc == 1 && !strcasecmp(argv[0],"clear")) {
                linenoiseClearScreen();
            } else {
                long long start_time = mstime(), elapsed;

                issueCommandRepeat(argc-skipargs, argv+skipargs, repeat);

                /* If our debugging session ended, show the EVAL final
                    * reply. */
                if (config.eval_ldb_end) {
                    config.eval_ldb_end = 0;
                    cliReadReply(0);
                    printf("\n(Lua debugging session ended%s)\n\n",
                        config.eval_ldb_sync ? "" :
                        " -- dataset changes rolled back");
                    cliInitHelp();
                }

                elapsed = mstime()-start_time;
                if (elapsed >= 500 &&
                    config.output == OUTPUT_STANDARD)
                {
                    printf("(%.2fs)\n",(double)elapsed/1000);
                }
            }
            /* Free the argument vector */
            sdsfreesplitres(argv,argc);
        }
        /* linenoise() returns malloc-ed lines like readline() */
        linenoiseFree(line);
    }
    exit(0);
}
maksim
Maksim(一笑,吡罗),PHPer,Goper
OωO
开启隐私评论,您的评论仅作者和评论双方可见