将文件中的行读入Bash数组[重复]

Posted

技术标签:

【中文标题】将文件中的行读入Bash数组[重复]【英文标题】:Read lines from a file into a Bash array [duplicate] 【发布时间】:2012-07-08 18:41:15 【问题描述】:

我正在尝试将包含行的文件读入 Bash 数组。

到目前为止,我已经尝试了以下方法:

尝试1

a=( $( cat /path/to/filename ) )

尝试2

index=0
while read line ; do
    MYARRAY[$index]="$line"
    index=$(($index+1))
done < /path/to/filename

这两种尝试都只返回一个包含文件第一行的单元素数组。我做错了什么?

我正在运行 bash 4.1.5

【问题讨论】:

您不需要使用 while 循环维护索引。您可以像这样附加到数组:myarray+=($line)。如果您需要增加一个整数,您可以使用(( index++ ))(( index += 1 )) @DennisWilliamson 或let index++ @DennisWilliamson ((index++)) 有一个返回值,如果在set -e 模式下运行,它可能会终止脚本。这同样适用于let index++。使用A=$((A+1)) 是安全的。 @ceving:你永远不应该使用set -e,它是无用的遗物。使用适当的错误处理。 @DennisWilliamson 我喜欢它,因为它高效而且非常有用。 set -eu 是我的标准前奏。 【参考方案1】:

The readarray command(也拼写为mapfile)是在 bash 4.0 中引入的。

readarray -t a < /path/to/filename

【讨论】:

我认为你在 bash 4 中引入是对的,这在 bash 3.2 中不起作用 当我发表评论时,我可能不确定它是在 4.0、4.1 还是 4.2 中。无论如何,bash release notes 确认它是在 4.0 中添加的。 readarray 不使用IFS;它仅使用每个元素一行来填充命名数组,没有字段拆分。 我建议在答案中添加-t 以去除换行符。使数组更容易使用(例如,用于字符串比较),而且您通常不会想要保留换行符。 @AquariusPower bash 4.4 将向readarray 添加一个-d 标志,以指定一个替代字符来终止输入的每一行。【参考方案2】:

最新版本基于BinaryZebra's comment 的评论 和tested here。添加command eval 允许将表达式保留在当前执行环境中,而之前的表达式仅在 eval 期间保留。

使用没有空格\制表符,只有换行符/CR的$IFS

$ IFS=$'\r\n' GLOBIGNORE='*' command eval  'XYZ=($(cat /etc/passwd))'
$ echo "$XYZ[5]"
sync:x:5:0:sync:/sbin:/bin/sync

另请注意,您可能将数组设置得很好,但读错了 - 请务必使用双引号 "" 和大括号 ,如上例所示


编辑:

请注意我在 cmets 中关于可能的全局扩展的回答的许多警告,特别是 gniourf-gniourf's comments 关于我之前尝试解决的问题

考虑到所有这些警告,我仍然在这里留下这个答案(是的,bash 4 已经出现多年,但我记得一些只有 2/3 岁的 mac 具有 pre-4 作为默认 shell)

其他说明:

也可以按照 drizzt 下面的建议,用

替换分叉的 subshel​​l+cat
$(</etc/passwd)

我有时使用的另一个选项是将 IFS 设置为 XIFS,然后再恢复。另请参阅Sorpigal's answer,它不需要为此烦恼

【讨论】:

为什么将IFS设置为回车换行? \r 不会出现在具有正确行尾的文件中,当然包括 passwd echo "$XYZ[@]" 将所有元素打印为一行;要将每个元素放在单独的行上,请使用 printf "%s\n" "$XYZ[@]". 为什么要使用无用的分叉?只需使用 $(/passwd) 这就是我使用 bash 25 年来变量赋值的工作原理。 "X=a Y=b" 设置 shell 的变量。 "X=a Y=b executable" 仅为该一次执行的 fork 和 exec 环境设置变量。这 ”:;”在给定的答案中会适得其反并污染外壳。 如果编辑被拒绝,我添加的是Placing variables in the environment of the split is done with command eval: IFS=$'\r\n' GLOBIGNORE='*' command eval 'XYZ=($(cat /etc/passwd))'作为前两行。随意编辑,因为无论如何这都是您的答案。 nJoy!【参考方案3】:

将文件的每一行读入bash 数组的最简单方法是:

IFS=$'\n' read -d '' -r -a lines < /etc/passwd

现在只需索引数组lines 即可检索每一行,例如

printf "line 1: %s\n" "$lines[0]"
printf "line 5: %s\n" "$lines[4]"

# all lines
echo "$lines[@]"

【讨论】:

所有行,每行一个:printf '%s\n' "$lines[@]". 这将丢弃文件中的空白行:mywiki.wooledge.org/BashFAQ/… 在这种情况下,read 返回false,因此您无法区分正确的功能或读取错误等错误。 readarray 是更好的选择。 @Magnus:它使读取将输入拆分为换行符上的字段。如果您省略它也会发生这种情况,但您将另外拆分另一个默认输入字段分隔符:空格。如果您的文件的行可能有空格,这将导致不同的结果。如果您的bash 足够新,则无论如何都应该使用mapfile -t lines &lt; /etc/passwd,这样更有效且同样安全。 如果你有一个旧版本的 Bash 没有 mapfilereadarray(例如 Mac 的古老默认版本 Bash),那么你必须使用这个方法。由于read 在此处返回false,因此如果您启用了错误检查 (set -e),您可以将|| true 添加到命令末尾以避免程序在此处退出。【参考方案4】:

如果文件包含不带空格且每行 1 个字符串的字符串,另一种方法:

fileItemString=$(cat  filename |tr "\n" " ")

fileItemArray=($fileItemString)

检查:

打印整个数组:

$fileItemArray[*]

Length=$#fileItemArray[@]

【讨论】:

小心! 这会扩展 shell 元字符,例如 fileItemString='*'。只有在关闭 globbing 时才能安全使用,这反过来又会使 shell 大部分无用。 这会将文件中的每个空格都视为分隔符(不仅是\n)。 IE。如果文件中的第 N 行是“foo bar”,则结果数组将包含单独的 foobar 条目,而不是单个 foo bar 条目。【参考方案5】:

您的第一次尝试很接近。这是使用您的想法的简单方法。

file="somefileondisk"
lines=`cat $file`
for line in $lines; do
        echo "$line"
done

【讨论】:

这很接近,但没有回答有关填充数组的部分。 不,lines 在这里不是数组;它只是一个字符串。当然,您在空格上拆分该字符串以对其进行迭代(并扩展它包含的任何 glob),但这不会使其成为数组。 小心! 这会扩展 shell 元字符,例如 lines='*' 虽然不是直接回答问题,但这个 sn-p 实际上解决了我在 google 引导我到这个页面时遇到的问题。 这会将文件中的每个空格都视为分隔符(不仅是 \n)。 IE。如果文件中的第 N 行是“foo bar”,则结果输出将包含 foobar 作为单独的行,而不是单个 foo bar 行。【参考方案6】:
#!/bin/bash
IFS=$'\n' read  -d '' -r -a inlines  < testinput
IFS=$'\n' read  -d '' -r -a  outlines < testoutput
counter=0
cat testinput | while read line; 
do
    echo "$(($inlines[$counter]-$outlines[$counter]))"
    counter=$(($counter+1))
done
# OR Do like this
counter=0
readarray a < testinput
readarray b < testoutput
cat testinput | while read myline; 
do
    echo value is: $(($a[$counter]-$b[$counter]))
    counter=$(($counter+1))
done

【讨论】:

坚持readarray,因为read返回false,所以第一个解决方案在set -e下失败。另请注意,循环后counter 仍然是0,因为它是在子shell 中完成的(由于管道)。 OSX 没有readarray @cmcginty,...因为 Apple 正在运送一份 bash 的副本,它几乎可以拿到驾照了,是的;对于关心 bash 的人来说,他们真的应该安装一个更新的而不是使用操作系统供应商提供的副本。 这里有一个问题:read -d'' -rread -d -r 完全相同; - 中的 -r 成为分隔符。这是不正确的;它应该read -d '' -r-d'' 之间的空间很重要。 要测试它,请尝试读取包含破折号的示例输入 - 它将被截断。

以上是关于将文件中的行读入Bash数组[重复]的主要内容,如果未能解决你的问题,请参考以下文章

将csv字符串读入bash数组

使用 bash 将多个匹配项读入数组

BASH - 从 csv 文件的行创建数组,其中第一个条目是数组名称

通过bash中的值访问数组的索引[重复]

从bash中的yaml数组获取值[重复]

Bash 脚本二进制搜索