【问题标题】:Mimicking the shell argument parser in C++在 C++ 中模仿 shell 参数解析器
【发布时间】:2011-04-04 14:53:05
【问题描述】:

我一直在研究一个模仿 shell 终端的程序,但我遇到了一个比我预期的更难的实现问题。基本上,我正在尝试拆分参数,就像 shell 如何传递给它的可执行文件一样。所以,想象一下这样的输入:

$> ./foo some arguments

人们会期望传递给程序的参数是一个数组,例如(假设 C/C++):

char ** argv = {"foo", "some" "arguments"}

但是,如果参数是:

$> ./foo "My name is foo" bar

数组将是:

char ** argv = {"foo", "My name is foo", "bar"}

任何人都可以提出一种有效的方法来实现这一点,例如界面如下:

vector<string> splitArgs(string allArgs);string[] splitArgs(string allArgs);

当然,我可以简单地在“阅读单词”/“阅读引用文本”的状态之间进行迭代和切换,但我觉得这并不像它应该的那样有效。我也玩弄了正则表达式的想法,但我对在 C++ 中是如何完成的还不够熟悉。对于这个项目,我也安装了 boost 库,如果有帮助的话。

谢谢! RR

【问题讨论】:

  • “但我觉得这并不像它应该的那样有效”......真的,你最好只是这样做并获得一个工作外壳。无论如何 - 既然你已经问过了 - 请检查 stackoverflow.com/questions/541561/… 以获取使用 boost tokenizer 的解决方案。
  • 只需遍历每个字符,看看你有什么。 Here's 我是如何在 C# 中做到这一点的。我不确定 RegEx 是否能满足您的需求。
  • 太好了,谢谢大家。我想这就是我想知道的。
  • 实施是步骤2。第 1 步是定义它。查看您最喜欢的 shell 的文档以找到您可以使用的定义。需要考虑的事项:多种引号;括号; I/O 重定向;反斜杠。 (另外,你真的是要去掉第一个标记的前两个字符吗?为什么?)

标签: c++ regex command-line command-line-arguments


【解决方案1】:

我有时仍然为此使用这个纯 C 实用程序函数。我主要在标准库非常有限的嵌入式系统上使用它,因此可以使用标准库控件更改大部分代码以提高效率,但基本技术应该保持不变,标记字符串的引用部分在解析之前,只需通过在标记上拆分将字符串拆分为单独的标记,最后从各个部分中删除引号。

/**
 * Split a line into separate words.
 */
static void splitLine(char *pLine, char **pArgs) {
    char *pTmp = strchr(pLine, ' ');

    if (pTmp) {
        *pTmp = '\0';
        pTmp++;
        while ((*pTmp) && (*pTmp == ' ')) {
            pTmp++;
        }
        if (*pTmp == '\0') {
            pTmp = NULL;
        }
    }
    *pArgs = pTmp;
}



/**
 * Breaks up a line into multiple arguments.
 *
 * @param io_pLine Line to be broken up.
 * @param o_pArgc Number of components found.
 * @param io_pargc Array of individual components
 */
static void parseArguments(char *io_pLine, int *o_pArgc, char **o_pArgv) {
    char *pNext = io_pLine;
    size_t i;
    int j;
    int quoted = 0;
    size_t len = strlen(io_pLine);

    // Protect spaces inside quotes, but lose the quotes
    for(i = 0; i < len; i++) {
        if ((!quoted) && ('"' == io_pLine[i])) {
            quoted = 1;
            io_pLine[i] = ' ';
        } else if ((quoted) && ('"' == io_pLine[i])) {
            quoted = 0;
            io_pLine[i] = ' ';
        } else if ((quoted) && (' ' == io_pLine[i])) {
            io_pLine[i] = '\1';
        }
    }

    // init
    MY_memset(o_pArgv, 0x00, sizeof(char*) * C_MAXARGS);
    *o_pArgc = 1;
    o_pArgv[0] = io_pLine;

    while ((NULL != pNext) && (*o_pArgc < C_MAXARGS)) {
        splitLine(pNext, &(o_pArgv[*o_pArgc]));
        pNext = o_pArgv[*o_pArgc];

        if (NULL != o_pArgv[*o_pArgc]) {
            *o_pArgc += 1;
        }
    }

    for(j = 0; j < *o_pArgc; j++) {
        len = strlen(o_pArgv[j]);
        for(i = 0; i < len; i++) {
            if('\1' == o_pArgv[j][i]) {
                o_pArgv[j][i] = ' ';
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    将整个字符串传递给 shell 可能会满足您的需求:

    例如:

    System("./foo some arguments");
    

    不是最好的解决方案

    更好的方法似乎是写一个parser to find each argument 并将其传递给exec style function

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-08
      • 1970-01-01
      • 2012-07-17
      • 2012-03-05
      • 2022-06-24
      • 1970-01-01
      • 2014-10-10
      • 1970-01-01
      相关资源
      最近更新 更多