我可以使用 read 内置函数将换行符分隔的字符串放入数组中吗?

Can I put strings divided by newlines into an array using the read builtin?

我有一个变量:

var='/path/to/filename.ext
     /path/to/filename2.ext
     /path/to/filename3.ext'

我想将所有由换行符分隔的字符串放入数组中:

declare -a arr

根据 Whosebug 上的大量帖子,我找到了几种方法:

# method 1: while loop
while read line; do
    arr+=($line)
done <<< "$var"

# method 2: readarray
readarray -t arr <<< "$var"

# method 3:
IFS=$'\n'
arr=("$var")

然而,在我学习所有这些方法之前,我正在使用另一种方法,即:

# method 4 (not working in the current situation)
IFS=$'\n'
read -a arr <<< "$var"

这不起作用,因为它只会将 var 的第一个字符串存储在 arr[0] 中。我不明白为什么它在定界符是换行符的情况下不起作用,而它 与其他定界符一起工作,例如:

IFS='|'
strings='/path/to/filename.ext|/path/to/filename2.ext|'
read -a arr <<< "$strings"

有什么我遗漏的吗?

编辑

删除了我自己的回答,认为您不能将 read 用于此目的。事实证明你可以。

原来你的答案是错误的。是的你可以!您需要使用 -d 切换到 read:

-d delim

The first character of delim is used to terminate the input line, rather than newline.

如果将它与空参数一起使用,bash 使用空字节作为分隔符:

$ var=$'/path/to/filename.ext\n/path/to/filename2.ext\n/path/to/filename3.ext'
$ IFS=$'\n' read -r -d '' -a arr < <(printf '%s[=10=]' "$var")
$ declare -p arr
declare -a arr='([0]="/path/to/filename.ext" [1]="/path/to/filename2.ext" [2]="/path/to/filename3.ext")'

成功。在这里,我们使用 printf 的进程替换,它只使用尾随空字节转储变量的内容,因此 read 很高兴并且 return 成功 return代码。您可以使用:

IFS=$'\n' read -r -d '' -a arr <<< "$var"

这样的话,arr的内容是一样的;唯一的区别是read的return代码是1(失败)。


附带说明:

之间存在差异
$ IFS=$'\n'
$ read ...

$ IFS=$'\n' read ...

前者全局设置 IFS(即,IFS 将为脚本的剩余部分保留此值——当然,直到您再次修改它):you很可能不想那样做!

后者只为命令read设置IFS你肯定想这样用!


另一个注意事项:关于您的方法 1。您缺少引号,您没有取消设置 IFS,并且您没有使用 -r 标志来 read .这很糟糕:

while read line; do
    arr+=($line)
done <<< "$var"

这个不错:

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

为什么?

  • 如果不取消设置 IFS,您将删除前导和尾随空格。
  • 如果没有 -r,一些反斜杠将被理解为转义反斜杠(\'、尾随 \\,可能还有其他)。
  • 如果不正确引用 ( "$line" ),您将打开分词和文件名扩展:如果您的输入包含空格或 glob 字符(如 *[, ?, 等等).