gpt4 book ai didi

c - gcc wordexp Posix C 库函数不使用 Linux IFS 环境变量来拆分单词

转载 作者:行者123 更新时间:2023-12-03 09:47:15 24 4
gpt4 key购买 nike

环境
操作系统:Ubuntu 20.4、Centos 8、macOS Catalina 10.15.7
语言:C、C++
编译器:gcc(每个操作系统的最新版本)
问题
我正在使用 wordexp Posix 库函数来获得类似 shell 的字符串扩展。
扩展工作正常,但有一个异常(exception):当我将 $IFS 环境变量设置为空格以外的其他值时,例如“:”,它似乎不会影响仅在空格上继续进行的单词的拆分,而不管 IFS 值如何.
重击测试
适用于 Linux 的 wordexp 手册页 https://man7.org/linux/man-pages/man3/wordexp.3.html状态:

  • “函数 wordexp() 对字符串执行类似 shell 的扩展...”
  • “字段拆分是使用环境变量 $IFS 完成的。如果未设置,字段分隔符为空格、制表符和换行符。”

  • 这就是为什么我希望 wordexp 在这方面表现得与 bash 相同。
    在所有列出的操作系统上,更改用于拆分的字符集时,我得到了完全正确和预期的结果:
    使用默认值(未设置 IFS)
        read -a words <<<"1 2:3 4:5"
    for word in "${words[@]}"; do echo "$word"; done
    在空间上正确拆分并产生结果:
        1
    2:3
    4:5
    将 IFS 设置为 ':'
        IFS=':' read -a words <<<"1 2:3 4:5"
    for word in "${words[@]}"; do echo "$word"; done
    在 ':' 上正确拆分并产生结果:
        1 2
    3 4
    5
    C代码测试
    但是无论是否设置 IFS 环境变量,运行下面的代码都会产生相同的结果:
    代码:
        #include <stdio.h>
    #include <wordexp.h>
    #include <stdlib.h>

    static void expand(char const *title, char const *str)
    {
    printf("%s input: %s\n", title, str);
    wordexp_t exp;
    int rcode = 0;
    if ((rcode = wordexp(str, &exp, WRDE_NOCMD)) == 0) {
    printf("output:\n");
    for (size_t i = 0; i < exp.we_wordc; i++)
    printf("%s\n", exp.we_wordv[i]);
    wordfree(&exp);
    } else {
    printf("expand failed %d\n", rcode);
    }
    }

    int main()
    {
    char const *str = "1 2:3 4:5";

    expand("No IFS", str);

    int rcode = setenv("IFS", ":", 1);
    if ( rcode != 0 ) {
    perror("setenv IFS failed: ");
    return 1;
    }

    expand("IFS=':'", str);

    return 0;
    }
    所有操作系统的结果都是一样的:
        No IFS input: 1 2:3 4:5
    output:
    1
    2:3
    4:5
    IFS=':' input: 1 2:3 4:5
    output:
    1
    2:3
    4:5
    请注意,上面的代码片段是为这篇文章创建的 - 我使用更复杂的代码进行了测试,以验证环境变量确实设置正确。
    源代码审查
    我查看了 https://code.woboq.org/userspace/glibc/posix/wordexp.c.html 上可用的 wordexp 函数实现的源代码。并且它似乎确实使用了 $IFS,但可能不一致,或者这是一个错误。
    具体来说:
    在以 开头的 wordexp 正文中第 2229 行 它确实获取 IFS 环境变量值并对其进行处理:
    第 2273 - 2276 行:
         /* Find out what the field separators are.
    * There are two types: whitespace and non-whitespace.
    */
    ifs = getenv ("IFS");
    但是后来在函数中它似乎没有
    使用 $IFS 值进行单词分隔。
    这看起来像一个错误,除非 上的“字段分隔符”第 2273 行
    上的“单词分隔符”第 2396 行 意思不同。
    第 2395 - 2398 行:
              default:
    /* Is it a word separator? */
    if (strchr (" \t", words[words_offset]) == NULL)
    {
    但无论如何,代码似乎只使用空格或制表符作为分隔符
    与尊重 IFS 设置拆分器值的 bash 不同。
    问题
  • 我是否遗漏了一些东西,有没有办法让 wordexp 拆分为空格以外的字符?
  • 如果拆分仅在空格上,这是一个错误
  • gcc 库实现或
  • 在 wordexp 的 Linux 手册页中,他们声称 $IFS 可用于定义拆分器


  • 非常感谢您的所有评论和见解!
    答案摘要和解决方法
    在接受的答案中,有一个关于如何从 $IFS 实现非空白字符拆分的提示:您必须设置 $IFS 并将要拆分的字符串作为临时环境变量的值,然后调用wordexp 针对该临时变量。这在下面的更新代码中进行了演示。
    虽然在源代码中可见的这种行为实际上可能不是一个错误,但在我看来它绝对是一个有问题的设计决策......
    更新代码:
        #include <stdio.h>
    #include <wordexp.h>
    #include <stdlib.h>

    static void expand(char const *title, char const *str)
    {
    printf("%s input: %s\n", title, str);
    wordexp_t exp;
    int rcode = 0;
    if ((rcode = wordexp(str, &exp, WRDE_NOCMD)) == 0) {
    printf("output:\n");
    for (size_t i = 0; i < exp.we_wordc; i++)
    printf("%s\n", exp.we_wordv[i]);
    wordfree(&exp);
    } else {
    printf("expand failed %d\n", rcode);
    }
    }

    int main()
    {
    char const *str = "1 2:3 4:5";

    expand("No IFS", str);

    int rcode = setenv("IFS", ":", 1);
    if ( rcode != 0 ) {
    perror("setenv IFS failed: ");
    return 1;
    }

    expand("IFS=':'", str);

    rcode = setenv("FAKE", str, 1);
    if ( rcode != 0 ) {
    perror("setenv FAKE failed: ");
    return 2;
    }

    expand("FAKE", "${FAKE}");

    return 0;
    }
    产生的结果:
        No IFS input: 1 2:3 4:5
    output:
    1
    2:3
    4:5
    IFS=':' input: 1 2:3 4:5
    output:
    1
    2:3
    4:5
    FAKE input: ${FAKE}
    output:
    1 2
    3 4
    5

    最佳答案

    让我们天真地假设 POSIX 是可以理解的,并尝试使用它。我们拿 wordexp() from posix :

    The words argument is a pointer to a string containing one or more words to be expanded. The expansions shall be the same as would be performed by the command line interpreter if words were the part of a command line representing the arguments to a utility. [...]


    所以让我们转到“命令行解释器”。来自 posix shell command language :

    2.1 Shell Introduction

    [...]

    1. The shell breaks the input into tokens: words and operators; see Token Recognition.[.......]

    2.3 Token Recognition

    [...]

    1. If the current character is an unquoted <blank>, any token containing the previous character is delimited and the current character shall be discarded.
    2. If the previous character was part of a word, the current character shall be appended to that word.

    [...]


    基本上全 2.3 Token Recognition部分适用于此处 - 这就是 wordexp()确实 - token 识别加上一些扩展。还有关于 field splitting 的最重要的东西,强调我的:

    After parameter expansion (Parameter Expansion), command substitution (Command Substitution), and arithmetic expansion (Arithmetic Expansion), the shell shall scan the results of expansions and substitutions that did not occur in double-quotes for field splitting and multiple fields can result.

    IFS影响字段拆分,它会影响 其他扩展的结果 被吐槽成文字。 IFS不影响字符串如何拆分为标记,它仍然使用 <blank> 拆分- 制表符或空格。所以你看到的行为。
    换句话说,当您输入 IFS=: 时在您的终端中,那么您就不会开始通过 IFS 分隔 token , 喜欢 echo:Hello:World ,但仍继续使用空格分隔命令的各个部分。
    无论如何,手册页是正确的......:p

    Am I missing something and there is a way to get wordexp to split on characters other than whitespace?


    不。如果您想在单词中有空格,请引用参数,就像在 shell 中一样。 "a b" "c d" "e" .

    If the split is only on whitespace, is this a bug in the


    无:p

    关于c - gcc wordexp Posix C 库函数不使用 Linux IFS 环境变量来拆分单词,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65822194/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com