• 关于 Word Splitting 和 IFS 的三个细节


    在 Bash manual 里叫 Word Splitting,在 Posix 规范里叫 Field Splitting,这两者指的是同一个东西,我把它翻译成“分词”,下面我就说三点很多人都忽略掉(或者说从没仔细考虑过)的分词细节。

    1. 分隔符到底是字符还是字符串?

    IFS 里面可以包含多个字符,那么在分词的过程中,是 IFS 中的每个单独的字符作为分隔符,还是由这些字符组合成的任意字符串作为分隔符?我们写个简单的例子证明一下:

    $ var=a12b21c IFS=12

    $ printf "<%s> " $var

    <a>

    <>

    <b>

    <>

    <c>

    由于输出了两个空的字段,也就证明了是 1 和 2 两个单独的 IFS 字符作为了分隔符,而不是 12 和 21 这两个由 IFS 字符组成的字符串作为了分割符。但结论没这么简单,再看一个例子:

    $ var=$'1   2' IFS=$'  ' #红色背景的是空格

    $ printf "<%s> " $var

    <1>

    <2>

    在这个例子中,IFS 包含两个字符:空格符和制表符, 如果说它们俩是单独作为分隔符的,那么 $var 就应该被分割成四个字段,分别是 <1> <> <> <2>,但实际的结果并不是这样的。这是因为:空格符、制表符( )、换行符( )这三个空白符在 IFS 中会被特殊对待,Shell 会把它们按照任意顺序任意数量组合成的字符串作为分隔符,而不是单个字符作为分隔符。在这个例子中,是“   ”整体作为了一个分隔符,把 1 和 2 分成了两个字段。下面再演示一下 IFS 为换行符的情况:

    $ var=$'1 2 3' IFS=$' '

    $ printf "<%s> " $var

    <1>

    <2>

    <3>

    这个例子中,三个连续的换行符作为了分隔符,把 var 分成了三个字段。

    如果 IFS 既包含空白符,又包含非空白符,会怎么样?

    看下面的例子,IFS 中既有空白符 又有非空白符 2:

    $ var=$'1 2 3' IFS=$' 2'

    $ printf "<%s> " $var

    <1>

    <3>

    咦?有些同学就想问了:上面不是说,Shell 会把以任意个 IFS 包含的空白符组成的字符串作为分隔符,把单个 IFS 中包含的非空白符作为分隔符吗,那不就是有三个分隔符:“ ”、“2”、“ ”吗?但从表现上来看,是“ 2 ”整体作为了一个分隔符,这是怎么回事?

    下面我们就再说个法则:“一个 IFS 中包含的非空白符会和它两边存在的由 IFS 中包含的空白符组成的字符串组合成一起作为分隔符”。在上面的例子中,就是 2 和它两边的 “ ” 5个字符组合起来作为了一个分割符,所以产生了 1 和 3两个字段。

    2. 尾部的空字段会被丢弃

    $ var=:1:2:3: IFS=:

    $ printf "<%s> " $var

    <>

    <1>

    <2>

    <3>

    四个分隔符,应该把 var 切割成 5 个字段,但从结果上看,尾部的空字段不见了?是的,再说一个法则:分词之后,如果最后一个字段是空的,那么这个字段会被丢弃掉。其实,一个包含空值的变量在分词之后会被丢弃,也符合这条法则:

    $ var=""

    $ set -- $var

    $ echo $#

    0

    上面的例子中,var 的值就是空,所以在分词之后也是只有一个空的字段,也是最后一个字段,符合尾部空字段被丢弃的法则,所以 set 命令只看到了 -- 这一个参数。 

    3. 首尾的空白符序列会被丢弃掉

    $ var=$' 1:2 ' IFS=$' :'

    $ printf "<%s> " $var

    <1>

    <2>

    这个例子中,分割符应该有三个,分别是 、:、 ,它们会把 var 分割成四个字段 <> <1> <2> <>,尾部的字段是空的,被丢弃,就成了 <> <1> <2>。咦?WTF,为什么和真实的输出不符!下面是最后一条法则:在正式分词之前,变量两边的由 IFS 包含的空白符组合成的序列会被丢弃掉,然后才进行正式分词。在上面的例子中,var 会先被切头去尾,也就变成 “1:2”,才进行正式的分词,也就最终被分成了 1 和 2 两个字段了。注意,首尾的空白符序列只包含由 IFS 中包含的空白符组成的序列,比如上面的例子改一下:

    $ var=$' 1:2 ' IFS=$' :'

    $ printf "<%s> " $var

    <

    1>

    <2

    >

    由于 没有包含在 IFS 中,所以 var 首尾的 也就不会被去掉。 关于这点,Bash 的文档记载有 bug,我给提 bug 修复了。

    最后说一句,本文中所举的例子都是用 parameter expansion 来演示的,command substitution 和 arithmetic expansion 虽然没有演示,但同样适用。

  • 相关阅读:
    使用Dagger2做静态注入, 对比Guice.
    利用Cglib实现AOP
    Guice之IOC教程
    layui使用心得
    Protocol Buffer序列化对比Java序列化.
    IE之页面加载慢.
    浏览器Agent大全 (含IE 11, Edge)
    ASpectJ对AOP的实现
    Spring之AOP
    创建自己的加密货币MNC——以太坊代币(二)
  • 原文地址:https://www.cnblogs.com/ziyunfei/p/4898318.html
Copyright © 2020-2023  润新知