C++ 命令行界面

16

我目前正在设计一个 Linux C++ 应用程序。它将从命令行运行,运行后我需要能够发出控制其执行的命令,最好像下面这样:

$ sudo ./myapplication
APP > 
APP > 
APP > //just pressing return
APP > openlog test1.txt //APP will now call the openlog function
APP >

我想这应该是一个相对简单的任务,但我不知道如何称呼这样的接口以便搜索。有人知道可以执行此功能的库或示例吗?或者我需要使用cout和cin编写自己的代码吗?如果需要的话,是否有任何首选方法?


1
C++没有反射。你需要绕过这个问题。 - chris
4
说这个应用程序需要由su运行是夸张的,更夸张的是因为感知到的专业水平而说某人不应编写超级用户运行的应用程序。我的翻译是否符合您的要求? - Nathaniel Ford
@Duck,我是根据评论“APP现在将调用openlog函数”来进行的。虽然在诸如C#之类的语言中,用户可以使用反射输入任意函数的名称以及参数值,但在C++中这样做并不那么容易或健壮。 - chris
1
你想要 REPL (http://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) 吗?还是其他什么? - n0rd
@chris,谢谢。当我看到PLPiper的答案并意识到你的意图时,我删除了我的评论。我没有立即联系起来。 - Duck
显示剩余2条评论
10个回答

17

我推荐使用GNU readline库来实现这个功能。它可以处理获取输入行的繁琐工作,同时允许用户使用退格、左右箭头等编辑自己的行,并且通过上箭头回忆以前的命令,甚至使用^R搜索以前的命令等操作。在类Unix操作系统下(如Linux)通常已预装Readline。但如果您没有安装它,您可以在此处找到它。

编辑:下面是一个最简单的readline示例:

#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>

int main(int argc, char ** argv)
{
    while(1)
    {
        char * line = readline("> ");
        if(!line) break;
        if(*line) add_history(line);
        /* Do something with the line here */
        free(line);
    }
}

9

GNU readline库具备完整的行编辑和历史功能,但如果只需要一个简单提示符(或者您不想要GNU许可证),那么您可以仅使用标准库来实现:

#include <iostream>
#include <string>

void process(std::string const & line);

int main()
{
    for (std::string line; std::cout << "APP > " && std::getline(std::cin, line); )
    {
        if (!line.empty()) { process(line); }
    }

    std::cout << "Goodbye.\n";
}

3

GNU readline是一个非常出色的选择,正如其他人所建议的那样。如果许可证问题会迫使您排除它,那么您应该考虑使用linenoise


1
看起来 linenoise 还提供了补全功能,这可能非常实用。 - etham

2

你可能需要查看readline库。虽然它有一定的学习曲线,但仍然比自己重新创建完整的CLI容易得多。不过,请检查许可证是否适合你的项目。


2
我同意Chris的评论,如果使用非反射性语言,则会更难。在C++中,您需要显式地将您输入的内容映射到特定的函数。
如果您要自己编写代码,您的设计应该大致如下:
- 读取输入行(最可能使用cin.getline读入字符串) - 确定第一个单词并确定它是否映射到任何函数(例如,您可以使用简单的switch语句、哈希表等) - 如果它没有映射到函数,则发布错误并重新显示提示。 - 如果它确实映射,则必须检查该行中的其他单词。 - 对于每个其他单词,您都必须将字符串转换为您想要作为函数参数的任何数据类型(这里stringstreams非常有用)。 - 现在,您必须确保您提供的参数对于调用的函数是合法的。您可以在调用函数之前预先检查它们,也可以在函数内部检查错误。 - 一旦您验证了函数名称和参数是否正确(以及数量正确),您就可以调用该函数了。
在反射性语言中,上述步骤的前半部分要简单得多,因为您可以直接将字符串转换为函数名。

1
我强烈推荐使用std::getline而不是std::istream::getline - chris

1

1
这个似乎正好满足OP的需求:
它实际上是readline库的一个包装器,但会解析/转换您函数的参数到它们各自的类型:
shpp::service svc;
svc.provide_command("my_function", [](int arg1, string arg2) -> { /* Do Something */ });
shpp::shell sh(svc);
sh.start();

它可以与函数指针和 lambda 表达式一起使用。

1

你将不得不至少部分自己编写。GNU readline 可能会有所帮助;查看 http://en.wikipedia.org/wiki/GNU_readline 获取一个简短的程序,它是实现此功能的“骨架”,您可以在其周围添加代码。


1
请注意GNU Readline的GPL许可证。 在答案中,人们提到了GNU readline的GPL许可证。在这个答案中,我想强调GPL许可证的影响-新用户/开发者可能会忽视。 从https://en.wikipedia.org/wiki/GNU_Readline复制的文本。
选择GPL作为GNU Readline的许可证[编辑] GNU Readline是一款自由软件库,其许可证采用GNU通用公共许可证(GPL)而不是GNU Lesser General Public License(LGPL)。自由软件库通常使用LGPL许可证,例如GNU C库、GNU gettext和FLTK。
在构建新应用程序时选择链接到LGPL许可的库的开发人员需要确保所使用的LGPL许可的库在分发合并后的应用程序时仍然遵循LGPL许可。合并应用程序中除LGPL许可的库之外的部分可以继续使用原始许可证。与此相反,如果开发人员选择使用GPL许可的库创建新应用程序,则整个合并的应用程序在分发时都必须遵循GPL的第5条款,以符合GPL的规定。
GNU Readline GPL许可证的影响[编辑]一个重要的例子是CLISP,它是Common Lisp的一个实现,它改变了其许可证以符合GNU Readline的版权条件。最初发布于1987年,它在1992年更改为GPL许可证,在此之前,CLISP的原始作者之一Bruno Haible和Richard Stallman进行了电子邮件交流,在这封邮件中Stallman认为,在CLISP中链接readline意味着Haible必须重新许可CLISP,如果他希望分发使用readline的CLISP实现。
希望实现命令行编辑功能但希望保持自由许可证的软件项目可以使用许可证宽松的替代命令行编辑库。其中典型的是BSD许可证的libedit。一些应用程序,如MariaDB或PHP,允许用户在构建时选择链接GNU Readline还是libedit。其他命令行编辑库具有双重目的:它们具有与某些主要项目相对齐的API,并具有兼容的许可证条款。一个例子是Haskeline库,它向Glasgow Haskell Compiler和其他需要行编辑服务的Haskell项目公开了Haskell兼容的API。
外部链接列出了几个这样的库的链接。

-4

您可以编写 TCL 或 Python 应用程序,并将它们用作前端。


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