在 bash 中重建制表符分隔的记录不起作用

在 bash 中重建制表符分隔的记录不起作用

我需要使用 bash 解析制表符分隔的 CSV 文件,检查记录的内容,如果记录满足特定条件,则将其添加到数组中。基本上,我想先从 CSV 文件中过滤出记录,然后再对它们进行处理。

我的想法是获取文件中的每一行,将每个字段放入一个数组中。然后我可以查看数组以查看记录是否满足某些条件(例如 field3="value" 等)。如果是,我将“重建”制表符分隔行并将其附加到新数组中。

这似乎失败的是我创建的行record。它似乎是附加一个空格而不是制表符,因为稍后, 的大小与details记录是用空格而不是制表符分隔的一样。

datafile=path/to/data.csv
records=()
header=$(head -n 1 $datafile)
IFS=$'\t' read -r -a fields <<< "$header"

while IFS=$'\t' read -r -a documents; do

    # processing to determine if current row in csv file matches certain criteria
    # if it does, the following will happen

    for r in ${documents[@]}; do record+="$r"$'\t'; done #appending space instead?
    records+="$record"
done < $datafile

for r in "${records[@]}"; do
    IFS=$'\t' read -r -a details <<< "$r"

    # size of details here is as if record is separated by spaces instead of tabs

    for i in "${!fields[@]}" ; do
        echo "${fields[i]}: ${details[i]}"
    done
done

示例:如果此记录是进程:

Hello World  [TAB]  nice weather we are having today  [TAB]  do you agree?

的大小details应该是 3,但我得到的是 11。为什么?

答案1

你的问题已被涵盖为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?。我将简要解释一下这里发生的事情。

罪魁祸首是for r in ${documents[@]}。由于变量扩展未加引号,因此您使用“split+glob”操作:每个数组元素的值根据 的值拆分为单词IFS,并且每个单词都被视为通配符模式。因为您只设置了IFS持续时间read(请参阅为什么如此频繁地使用“while IFS= read”,而不是“IFS=;”在阅读时..`?),IFS此时的值为默认值,其中包含空格。此外,如果您有一个包含类似 的字段foo *,您会看到当前目录中的文件名出现。解决方案是for r in "${documents[@]}",这是迭代数组的标准方法:双引号将其转换为直接变量取消引用,没有拆分和通配符,并且导致[@]每个数组元素被放置在单独的字中。

虽然对整个脚本的设置IFS=$'\t'似乎可以解决问题,但实际上它只解决了问题的一半:它不能防止${documents[@]}.虽然您可以使用 关闭通配符set -f,但使用双引号更清晰。

答案2

问题显然与 的多重声明有关IFS=$'\t'。删除它们并仅进行一个声明似乎IFS就解决了问题。

(尽管对于我的一生来说,我没有看到为什么这是一个问题。肯定有一个微妙的错字。)

相关内容