从Bash中的文本文件创建数组


86

脚本采用一个URL,将其解析为必填字段,然后重定向其输出以保存在文件中, file.txt中。每次找到字段时,输出将保存在新行中。

file.txt

A Cat
A Dog
A Mouse 
etc... 

我要拿 file.txt在新脚本中从中创建一个数组,其中每一行都将成为数组中自己的字符串变量。到目前为止,我已经尝试过:

#!/bin/bash

filename=file.txt
declare -a myArray
myArray=(`cat "$filename"`)

for (( i = 0 ; i < 9 ; i++))
do
  echo "Element [$i]: ${myArray[$i]}"
done

当我运行此脚本时,空格导致单词被拆分,而不是被获取

所需的输出

Element [0]: A Cat 
Element [1]: A Dog 
etc... 

我最终得到这个:

实际产量

Element [0]: A 
Element [1]: Cat 
Element [2]: A
Element [3]: Dog 
etc... 

如何调整下面的循环,使每一行中的整个字符串与数组中的每个变量一一对应?


5
这就是Bash FAQ 001的全部内容。也是Bash FAQ 005中数组主题的这一部分
Etan Reisner,2015年

1
我会将其链接为stackoverflow.com/questions/11393817/…的副本,但是那里被接受的答案很糟糕。
Charles Duffy

Etan,非常感谢您如此快速准确的回复!我曾尝试在论坛中搜索我的问题,但没有想到在stackoverflow上查找FAQ。mapfile命令完全满足了我的需求!再次感谢:)在第2.1节中回答。
user2856414 2015年

2
(以相反的方向设置链接,因为我们在这里得到的答案比在那里得到的更好)。
Charles Duffy 2015年

Answers:


107

使用mapfile命令:

mapfile -t myArray < file.txt

错误正在使用for-循环遍历文件的惯用方式是:

while IFS= read -r line; do echo ">>$line<<"; done < file.txt

有关更多详细信息,请参见BashFAQ / 005


5
由于这已被推广为规范问答,因此您还可以包括链接中提到的内容:while IFS= read -r; do lines+=("$REPLY"); done <file
fedorqui'SO停止伤害

10
在4.x之前的bash版本中,mapfile不存在
ericslaw

14
Bash 4大约5岁了。升级。
格琳·杰克曼(Glenn Jackman)'17

5
尽管bash 4于2009年发布,但@ericslaw的评论仍然有意义,因为许多机器仍随附bash 3.x(并且只要bash在GPLv3下发布,就不会升级)。如果您对可移植性感兴趣,请务必注意
De Novo

12
问题不是开发人员无法安装升级版本,而是开发人员应该意识到,mapfile如果没有其他步骤,使用的脚本将无法在许多计算机上按预期运行。在可预见的将来,@ ericslaw macs将继续提供bash 3.2.57。最新版本使用的许可证要求Apple共享或允许他们不希望共享或允许的内容。
De Novo

23

mapfilereadarray(同义)在Bash版本4及更高版本中可用。如果您使用的是较旧版本的Bash,则可以使用循环将文件读入数组:

arr=()
while IFS= read -r line; do
  arr+=("$line")
done < file

如果文件的最后一行不完整(缺少换行符),则可以使用以下替代方法:

arr=()
while IFS= read -r line || [[ "$line" ]]; do
  arr+=("$line")
done < file

有关:


我发现必须使用括号IFS= read -r line || [[ "$line" ]]才能使其正常工作。否则,效果很好!
Tatiana Racheva

@TatianaRacheva:是不是之前缺少的分号do
codeforester

9

你也可以这样做:

oldIFS="$IFS"
IFS=$'\n' arr=($(<file))
IFS="$oldIFS"
echo "${arr[1]}" # It will print `A Dog`.

注意:

文件名扩展仍然发生。例如,如果有一行带有文字的行,*它将扩展到当前文件夹中的所有文件。因此,仅当您的文件没有这种情况时才使用它。


有什么方法可以IFS只暂时设置(以便在执行此命令后恢复其原始值),同时仍将赋值保留给arr
Hugues 2015年

1
请注意,文件名扩展仍会发生;例如IFS=$'\n' arr=($(echo 'a 1'; echo '*'; echo 'b 2')); printf "%s\n" "${arr[@]}"
Hugues 2015年

@Hugues:是的,文件名扩展仍然发生。我要补充的是位info..thnks的..
Jahid

抱歉,我不同意。 在当前外壳IFS=... command中不更改IFS。然而,IFS=... other_variable=...(没有任何命令)不同时更改IFSother_variable在当前外壳。
Hugues 2015年

1
谢谢!这有效;不幸的是,没有一种更简单的方法,因为我喜欢这种arr=符号(与mapfile/相比readarray)。
Hugues 2015年


4

使用mapfile或读取-a

始终使用shellcheck来检查代码。它通常会给您正确的答案。在这种情况下,SC2207涵盖了将具有空格分隔或换行分隔值的文件读入数组的工作。

不要这样

array=( $(mycommand) )

值用换行符分隔的文件

mapfile -t array < <(mycommand)

值用空格分隔的文件

IFS=" " read -r -a array <<< "$(mycommand)"

shellcheck页面将为您提供被认为是最佳实践的理由。


0

这个答案说用

mapfile -t myArray < file.txt

我做了一个垫片mapfile,如果你想使用mapfile上的bash <4.x版无论出于何种原因。它使用现有的mapfile如果您使用bash> = 4.x,命令

当前,只有选项-d-t工作。但这对于上面的命令应该足够了。我只在macOS上进行过测试。在macOS Sierra 10.12.6上,系统bash为3.2.57(1)-release。因此,垫片可以派上用场。您也可以使用自制软件更新bash,自己构建bash等。

它使用这种技术将变量设置为一个调用堆栈。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.