在Linux libreadline中解析带引号和双引号的行

问题描述

我正在用C中的libreadline Linux库编写自己的shell。在readline()中,它从shell中获取行,并用空格字符进行分析并将其存储到指针变量中。现在,我想解析这些行以像包含命令和执行命令的文件名的命令行参数那样获取

如果文件名中的文件名有空格且解析如下,我就遇到问题了

myshell$ ls 'file name 1' 'file name 2' file\ name\ 3

上面的cli被解析为

ls
'file
name
1'
'file
name
2'
file\
name\
3

因此,请帮助我解决此问题,以从读取行中获取空格文件名。

这是我正在调用readline的代码段。

    while (!done)
    {
        temp = readline (prompt);

        /* Test for EOF. */
        if (!temp)
            exit (1);

        /* If there is anything on the line,print it and remember it. */
        if (*temp)
        {
            add_history (temp);
        }

        <Other stuffs to execute the command>
    }

使用tokeniser更新了帖子。

    ptr = strtok(temp," ");
    while(ptr != NULL) {
        printf("%s\n",ptr);
        ptr = strtok(NULL," ");
    }

解决方法

使用朴素的strtok()应用程序很难做到这种复杂程度的令牌化。即使您可以使其正常工作,代码也将不可读也不可维护。有一些工具(如flex)可以根据规范生成标记器,或者您可以使用有限状态机(FSM)自己编写逻辑代码。使用FSM,您需要检查每个字符,并根据令牌生成器的当前状态采取措施。某些状态/字符组合会引起状态变化。

例如,如果您处于“双引号”状态,则不会将空格视为令牌的结尾。但是,处于双引号状态的双引号字符将表示从该状态过渡到其他状态。状态和它们之间的字符类形成一种矩阵,其中的条目控制着遇到状态和字符类的每种组合时要采取的动作。

FWIW我有一个简单的实现,可以按照所需的行解析ASCII文本;请参见以下代码中的tokenize()函数:

https://github.com/kevinboone/xine-server/blob/master/client/src/string.c

如果要创建外壳,请注意“真实”外壳中的标记化逻辑非常复杂,通常使用工具来实现。此外,大多数现代的实际应用程序都需要宽字符支持,这又增加了另一层复杂性。