我可以使用read builtin将字符串除以换行符放入数组中吗?

时间:2015-02-09 19:09:38

标签: arrays string bash newline delimiter

我有一个变量:

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

我想把所有字符串换成换行符的新行:

declare -a arr

基于StackOverflow上的众多帖子,我找到了几种方法:

# 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用于此目的。事实证明你可以。

1 个答案:

答案 0 :(得分:7)

事实证明你的答案错误。是的你可以!您需要使用-d切换到read

  

-d delim

     

delim 的第一个字符用于终止输入行,而不是换行符。

如果将它与空参数一起使用,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\0' "$var")
$ declare -p arr
declare -a arr='([0]="/path/to/filename.ext" [1]="/path/to/filename2.ext" [2]="/path/to/filename3.ext")'

成功。在这里,我们使用printf的进程替换,只使用尾随的空字节转储变量的内容,以便read满意并返回成功返回代码。你可以使用:

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

在这种情况下,arr的内容是相同的;唯一的区别是read的返回码是1(失败)。


作为旁注:

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

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

前者设置全局IFS(即IFS将为脚本的其余部分保留此值 - 当然,直到您再次修改它:你很可能不会'我想这样做!

后者仅为命令IFS设置read你当然希望以这种方式使用它!


另一个注意事项:关于你的方法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" ),您将获得分词和文件名扩展功能:如果您的输入包含空格或全局字符(例如*,{{1} },[等。)。