【问题标题】:Creating an array from a text file in Bash在 Bash 中从文本文件创建数组
【发布时间】:2015-09-08 09:56:36
【问题描述】:

脚本获取一个 URL,对其进行解析以获取所需字段,并将其输出重定向以保存在文件 file.txt 中。每次找到一个字段时,输出都会保存在一个新行上。

文件.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

当我运行这个脚本时,空格会导致单词被拆分,而不是得到 ​​p>

期望的输出

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

我最终得到了这个:

实际输出

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

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

【问题讨论】:

  • 这就是Bash FAQ 001 的全部意义所在。还有this sectionBash FAQ 005中的数组主题。
  • 我会将其链接为 stackoverflow.com/questions/11393817/… 的副本,但接受的答案很糟糕。
  • Etan,非常感谢您如此快速准确的回复!我曾尝试在论坛中搜索我的问题,但没有考虑在 stackoverflow 上查找常见问题解答。 mapfile 命令完全满足了我的需求!再次感谢 :) 回复 section 2.1
  • (设置相反方向的链接,因为我们这里的答案比那里更好)。

标签: arrays bash loops scripting variable-assignment


【解决方案1】:

使用mapfile 命令:

mapfile -t myArray < file.txt

错误是使用for——循环文件的的惯用方式是:

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

更多详情请见BashFAQ/005

【讨论】:

  • 由于这被宣传为规范的问答,您还可以包括链接中提到的内容:while IFS= read -r; do lines+=("$REPLY"); done &lt;file
  • mapfile 在 4.x 之前的 bash 版本中不存在
  • Bash 4 现在大约 5 岁了。升级。
  • 尽管 bash 4 于 2009 年发布,但@ericslaw 的评论仍然具有相关性,因为许多机器仍然附带 bash 3.x(并且不会升级,只要 bash 是在 GPLv3 下发布的)。如果您对便携性感兴趣,请务必注意
  • 问题不在于开发人员无法安装升级版本,而在于开发人员应该意识到使用mapfile 的脚本在没有额外步骤的情况下将无法在许多机器上按预期运行。在可预见的未来,@ericslaw mac 将继续随 bash 3.2.57 一起发布。较新的版本使用的许可证要求苹果共享或允许他们不想共享或允许的内容。
【解决方案2】:

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" ]] 周围加上括号才能正常工作。否则,效果很好!
  • @TatianaRacheva:不就是do之前少了个分号吗?
【解决方案3】:

你也可以这样做:

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

注意:

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

【讨论】:

  • 有什么方法可以暂时设置IFS(以便在执行此命令后恢复其原始值),同时仍将分配保留给arr
  • 注意文件名扩展仍然存在;例如IFS=$'\n' arr=($(echo 'a 1'; echo '*'; echo 'b 2')); printf "%s\n" "${arr[@]}"
  • @Hugues :是的,文件名扩展仍然存在。我将添加那一点信息..thnks..
  • 对不起,我不同意。 IFS=... command 不会更改当前 shell 中的 IFS。但是,IFS=... other_variable=...(没有任何命令)确实会更改当前 shell 中的 IFSother_variable
  • 谢谢!这行得通;不幸的是,没有更简单的方法,因为我喜欢 arr= 表示法(与 mapfile/readarray 相比)。
【解决方案4】:

使用mapfile或读取-a

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

不要这样做

array=( $(mycommand) )

值由换行符分隔的文件

mapfile -t array < <(mycommand)

值以空格分隔的文件

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

shellcheck 页面将为您说明为什么这被认为是最佳实践。

【讨论】:

    【解决方案5】:

    您可以简单地从文件中读取每一行并将其分配给一个数组。

    #!/bin/bash
    i=0
    while read line 
    do
            arr[$i]="$line"
            i=$((i+1))
    done < file.txt
    

    【讨论】:

    • 如何访问数组?
    【解决方案6】:

    This answer 说要使用

    mapfile -t myArray < file.txt
    

    如果您出于任何原因想在 bash mapfile,我为 mapfile 制作了 shim。如果您使用的是 bash >= 4.x

    ,它将使用现有的 mapfile 命令

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

    它使用this technique 将变量设置到一个调用堆栈。

    【讨论】:

      猜你喜欢
      • 2020-08-04
      • 2017-09-03
      • 2021-08-29
      • 1970-01-01
      • 2014-03-07
      相关资源
      最近更新 更多