是否有一个字符作为“read”的“delim”,以便“read”一次读取整个文件?

是否有一个字符作为“read”的“delim”,以便“read”一次读取整个文件?

在 bash 手册中,关于read内置命令

-d delim的第一个字符delim用于终止输入行,而不是换行符。

是否可以指定一个字符 as delimof 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 %bin 的参数中使用它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 字节和逐字节读取,从而使其速度更慢。

相关内容