【问题标题】:read -N and IFS读取 -N 和 IFS
【发布时间】:2015-08-23 11:22:27
【问题描述】:

根据手册页中的“read -N”描述:

-N nchars 仅在准确读取 NCHARS 字符后返回,除非遇到 EOF 或读取超时,忽略任何分隔符

但是,响应以下命令:

$ echo 'a b' | while read -N1 c; do echo ">>>$c<<<"; done
>>>a<<<
>>><<<
>>>b<<<
>>><<<

空格和换行符都被翻译成空字符串,而在命令中:

$ echo 'a b' | while IFS= read -N1 c; do echo ">>>$c<<<"; done
>>>a<<<
>>> <<<
>>>b<<<
>>>
<<<

空格和换行符已正确存储在变量中。

所以,分隔符在“read”或“while”命令中似乎仍有一些处理,我不明白。

我们可以将这些结果与使用“read -n”的结果进行比较,该手册描述为:

-n nchars 在读取 NCHARS 个字符后返回,而不是等待换行符,但如果在分隔符之前读取的 NCHARS 个字符少于此,则使用分隔符

$ echo 'a b' | while read -n1 c; do echo ">>>$c<<<"; done
>>>a<<<
>>><<<
>>>b<<<
>>><<<

$ echo 'a b' | while IFS= read -n1 c; do echo ">>>$c<<<"; done
>>>a<<<
>>> <<<
>>>b<<<
>>><<<

【问题讨论】:

    标签: bash ifs


    【解决方案1】:

    这是POSIX 行为。分配给变量时,应去除 IFS 字符:结果应像在 shell 中一样被拆分为字段以获取参数扩展的结果(当然,-n 和 -N 不是 POSIX)。

    这是read源代码cmets的诞生:

    /* This code implements the Posix.2 spec for splitting the words
         read and assigning them to variables. */
      orig_input_string = input_string;
    
      /* Remove IFS white space at the beginning of the input string.  If
         $IFS is null, no field splitting is performed. */
    

    【讨论】:

    • 非常有趣。只有在“while IFS= read -n1 c”的情况下将换行符转换为空字符串似乎很难与这些描述相匹配。我期望或“循环结束”并且没有打印或换行分配。事实上,这种情况是 -n1 和 -N1 测试之间发现的唯一区别。
    • 换行符是默认IFS 设置的一部分,换句话说,它是一个分隔符。我没有看到任何不一致之处。
    • 是的,但默认 IFS(未设置 IFS)与空 IFS 不同,即在此集中使用的 IFS。此外,空格也在默认分隔符集中,并且在本次测试中以不同的方式处理。
    • 默认 IFS 不是未设置的 IFS。默认设置为' \r\n'
    【解决方案2】:

    在我看来,在使用选项-N 时,read 的行为会有所不同

    • 读取分隔符作为输入
    • 分配分隔符给变量

    在读取字符时,分隔符与非分隔符相同,read 将计算它们。但是,当read 分配分隔符时,它会考虑读取的输入是否为分隔符,如果是分隔符,则将 null 分配给相应的变量。

    因此,IFS= 将改变将空格分配给变量的行为,并导致将空格分配给 c 而不是空值。

    【讨论】:

      【解决方案3】:

      使用hexdump 可以让我们准确地看到构成输出的字符,因此稍微更改您的查询可能会有所帮助:

      (1) 使用普通 IFS 并使用 -N 选项

      $ (echo 'a b' | while read -N1 c; do c="$c<"; echo -n "$c"; done | hexdump -C)
      00000000  61 3c 3c 62 3c 3c                                 |a<<b<<|
      00000006 
      

      在第一种情况下,0x0a 和空格字符的 read 内置函数返回空字符串,因为字符在默认 IFS 中,并且 IFS 中的字符在输出中被忽略,原因在 cdarke 的答案中解释。

      (2) 使用空 IFS 和 -N 选项

      $ (IFS=""; echo 'a b' | while read -N1 c; do c="$c<"; echo -n "$c"; done | hexdump -C)
      00000000  61 3c 20 3c 62 3c 0a 3c                              |a< <b<.<|
      00000008
      

      在这种情况下,内置命令 read 将匹配 echo 命令输出的四个字符中的每一个,并且在输出中可以看到 0x0a 和一个空格,因为使用空的 IFS 可以将读取的字符分配给局部变量c

      (3) 使用普通 IFS 和 -n 选项

      $ (echo 'a b' | while read -n1 c; do c="$c<"; echo -n "$c"; done | hexdump -C)
      00000000  61 3c 3c 62 3c 3c                                 |a<<b<<|
      00000006 
      

      这给出了与案例 (1) 相同的输出,尽管语义有点不同:0x0a 和空格字符的 read 内置函数返回空字符串,因为 (i) 这两个字符都在默认的 IFS 和 (ii) read 内置的 -n 选项在任何情况下都不会传递尾随 0x0a 字符

      (4) 使用空 IFS 和 -n 选项

      $ (IFS=""; echo 'a b' | while read -n1 c; do c="$c<"; echo -n "$c"; done | hexdump -C)
      00000000  61 3c 20 3c 62 3c 3c                              |a< <b<<|
      00000007
      

      在这里,我们观察到读取 -n 和 -N 选项之间的区别:使用 -n 选项,换行符由 read 内置函数特殊处理并删除,因此从 IFS 中排除 0x0a 没有有机会将其传递给局部变量c

      【讨论】:

      • 很好的解释,但仍然有一个开放点,即案例 (4) 中的换行符,它的处理方式与空格不同,即使不属于 IFS。也就是说,我认为在情况(3)中我们不能说“0x0a 和空格字符返回空字符串,因为这两个字符都在默认的 IFS 中”。
      • @pasabaporaqui - 你说得很对:-n 开关的语义意味着 0x0a 在 IFS 中是多余的。我已经改变了讨论以明确这种冗余。
      • 完美,只是情况(2)中的编辑评论:“IFS unset”应该是“IFS empty”。
      【解决方案4】:

      read 在读取字符之前无法确定字符是否是分隔符(忽略它),并且read 必须为c 分配 some 值,即使这样value 是空字符串。当分隔符被读取并随后被丢弃时,c 的值必须设置为 something,因此它被分配了空字符串。

      这与在没有-n/-N 选项的情况下使用的read 一致;分隔符仅在 被读取并且不需要设置所提供参数的值时被丢弃。最简单的情况是您不向read 提供任何参数:

      $ read <<< " a b c "
      $ echo ">>>$REPLY<<<"
      >>> a b c <<<
      

      使用单个显式参数,前导和尾随分隔符被剥离:

      $ read line <<< " a b c "
      $ echo ">>>$line<<<"
      >>>a b c<<<
      

      使用 两个 参数,第一个分隔符在读取后将被忽略。第二个是保留的,因为字符串只需要拆分成两个词来填充提供的参数。

      $ read field1 field2 <<< " a b c """
      $ echo ">>>$field1<<<"
      >>>a<<<
      $ echo ">>>$field2<<<"
      >>>b c<<<
      

      【讨论】:

        猜你喜欢
        • 2023-03-08
        • 2014-10-27
        • 1970-01-01
        • 2022-01-22
        • 2021-10-05
        • 2011-05-22
        • 2011-05-06
        • 1970-01-01
        相关资源
        最近更新 更多