【问题标题】:C Split CMD ArgumentsC 拆分 CMD 参数
【发布时间】:2014-06-13 22:43:53
【问题描述】:

我正在尝试做某事,但我可以。我有一个 C 语言程序,我想在其中解析所有参数。让我们更具体一些

编辑:我在缓冲区中读取命令,而不是在程序以 argv 等启动时读取。 我使用来自 STDIN 的 fgets 读取命令

假设我读了这样一个命令行:

ls -la

我想以这种格式将命令和参数列表保存在n 数组中:

char ***command; // A list of lists of strings.

command[0][0] = "ls";
command[0][1] = "-l";
command[0][2] = "a";
command[0][3] = NULL;

我想使用 execvp 执行上述命令,这就是我想要这种格式的原因。也看看下面的例子

ls -la | grep 1

数组必须如下:

command[0][0] = "ls";
command[0][1] = "-l";
command[0][2] = "a";
command[0][3] = NULL;
command[1][0] = "grep";
command[1][1] = 1;
command[1][2] = NULL;

简而言之,我想根据字符拆分命令并将它们放入二维数组中 |但也要维护命令参数。

我尝试使用 strtok(空格字符分隔符)来做到这一点,然后将它们存储在数组中,但我失败了。

你能帮帮我吗?

谢谢

【问题讨论】:

  • 我不想解析 ls 的输出。我只想从 cmd 行解析参数列表
  • 您确定要将"ls -la" 拆分为您最初写的"ls", "-l", "a" 吗?除非您对ls 在这种情况下会拆分它的论点有内部了解,否则这很难做到。我会假设 "ls", "-la" 是你想要的。
  • 对于想要一个非常简单但功能强大的实现来处理 bash 样式转义和引用的 unix 开发人员:github.com/pasztorpisti/cmd2argv

标签: c linux shell split arguments


【解决方案1】:

*利用 getopt... 有关 getopt 的更多信息,请访问 "man 3 getopt"*

int main (int argc, char **argv)
{
    int opt, flgl = 0, flga = 0;
    int indx;

    while ((opt = getopt (argc, argv, "la")) != ERROR) {
        switch (opt) {
            case 'l':
                    flgl = 1;           //printf ("%c", opt);
                    break;
            case 'a':
                    flga = 1;           //printf ("%c", opt);
                    break;
            default:
                    fprintf (stderr, "missing file operand\n");
                    break;
        }   
    }
    if (optind >= argc) {
        fprintf (stderr, "missing file operand\n");
        return FAILURE;
    }   
    for (indx = optind ; indx < argc ; indx++)
        call_rm (argv[indx], flgd, flgr);
    return SUCCESS;
}

【讨论】:

  • 对不起,我在启动命令时没有读取参数。我使用 fgets 从 CMD 读取整行。我会修复我的第一篇文章
【解决方案2】:

下面的答案显示了如何拆分 C 字符串,就像 shell 在将其作为 argcargv 传递给 main 函数之前所做的那样。那就是ls -la会被拆分成:

argv[0] == "ls"
argv[1] == "-la"

但是,由于您想包含多个用管道字符粘在一起的命令行,您首先必须在每个 | 字符处拆分字符串。正如您所提到的,这可以使用strtok 来完成。

str = "ls -la|grep hello";
...
// Do split on |
...
strs[0] == "ls -la";
strs[1] == "grep hello";

然后将这些命令行进一步拆分为它们自己的argv 数组。然后最后按照您的描述将它们组合成一个带有NULL 分隔符的数组。

由于您在开始时不知道最终数组的大小。或者对于有多少 | 符号,您可以首先计算它们并分配一个包含所有 argvs 的数组:

char **strs = NULL;
char **argvs = NULL;
size_t count = 0;
size_t i = 0;
while (*str) if (*str++ == '|') ++count;

strs = calloc(count, sizeof(char *));

...
// strtok on | and save tokens into strs[i]
...

现在拆分命令行并在末尾附加NULL

// Code for split_commandline below.
for (i = 0; i < count; i++)
{
    argvs[i] = split_commandline(strs[i], &argc);

    // Make room for NULL at the end of the argv array.
    argvs[i] = realloc(argvs[i], (argc + 1) * sizeof(char *));
    argvs[i][argc] = NULL;
}

有点做作,当然可以使用存储,但这样做的步骤很明确。

注意:这不会将"ls -la" 拆分为"ls", "-l", "a" 作为原始问题所要求的,而是"ls", "-la"。我不确定为什么需要这样做,但它需要对单个命令进行 hack,因为 "-la" 的含义是特定于 ls 程序的。

分割命令行

Unix/Linux 你可以使用wordexp 来做这件事。但是,这并不是说您应该注意一些安全隐患。也就是说,它会扩展 shell 变量,并且许多(如果不是全部)实现会导致调用 sh

注意:即使在调用 wordfree 之后,OSX 10.9.5 似乎也会在 wordexp 中泄漏内存。详情请见Is wordexp in libc on OSX 10.9.5 known to leak?

Windows 我知道这个问题只针对 Linux 进行了标记。但其他人可能对多平台解决方案感兴趣。

在这里您可以使用CommandLineToArgvW。请注意,这是针对 wchar_t * 的,因此下面的示例代码首先从 char * 转换为 wchar_t *,进行拆分,然后再转换回 char *,以便为两个平台获得一致的 API。

写完这个实现后,我还发现__getmainargs 支持char *,但我没有尝试过使用它。

代码示例:

char **split_commandline(const char *cmdline, int *argc)
{
    size_t i;
    char **argv = NULL;
    assert(argc);

    if (!cmdline)
    {
        return NULL;
    }

    // Posix.
    #ifndef _WIN32
    {
        int ret;
        wordexp_t p;
        memset(&p, 0, sizeof(p));

        // Note! This expands shell variables (might be a security issue).
        if ((ret = wordexp(cmdline, &p, 0)))
        {
            return NULL;
        }

        *argc = p.we_wordc;

        if (!(argv = calloc(*argc, sizeof(char *))))
        {
            goto fail;
        }

        for (i = 0; i < p.we_wordc; i++)
        {
            if (!(argv[i] = strdup(p.we_wordv[i])))
            {
                goto fail;
            }
        }

        // Note that on some OSX versions this does not free all memory (10.9.5)
        wordfree(&p);

        return argv;
    fail:
        p.we_offs = 0;
        wordfree(&p);
    }
    #else // WIN32
    {
        // TODO: __getmainargs is an alternative... https://msdn.microsoft.com/en-us/library/ff770599.aspx
        wchar_t **wargs = NULL;
        size_t needed = 0;
        wchar_t *cmdlinew = NULL;
        size_t len = strlen(cmdline) + 1;

        if (!(cmdlinew = calloc(len, sizeof(wchar_t))))
        {
            goto fail;
        }

        if (!MultiByteToWideChar(CP_ACP, 0, cmdline, -1, cmdlinew, len))
        {
            goto fail;
        }

        if (!(wargs = CommandLineToArgvW(cmdlinew, argc)))
        {
            goto fail;
        }

        if (!(argv = calloc(*argc, sizeof(char *))))
        {
            goto fail;
        }

        // Convert from wchar_t * to ANSI char *
        for (i = 0; i < *argc; i++)
        {
            // Get the size needed for the target buffer.
            // CP_ACP = Ansi Codepage.
            needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
                                        NULL, 0, NULL, NULL);

            if (!(argv[i] = malloc(needed)))
            {
                goto fail;
            }

            // Do the conversion.
            needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
                                        argv[i], needed, NULL, NULL);
        }

        if (wargs) LocalFree(wargs);
        free(&cmdlinew);
        return argv;

    fail:
        if (wargs) LocalFree(wargs);
        free(&cmdlinew);
    }
    #endif // WIN32

    if (argv)
    {
        for (i = 0; i < *argc; i++)
        {
            if(argv[i]) free(argv[i]);
            argv[i] = NULL;
        }

        free(argv);
    }

    return NULL;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-05
    • 1970-01-01
    • 1970-01-01
    • 2015-09-18
    相关资源
    最近更新 更多