Bash:将一个简短的文本文件读取到变量会丢失最后一个空行

Paa*_*nen 6 bash

我有一个脚本处理来自慢速大容量存储器的许多小文件.

出于性能原因,我将文件读取到变量,然后使用此变量进行所有处理.这允许我只读取一次文件.

这很有效,除非最后一行为空,那么变量将比文件短一行,请参见下面的简化示例.

有没有办法将文件末尾的空行读取到变量?

$ rm -f /tmp/a ; for i in $(seq 3) ; do echo $i >> /tmp/a ; done
$ cat /tmp/a
1
2
3
$ wc -l /tmp/a
3 /tmp/a
$ a="$(cat /tmp/a)"
$ echo "$a"
1
2
3
$ echo "$a" | wc -l
3

$ rm -f /tmp/b ; for i in $(seq 3) ; do echo $i >> /tmp/b ; done
$ echo >> /tmp/b # ADD EXTRA EMPTY LINE TO THE END
$ cat /tmp/b
1
2
3

$ wc -l /tmp/b
4 /tmp/b
$ b="$(cat /tmp/b)"
$ echo "$b"
1
2
3
$ echo "$b" | wc -l
3
Run Code Online (Sandbox Code Playgroud)

Joh*_*ica 6

$(...)剥离所有尾随换行符.从bash手册页:

命令替换允许输出命令来替换命令名称.有两种形式:

$(command)
Run Code Online (Sandbox Code Playgroud)

要么

`command`
Run Code Online (Sandbox Code Playgroud)

Bash通过执行命令并用命令的标准输出替换命令替换来执行扩展,删除任何尾随换行符.嵌入的换行不会被删除,但在分词时可能会被删除.命令替换$(cat file)可以替换为等效但更快$(< file).

用于mapfile在保留换行符的同时读取整个文件.它将每一行读入一个数组.

$ mapfile b < /tmp/b
$ printf '%s' "${b[@]}"
1
2
3

$ printf '%s' "${b[@]}" | wc -l
4
Run Code Online (Sandbox Code Playgroud)

避免echo,这会增加额外的换行符.printf '%s'不这样做,所以你得到了数组中的确切内容.

如果不需要数组,可以使用printf -v它将其展平为单个字符串,同时保留换行符.

$ mapfile b < /tmp/b
$ printf -v b '%s' "${b[@]}"
$ printf '%s' "$b"
1
2
3

$ printf '%s' "$b" | wc -l
4
Run Code Online (Sandbox Code Playgroud)

出于性能原因,我将文件读取到变量,然后使用此变量进行所有处理.这允许我只读取一次文件.

这可能是过早的优化.从磁盘读取文件后,操作系统会将其保留在缓存中.重新读取仍在缓存中的文件非常快.