什么时候将 IFS 设置为 Bash 中的换行符?
When do I set IFS to a newline in Bash?
我认为将 IFS 设置为 $'\n'
会帮助我将整个文件读入数组,如:
IFS=$'\n' read -r -a array < file
但是,上面的命令只是将文件的第一行读入数组的第一个元素,没有别的。
即使这样也只将第一行读入数组:
string=$'one\ntwo\nthree'
IFS=$'\n' read -r -a array <<< "$string"
我在该站点上看到其他帖子,这些帖子讨论了使用 mapfile -t
或 read
循环将文件读入数组。
现在我的问题是:我什么时候使用 IFS=$'\n'
?
你的第二次尝试几乎成功了,但你必须告诉 read
它不应该只读取到换行符(默认行为),而是例如直到空字符串:
$ IFS=$'\n' read -a arr -d '' <<< $'a b c\nd e f\ng h i'
$ declare -p arr
declare -a arr='([0]="a b c" [1]="d e f" [2]="g h i")'
但是正如您所指出的,mapfile
/readarray
是您的最佳选择(需要 Bash 4.0 或更高版本):
$ mapfile -t arr <<< $'a b c\nd e f\ng h i'
$ declare -p arr
declare -a arr='([0]="a b c" [1]="d e f" [2]="g h i")'
-t
选项删除每个元素的换行符。
至于何时使用 IFS=$'\n'
:
- 如刚才所示,如果您想将文件读入数组,每个元素一行,如果您的 Bash 早于 4.0,并且您不想使用循环
- Some people promote 使用
IFS
而不使用 space 以避免分词带来的意外副作用;不过,在我看来,正确的方法是理解单词拆分,并确保根据需要通过适当的引用来避免它。
- 我看到
IFS=$'\n'
在制表符完成脚本中使用,例如 the one for cd
in bash-completion:这个脚本摆弄路径并用换行符替换冒号,然后使用 IFS
.
您对什么是 IFS 有点困惑。 IFS 是 内部字段分隔符 被 bash 用来执行 word-splitting扩展后 split lines 为 words。默认值为 [ \t\n]
(space、制表符、换行符)。
通过重新分配 IFS=$'\n'
,您将删除 ' \t'
并告诉 bash 仅在 newline
个字符上拆分单词(您的想法是正确的)。这具有允许将 some line with spaces
读入单个数组元素而无需引用的效果。
您的实施失败的地方在于您的 read -r -a array < file
。 -a
导致行 中的单词 被分配给顺序数组索引。但是,您已告诉 bash 仅在 newline
(整行)处中断。由于你只调用了一次read,所以只填充了一个数组索引。
您可以:
while IFS=$'\n' read -r line; do
array+=( $line )
done < "$filename"
(如果你只是引用 "$line"
,你可以不改变 IFS
)
或者使用IFS=$'\n'
,你可以
IFS=$'\n'
array=( $(<filename) )
或者最后,您可以使用 IFS
和 readarray
:
readarray array <filename
尝试一下,如果您有任何问题,请告诉我。
我认为将 IFS 设置为 $'\n'
会帮助我将整个文件读入数组,如:
IFS=$'\n' read -r -a array < file
但是,上面的命令只是将文件的第一行读入数组的第一个元素,没有别的。
即使这样也只将第一行读入数组:
string=$'one\ntwo\nthree'
IFS=$'\n' read -r -a array <<< "$string"
我在该站点上看到其他帖子,这些帖子讨论了使用 mapfile -t
或 read
循环将文件读入数组。
现在我的问题是:我什么时候使用 IFS=$'\n'
?
你的第二次尝试几乎成功了,但你必须告诉 read
它不应该只读取到换行符(默认行为),而是例如直到空字符串:
$ IFS=$'\n' read -a arr -d '' <<< $'a b c\nd e f\ng h i'
$ declare -p arr
declare -a arr='([0]="a b c" [1]="d e f" [2]="g h i")'
但是正如您所指出的,mapfile
/readarray
是您的最佳选择(需要 Bash 4.0 或更高版本):
$ mapfile -t arr <<< $'a b c\nd e f\ng h i'
$ declare -p arr
declare -a arr='([0]="a b c" [1]="d e f" [2]="g h i")'
-t
选项删除每个元素的换行符。
至于何时使用 IFS=$'\n'
:
- 如刚才所示,如果您想将文件读入数组,每个元素一行,如果您的 Bash 早于 4.0,并且您不想使用循环
- Some people promote 使用
IFS
而不使用 space 以避免分词带来的意外副作用;不过,在我看来,正确的方法是理解单词拆分,并确保根据需要通过适当的引用来避免它。 - 我看到
IFS=$'\n'
在制表符完成脚本中使用,例如 the one forcd
in bash-completion:这个脚本摆弄路径并用换行符替换冒号,然后使用IFS
.
您对什么是 IFS 有点困惑。 IFS 是 内部字段分隔符 被 bash 用来执行 word-splitting扩展后 split lines 为 words。默认值为 [ \t\n]
(space、制表符、换行符)。
通过重新分配 IFS=$'\n'
,您将删除 ' \t'
并告诉 bash 仅在 newline
个字符上拆分单词(您的想法是正确的)。这具有允许将 some line with spaces
读入单个数组元素而无需引用的效果。
您的实施失败的地方在于您的 read -r -a array < file
。 -a
导致行 中的单词 被分配给顺序数组索引。但是,您已告诉 bash 仅在 newline
(整行)处中断。由于你只调用了一次read,所以只填充了一个数组索引。
您可以:
while IFS=$'\n' read -r line; do
array+=( $line )
done < "$filename"
(如果你只是引用 "$line"
,你可以不改变 IFS
)
或者使用IFS=$'\n'
,你可以
IFS=$'\n'
array=( $(<filename) )
或者最后,您可以使用 IFS
和 readarray
:
readarray array <filename
尝试一下,如果您有任何问题,请告诉我。