从文件读取行到Bash数组

Hom*_*lli 180 arrays bash

我试图将包含行的文件读入Bash数组.

到目前为止,我已尝试过以下内容:

Attempt1

a=( $( cat /path/to/filename ) )
Run Code Online (Sandbox Code Playgroud)

ATTEMPT2

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

两次尝试仅返回包含文件第一行的一个元素数组.我究竟做错了什么?

我正在运行bash 4.1.5

che*_*ner 231

readarray命令(也拼写mapfile)是在bash 4推出,我相信.

readarray -t a < /path/to/filename
Run Code Online (Sandbox Code Playgroud)

  • 我建议在答案中添加`-t`来删除换行符.使用数组更容易(例如,用于字符串比较)并且通常不会想要保留换行符. (62认同)
  • 我认为你在bash 4中被引入是正确的,这在bash 3.2中不起作用 (8认同)
  • 当我发表评论时,我可能不确定它是在4.0,还是4.1或4.2.无论如何,[bash发行说明](http://tiswww.case.edu/php/chet/bash/NEWS)确认它是在4.0中添加的. (4认同)
  • `readarray`不使用`IFS`; 它只填充命名数组,每个元素一行,没有字段拆分. (4认同)
  • @AquariusPower`bash` 4.4将在`readarray`中添加一个`-d`标志,以指定一个替代字符来终止输入的每一行. (3认同)
  • 一个问题:不要忘记在尝试处理命令输出时使用进程替换。否则它不起作用。示例:`readarray my_array &lt; &lt;(command)` (2认同)

nhe*_*hed 119

最新修订基于BinaryZebra评论的评论在此进行测试.添加command eval允许表达式保持在当前执行环境中,而之前的表达式仅在eval的持续时间内保持.

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

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

另请注意,您可能正在设置数组,但读错了 - 请务必同时使用双引号""和括号,{}如上例所示


编辑:

请注意关于我可能的全球扩展的评论中有关我的答案的许多警告,特别是gniourf-gniourf关于我之前尝试解决的问题的评论

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

其他说明:

也可以按照下面的drizzt的建议,用一个分叉的子壳+猫替换

$(</etc/passwd)
Run Code Online (Sandbox Code Playgroud)

我有时使用的另一个选项是将IFS设置为XIFS,然后恢复.另见Sorpigal的答案,不需要为此烦恼

  • `echo"$ {XYZ [@]}"`将所有元素打印为单行; 将每个元素放在一个单独的行上使用`printf"%s \n""$ {XYZ [@]}"`. (4认同)
  • 为什么将“ IFS”设置为回车和换行?\ r将不会出现在带有适当行尾的文件中,其中肯定包含`passwd`。 (2认同)
  • 为什么用无用的叉?只需使用$(</ etc/passwd) (2认同)
  • 这就是 bash 中变量赋值的工作方式,我已经使用它 25 年了。“X=a Y=b”设置 shell 的变量。“X=a Y=b 可执行文件”仅为该一次执行的 fork 和 exec 环境设置变量。这 ”:;” 在给定的答案中会适得其反并污染外壳。 (2认同)
  • 如果编辑被拒绝,我添加的是`在拆分环境中放置变量是使用命令eval完成的:IFS = $'\ r \n'GLOBIGNORE ='*'命令eval'XYZ =($(cat/etc/passwd))'`作为前两行.随意编辑,因为这是你的答案.的nJoy! (2认同)

Sor*_*gal 102

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

IFS=$'\n' read -d '' -r -a lines < /etc/passwd
Run Code Online (Sandbox Code Playgroud)

现在只需索引到数组中lines以检索每一行,例如

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

# all lines
echo "${lines[@]}"
Run Code Online (Sandbox Code Playgroud)

  • 所有行,每行一行:`printf'%s \n'"$ {lines [@]}"`. (8认同)
  • 这将丢弃文件中的空白行:http://mywiki.wooledge.org/BashFAQ/005#Loading_lines_from_a_file_or_stream (5认同)
  • 在这种情况下,“ read”会返回“ false”,因此您无法区别于正确的功能或诸如读取错误之类的错误。`readarray`是更好的方法。 (3认同)
  • 如果你有一个旧版本的Bash,没有`mapfile`或`readarray`(例如Mac的古老默认版本Bash),那么你必须使用这个方法。由于“read”在这里返回“false”,因此您可以添加“||” 如果您启用了错误检查(`set -e`),则在命令末尾添加 true` 以避免程序在此处退出。 (2认同)

小智 20

一种替代方法,如果文件包含没有空格的字符串,每行包含1个字符串:

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

fileItemArray=($fileItemString)
Run Code Online (Sandbox Code Playgroud)

校验:

打印整个数组:

${fileItemArray[*]}

Length=${#fileItemArray[@]}
Run Code Online (Sandbox Code Playgroud)

  • **当心!** 这会扩展 shell 元字符,例如如果 `fileItemString='*'`。只有在 globbing 关闭时才能安全使用,这反过来会使 shell 变得毫无用处。 (3认同)

小智 13

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

file="somefileondisk"
lines=`cat $file`
for line in $lines; do
        echo "$line"
done
Run Code Online (Sandbox Code Playgroud)

  • 不,`lines`在这里不是*数组; 它只是一个字符串.当然,你要在空格上拆分该字符串来迭代它(并且还扩展它包含的任何globs),但这并不能使它成为一个数组. (7认同)
  • **注意!**这会扩展shell元字符,例如`lines ='*'`. (6认同)
  • 这很接近,但没有回答关于填充数组的部分. (5认同)
  • 虽然不是直接回答问题,但这个片段实际上解决了我在谷歌引导我到这个页面时遇到的问题。 (4认同)
  • 这会将文件中的每个空格视为分隔符(不仅是 \n)。即如果文件中的第 N 行是“foo bar”,则结果输出将包含 `foo` 和 `bar` 作为单独的行,而不是单个的 `foo bar` 行。 (3认同)