【问题标题】:Can't get IFS to work when converting array to string将数组转换为字符串时无法使 IFS 工作
【发布时间】:2012-12-22 02:08:53
【问题描述】:

下面是一个 bash shell 脚本,用于接收 csv 文件并以我想要的方式输出格式化的行(还有一些更改,但我只保留了影响下面显示的数组)。

FILENAME=$1
cat $FILENAME | while read LINE
do
    OIFS=$IFS;
    IFS=","
    columns=( $LINE )
    date=${columns[4]//\"/}
    columns[13]=${columns[13]//\"/}
    columns[4]=$(date -d $date +%s)
    newline=${columns[*]}
    echo $newline
    IFS=$OIFS;
done

我正在为 CentOS 6.3 使用 GNU bash v 4.1.2(1)-release。我试过把引号像

newline="${columns[*]}"

还是没有运气。

以下是示例数据行

112110120001299169,112110119001295978,11,"121.119.163.146.1322221980963094","2012/11/01"

似乎应该将数组输出为逗号分隔的字符串。相反,字符串是用空格分隔的。有谁知道原因吗?

我怀疑这与以下事实有关:如果我在脚本中回显 $IFS 它是一个空字符串,但是当我回显 "${IFS}" 时它就是我期望的逗号。

编辑:解决方案

我找到了解决方案。当回显 $newline 时,我必须在它周围使用引号,即

echo "$newline"

否则,它将使用默认空白。我相信它与bash有关,当你用引号强制它时,它只代替IFS。

【问题讨论】:

    标签: linux bash shell


    【解决方案1】:

    我不清楚为什么,但是当扩展 ${array[*]} 在双引号中时,bash 似乎只使用 IFS 的第一个字符作为分隔符:

    $ columns=(a b "c d e" f)
    $ IFS=,
    $ echo ${columns[*]}
    a b c d e f
    $ echo "${columns[*]}"
    a,b,c d e,f
    $ newline=${columns[*]}; echo "$newline"
    a b c d e f
    $ newline="${columns[*]}"; echo "$newline"
    a,b,c d e,f
    

    幸运的是,解决方案很简单:使用双引号 (newline="${columns[*]}")

    (顺便说一句,我的测试都是在 bash v3 和 v2 上进行的,因为我手边没有 v4;所以对你来说可能会有所不同。)(更新:在 bash v4.2.10 上测试,结果相同。)

    【讨论】:

    • 如果您想弄清楚原因,请阅读bash reference manual。都在里面。
    • 这是 "$*" 扩展的 POSIX 行为。基本上每个带有类似 ksh 数组的 shell 中的数组扩展都旨在类似于 @* 特殊参数。 (除了 zsh 注入了很多变化)
    • @gniourf_gniourf, @ormaaj:双引号中的行为是有道理的(并且遵循手册)。想了想,echo ${columns[*]} 也是有道理的(bash 将每个元素扩展为一个单词,以这种方式将它们传递给 echo,echo 打印它们之间有空格)。但我不明白newline=${columns[*]} 结果——手册上说[*] 没有双引号将每个元素扩展为一个单独的单词,但是在分配的 RHS 上,扩展后不会发生单词拆分(如果确实,只有第一个单词会分配给换行符),所以这似乎是一个未定义的情况。
    • 不起作用。我已经厌倦了使用双引号。它在 shell 中也适用于我,但在脚本中不起作用。
    • 其实,没关系,它确实有效。我在没有引号的情况下呼应换行符。看来 bash 在后面处理事情的方式与我的预期完全不同。
    【解决方案2】:

    编辑感谢@GordonDavidson,删除了关于 IFS 如何在 bash 中工作的错误 cmets。

    awk 有一对非常漂亮的变量,名称为 FS=","; OFS="|",它们确实执行此转换。您必须构造 awk -F, '{"date -d "$date" +%s" | getline columns[4]}' 或类似的东西来调用外部程序和填充变量。不像 shell 的 c[4]=$(date ...) 那样直观,但 awk 是一个非常好的工具,可以学习您在问题中概述的数据操作。

    有点像

    #!/bin/awk -f
    {
      # columns=( $LINE )
      split($0, columns)
    
      # date=${columns[4]//\"/}
      myDcolucolumns[4] ; gsub(/\"/, "", myDate)
      # gcolumns[13]=${columns[13]//\"/}
      gsub(/\"/,""columns[13]}
      # columns[4]=$(date -d $date +%s)
      "date -d '"$date"' +%s" | getline columns[4]
    
      #Don_t_need_this newline=${columns[*]}
    
      #echo $newline
    } print  $0
    

    习惯了

     cat myFile | myAwkScript 
    

    应该达到同样的效果。

    抱歉,我现在没有时间或样本数据来测试这个。 请随时回复您收到的错误消息,我会看看是否可以提供帮助。

    您还可以考虑使用 1 行示例数据和您要处理的日期值来更新您的帖子。 IHTH

    【讨论】:

      猜你喜欢
      • 2012-07-10
      • 1970-01-01
      • 2017-03-15
      • 2015-05-16
      • 1970-01-01
      • 2020-08-10
      • 2014-10-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多