哪些 shell(如果有)可以避免将此处文档一次性读入内存?

哪些 shell(如果有)可以避免将此处文档一次性读入内存?

sh是否有任何好的技术可以构建巨大的自动生成的 shell 脚本,以便即使文件无法容纳在内存中也可以执行它们?另外,是否有任何形式的保证,shell 本身不会将heredoc 一次性全部读入内存?在实践中,哪些 shell 会避免将此处文档存储在内存中,是否可以依靠某个任意机器上的一致性sh来执行此操作?

我正在阅读有关 GNU 的内容shar,想知道它是否可以用于太大而无法放入内存的文件。它使用heredocs来存储内容。

sed -e 's/^X//' << 'SHAR_EOF' | uudecode &&
Msome binary content
Xsome text content
SHAR_EOF

然而,有多个这样的heredoc,并且脚本末尾有一些固定的非heredoc内容shar,可能需要在执行脚本的任何部分之前对其进行解析。如果 shell 没有解析整个脚本,那么在执行第一个命令之前就不可能拒绝格式错误的脚本。

以下是来自 shar 存档的尾随 shell 注释的片段:

...
else
test `LC_ALL=C wc -c < 'a.binary'` -ne 126472 && \
  ${echo} "restoration warning:  size of 'a.binary' is not 126472"
  fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
     exit 1
fi
exit 0

答案1

无法保证 shell 会或不会将整个此处文档加载到内存中。巨大的脚本并不常见,因此 shell 实现者不太可能优化这种情况。甚至有点不可取的是,在执行开始之前整个脚本没有加载到内存中来执行它,但是所有常见的 shell 都会在完全加载之前执行,这意味着如果脚本文件在执行过程中被修改,它们将执行垃圾。

实验上,在 Debian jessie 上,dash、bash、mksh 和 zsh 将 130kB 的此处文档加载到内存中,而 ksh93 则复制 64kB 块而不分配更多内存。因此,使用不适合内存的此处文档的唯一方法是确保您的脚本是使用 ksh93(或者可能是 ksh88)执行的 - 在执行此操作之前,请确保其他版本也是如此,我还没有尚未验证所有 ksh 版本的行为是否相同。

您可以做的更便携的是将所有数据放在脚本的末尾,并用于tail -c $offset提取有效负载。它在实践中是有效的,因为通常的 shell 都不会在执行脚本之前将其完全加载到内存中。此方法的优点是有效负载可以是二进制的 - 这里文档不能包含 end-of-heredoc 字符串或空字节。

如果您的脚本是不变的,您可以对有效负载的偏移量进行硬编码。如果不是,您可以在脚本末尾放置一个可区分的标记,并使用 awk 来确定其位置:

offset=$(awk '{offset += length($0) + 1}
              $0 == "# payload starts here (do not remove this magic comment)" {
                  print offset; exit
              }')
tail -c "$offset" <"$0" — …
# payload starts here (do not remove this magic comment)

如果您有多个有效负载,则需要更复杂的命令来提取它们。请注意,head -c并非所有 UNIX 变体中都存在这一点。您可以使用它dd ibs=1 count=$bytes来实现相同的效果,但它可能会非常慢,因为许多dd实现一次会复制一个字节。我建议附加一个 tar 存档并从中按名称提取文件。

相关内容