【问题标题】:How bash IFS is working in this example?bash IFS 在这个例子中是如何工作的?
【发布时间】:2012-10-17 11:18:46
【问题描述】:

我正在阅读有关 bash internal variables 的信息,并遇到了这个 IFS 示例:

output_args_one_per_line()
{
  for arg
  do
    echo "[$arg]"
  done #  ^    ^   Embed within brackets, for your viewing pleasure.
}

案例1

IFS=" "
var=" a  b c   "

output_args_one_per_line $var
#OUTPUT
# [a]
# [b]
# [c]

案例2

IFS=:
var=":a::b:c:::"               # Same pattern as above
CASE2   
                        # but substituting ":" for " "  ...
output_args_one_per_line $var
# []
# [a]
# []
# [b]
# [c]
# []
# []

现在,根据我的理解,如果 IFS 的值是默认的 \t\n,那么会删除前导和尾随空格。因此,对于 case1,bash 将 var 视为 a b c,因此是输出。

根据我的说法,对于 case2,bash 在此处将 var 视为 |a||b|c|||| 视为 space。我使用

检查了它
Noob@Noob:~/tmp$ IFS=$':' FOO=$":a::b:c:::"; echo $FOO $'x'
 a  b c   x

所以,我对案例 2 的预期输出是

# []
# [a]
# []
# []
# [b]
# [c]
# []
# []
# []

那么,有人可以在内部向我解释 bash 在情况 2 中如何处理 var 以及我的理解哪里出错了。

【问题讨论】:

  • OT:您的 output_args_one_per_line 函数可以替换为 printf "[%s]\n" $var
  • var 展开时,查看bash -x 显示的拆分。请参阅我的答案的编辑。

标签: bash ifs


【解决方案1】:

您的声明(编辑为使用“:”而不是“|”):

根据我的说法,对于 case2,bash 将 var 视为 :a::b:c::: 将 : 视为此处的空格。

是假的。 IFS 导致 bash: 视为单词分隔符,而不是空格。不要因为空格是 默认 单词分隔符而混淆两者。

【讨论】:

  • 你说得对,我说错了,我真正的意思是 bash 使用空格作为分隔符将 a:b 更改为 a b
【解决方案2】:

案例 2 上,您有 ":a::b:c:::"IFS=:
所以你正在做的是在每个遇到:

所以,从字符串的开头,直到第一个 : 你有 ":
什么都没有,因此[]

从第一个: 到下一个: 你有:a:
这是a,因此是[a]

从那里到下一个: 你有::
什么都没有,因此[]

从那里到下一个: 你有:b:
这是b,因此是[b]

从那里到下一个: 你有:c:
这是c,因此是[c]

从那里到下一个: 你有::
什么都没有,因此[]

从那里到下一个: 你有::"
什么都没有,因此[]

所以你得到了那个结果..


正如 @Mark Reed 在 cmets 中提到的,您可以使用 printf
bash -x 一起获得:

$ bash -x
$ IFS=':'; var=':a::b:c:::'; printf "[%s]\n" $var
[12]+ IFS=:
[12]+ var=:a::b:c:::
[12]+ printf '[%s]\n' '' a '' b c '' ''     # notice this
[]
[a]
[]
[b]
[c]
[]
[]

【讨论】:

    【解决方案3】:

    : 是分隔符。 a:b 分为 ab,两者之间没有任何区别。在您的预期行为中,您将如何编码以下内容?

    [a]
    []
    [b]
    

    唯一奇怪的是最后没有三个空字符串。这可能是因为

    不带引号的隐式空参数,由没有值的参数扩展产生,被删除。

    【讨论】:

    • 如果我理解正确的话,a::b 将被视为a(space)(empty word)b which will result into a[]b。您能否详细说明一下strange thing,因为我是一个 bash 菜鸟,没有示例,我不太了解官方文档。
    • 你从一开始就分裂到下一次遇见IFS。然后从那个点到下一个遇到IFS,等等。像:: 这样的字符串将返回[],这是空字符串,因为从第一个: 到下一个@987654334 @你之间什么都没有,但是分裂发生了。
    • @Noob - 在您的预期输出中,您声称a::bab 之间应该有两个空字符串,而实际上它应该只有一个......
    • @c00kiemon5ter:谢谢,非常有帮助的解释。
    • 删除尾随空参数(但不是前导参数)来自 POSIX 标准的措辞。据报道,委员会对此进行了激烈的辩论,并且在许多关心这类事情的人中是一个不受欢迎的决定。但它至少在各种 shell(ksh、{d、}ash、zsh 等)中是一致的。这只是您必须考虑的怪癖之一。
    【解决方案4】:

    原因很简单:

    IFS=" "
    var=" a  b c   "
    
    output_args_one_per_line $var
    

    这意味着output_args_one_per_line 是用这个参数调用的:

    output_args_one_per_line  a  b c   
    

    在解析命令行时,BASH 会去掉多余的空格,所以实际调用会用到

    output_args_one_per_line a b c
    

    即多个空格合并为一个,a 之前的空格将成为命令和第一个参数之间的空格。

    这意味着空格将在 IFS 应用之前消失。这也意味着你不能写

    IFS=:
    output_args_one_per_line:$var
    

    命令后必须有空格,不能是单词分隔符。

    您可以使用 set -x 运行脚本以查看跟踪输出(即 BASH 如何扩展行)。

    在第二种情况下,单词分隔符不是空格字符,因此命令和第一个参数之间的必要空格不会与参数合并,并且该行变为

    output_args_one_per_line :a::b:c:::
    

    唯一奇怪的是输出应该是c 之后的三个空参数,但这可能是因为空的尾随参数被删除(就像 BASH 删除参数后的空格一样)。这是另一个奇怪的输出:

    IFS=:
    var=":a::b:c::: "   # Blank after C
    > output_args_one_per_line $var
    []
    [a]
    []
    [b]
    [c]
    []
    []
    [ ]
    

    因此,如果var 在最后一个冒号之后确实包含任何内容,我们会得到缺少的参数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-05
      • 2017-11-26
      • 2021-10-14
      • 2021-10-05
      • 1970-01-01
      • 2014-10-22
      • 2018-08-04
      • 1970-01-01
      相关资源
      最近更新 更多