什么时候将 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 -tread 循环将文件读入数组。

现在我的问题是:我什么时候使用 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 lineswords。默认值为 [ \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) )

或者最后,您可以使用 IFSreadarray:

readarray array <filename

尝试一下,如果您有任何问题,请告诉我。