将制表符分隔的文件逐行读入数组

84
我想将一个文件逐行读入脚本。文件中每一行都是由制表符分隔的多个值,我想将每一行读入一个数组。
典型的bash“逐行读取文件”的示例;
while read line
do
echo $line;
done < "myfile"

然而对于我来说,myfile看起来是这样的(制表符分隔的值):

value1 value2 value3
value4 value5 value6

在循环的每次迭代中,我希望每行都进入一个数组中,以便我可以

while read line into myArray
do
 echo myArray[0]
 echo myArray[1]
 echo myArray[2]
done < "myfile"
这将在第一次循环迭代中打印以下内容;
value1
value2
value3

然后在第二次迭代中,它将会打印

value4
value5
value6

这个可行吗?我唯一能想到的方式是编写一个小函数手动分解值,Bash 中是否有内置支持呢?

3个回答

193
你已经非常接近了:
while IFS=$'\t' read -r -a myArray
do
 echo "${myArray[0]}"
 echo "${myArray[1]}"
 echo "${myArray[2]}"
done < myfile

-r 告诉 read 在输入数据中 \ 不是特殊字符;-a myArray 告诉它将输入行分割成单词并将结果存储在 myArray 中;IFS=$'\t' 告诉它仅使用制表符来分割单词,而不像 Bash 默认情况下还允许空格来分割单词。请注意,这种方法将把一个或多个制表符视为分隔符,因此如果任何字段为空,后续字段将被“移动”到数组中较早的位置。可以吗?)

7
这是一个很棒的回答,感谢您这样详细地解释,我非常感激。正是我所需要的,谢谢 :D - jwbensley
不,如果你删除 echo "${myArray[1]}"echo "${myArray[2]}",它仍然会给出相同的输出。 - Ahmed Hussein
@AhmedHussein:这是一个有趣的说法。在OP的情况下,显然是错误的-- echo会添加一个换行符,而OP肯定不会混淆输出是否在正确的位置有换行符--但如果你有类似但不同的情况,我邀请你发布自己的问题,并提供足够的细节让别人帮助你。(如果你在这里评论你的问题链接,我会看一下。) - ruakh
我正在尝试在geonames dumps上进行操作,有时会出现多个列为空的情况,这意味着多个制表符被视为一个,就像你所描述的“shift”一样。我该如何避免这种情况? - giorgio79
1
@giorgio79:我能想到的最干净的方法是将整行读入一个变量(例如 IFS= read -r line),然后使用 readarray 将该变量“分割”成一个数组:readarray -d $'\t' -t myArray < <(printf %s "$line")。(注意:使用 < <(printf %s "$line") 而不是 <<<"$line" 的原因是后者会添加一个换行符,这个换行符会被包含在最后一个字段中。) - ruakh
@ruakh:完美、清晰且解釋得很好。幹得漂亮! - schweik

22
如果你真的想把值放在一个数组里,那么@ruakh的答案是正确的方法。但是read也支持将每个值放入单独的变量中,如果你有可以用于它们的有意义的名称,这种方式更易读。例如,如果三列分别是用户ID、用户名和电子邮件地址,那么你可以这样写:
while IFS=$'\t' read -r user_id username email ; do
  echo "${user_id}"
  echo "${username}"
  echo "${email}"
done < "myfile"

实际上,由@gniourf_gniourf添加的-r选项避免了反斜杠字符的扩展。如果这样做,printf格式字符串中的%b可以被替换为%s,因为反斜杠转义字符将被表示为字面值。因此,使用或不使用取决于您真正想要做什么。 - slylittl3

0
您也可以尝试,
OIFS=$IFS;
IFS="\t";

animals=`cat animals.txt`
animalArray=$animals;

for animal in $animalArray
do
    echo $animal
done

IFS=$OIFS;

1
似乎是对 cat 命令的无用使用。https://dev59.com/pmgt5IYBdhLWcg3w7BuL - jwbensley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接