我可以使用 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 字符(如 *
、[
, ?
, 等等).
我有一个变量:
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
delimThe 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 字符(如*
、[
,?
, 等等).