在 bash 手册中,关于read
内置命令
-d delim
的第一个字符delim
用于终止输入行,而不是换行符。
是否可以指定一个字符 as delim
of read
,以便它永远不会匹配(除非它可以匹配 EOF,这是一个字符?)并且read
总是一次读取整个文件?
谢谢。
答案1
由于bash
无论如何都无法在其变量中存储 NUL 字节,因此您始终可以这样做:
IFS= read -rd '' var < file
如果文件没有 NUL 字节,它将存储文件的内容直到第一个 NUL 字节或文件末尾(根据定义(至少根据 POSIX 定义),文本文件不包含 NUL 字节)。
另一种选择是将文件的内容存储为其行数组(包括行分隔符,如果有):
readarray array < file
然后您可以通过以下方式加入他们:
IFS=; var="${array[*]}"
如果输入包含 NUL 字节,则每行中第一次出现之后的所有内容都将丢失。
在 POSIX sh 语法中,您可以执行以下操作:
var=$(cat < file; echo .); var=${var%.}
我们添加了一个.
,然后将其删除,以解决命令替换会删除所有尾随换行符的事实。
如果文件包含 NUL 字节,则不同实现的行为会有所不同。zsh
是唯一会保留它们的 shell(它也是唯一可以在其变量中存储 NUL 字节的 shell)。bash
其他一些 shell 只是删除它们,而其他一些 shell 会被它们阻塞并丢弃第一个 NUL 出现之后的所有内容。
您还可以以某种编码形式存储文件的内容,例如:
var=$(uuencode -m - < file)
并通过以下方式取回:
printf '%s\n' "$var" | uudecode
或者使用 NUL 进行编码,\0000
以便能够在printf %b
in 的参数中使用它bash
(假设您没有使用字符集为 BIG5、GB18030、GBK、BIG5-HKCSC 的语言环境):
var=; while true; do
if IFS= read -rd '' rec; then
var+=${rec//\\/\\\\}\\0000
else
var+=${rec//\\/\\\\}
break
fi
done < file
进而:
printf %b "$var"
把它拿回来。
答案2
答案通常是“否”,因为作为一般规则,文件中不存在最终标记文件结尾的实际字符。
强烈建议您尝试不同的方法,例如此处建议的方法之一:https://stackoverflow.com/questions/10984432/how-to-read-the-file-content-into-a-variable-in-one-go。用于:
IFS="" contents=$(<file)
特别优雅;它导致 Bash 将 的内容读file
入变量contents
,但 Bash 变量无法保存 NULL 字节(由于其内部使用 C 风格、以 NULL 字节结尾的字符串)。IFS=""
将内部字段分隔符设置为空,以禁用分词(从而避免删除换行符)。
注意:由于(由于缺乏声誉点)我无法对建议使用read
该-N
选项的答案发表评论,我在这里注意到该答案 - 根据定义 - 不能保证按原样工作,因为文件大小未知提前。
答案3
在 中bash
,使用-N
(字符数)选项。
read -rN 40000000 foo
-r
如果您确实希望反斜杠转义文件中的字符,请忽略该选项。
从help read
:
-N nchars return only after reading exactly NCHARS characters, unless EOF is encountered or read times out, ignoring any delimiter
EOF
不是一个字符,而是一个状态:a read
(系统调用,而不是 shell 内置函数)返回了零长度。但是getchar()
其他函数将方便地返回EOF
一个值为 (-1) 的整数,该整数不能与任何字符集中的任何有效字符冲突。因此,一些旧操作系统确实使用了 EOF 标记(通常^Z
),因为它们只跟踪文件系统元数据中的整个块,这一事实加剧了混乱。
奇怪的是,read -N0
似乎做了一个“缓慢的slurp”(它将以相同的方式读取整个文件,但对每个字符进行系统调用)。我不确定这是一个预期的功能;-)
strace -fe trace=read ./bash -c 'echo yes | read -N0'
...
[pid 8032] read(0, "y", 1) = 1
[pid 8032] read(0, "e", 1) = 1
[pid 8032] read(0, "s", 1) = 1
[pid 8032] read(0, "\n", 1) = 1
[pid 8032] read(0, "", 1) = 0
bash
请注意,内置使用的缓冲区read
只有 128 字节,因此您不应该用它读取大文件。另外,如果您的文件大量使用 utf-8,则应该使用LC_CTYPE=C read ...
;否则bash
将交替读取 128 字节和逐字节读取,从而使其速度更慢。