等于换行符的 IFS 在 bash 中不起作用

IFS equal to newline character is not working in bash

我有一个多行字符串,我想根据 \n 进行拆分,为此我设置了 IFS=$'\n' 但它不起作用,我也尝试过 IFS=IFS=" " 但运气不好。 下面是示例代码

IFS=$'\n' read -ra arr <<< "line1\nline2\nline3"
printf "%s\n" "${arr[@]}"
# it only generates array of length 1

我正在使用 Ubuntu,bash 版本为

GNU bash, version 4.4.19(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
<http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

谁能指出显而易见的地方。

来自 bash 手册,bash 内置部分:

read

read [-ers] [-a aname] [-d delim] [-i text] [-n nchars]
    [-N nchars] [-p prompt] [-t timeout] [-u fd] [name …]

One line is read from the standard input, or from the file descriptor fd supplied as an argument to the -u option, 

行分隔符可以用-d

指定

-d delim

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

注意后面跟一个空参数-d '',行分隔符就是nul字符,如果input中没有nul字符,可以用来读取整个input

但是即使使用 -r 模式,读取速度也很慢。

对于更大的输入,更快的解决方案可以是使用分词:

input=$'line1\nline2\nline3'
IFS=$'\n';set -f;arr=($input);set +f;IFS=$' \t\n'

注意 set -f 以避免全局匹配阶段。

您尝试的主要问题是 分词 未使用 herestring 进行预制。来自男人 bash:

Here Strings
   A variant of here documents, the format is:

          <<<word

   ... Pathname expansion and word splitting are not performed.
   The result is supplied as a single string to the command on its 
   standard input.

Bash 确实提供了 heredoc(例如 "Here Document" in man bash),word-splitting 将使用默认值 IFS 执行。然而,即便如此,您仍然会读取文字 '\n' 作为数组内容的一部分。不用担心,bash 提供了一种特定的方式,可以通过 readarray-t 选项来避免这种情况(a/k/a mapfile)。

一个尽可能接近您最初尝试的简短示例是:

readarray -t arr << EOF
line1
line2
line3
EOF
declare -p arr

这会导致您的行按需要保存,例如输出将是:

declare -a arr='([0]="line1" [1]="line2" [2]="line3")'

另一种选择是使用 进程替换 并让 printf 提供拆分,例如

readarray -t arr < <(printf "line1\nline2\nline3\n")

不包含换行符的填充数组的关键是readarray -t,允许分词发生的关键是避免herestring.