lur*_*urv 2 command-line performance pipe macos
我知道使用tail -c +N,但它非常慢,并且固定了一个 CPU 核心:
leijurvs-MacBook-Pro:Downloads leijurv$ time cat /dev/zero | head -c 100000000 | shasum -a 256
a993f8c574e0fea8c1cdcbcd9408d9e2e107ee6e4d120edcfa11decd53fa0cae -
cat /dev/zero 0.00s user 0.02s system 4% cpu 0.471 total
head -c 100000000 0.01s user 0.03s system 9% cpu 0.470 total
shasum -a 256 0.45s user 0.02s system 99% cpu 0.469 total
leijurvs-MacBook-Pro:Downloads leijurv$ time cat /dev/zero | head -c 100000000 | tail -c +2 | shasum -a 256
f4be792b71a024a60d77b3ac4c1c2b88ac51480fa25f88d10865827f8c086506 -
cat /dev/zero 0.01s user 0.03s system 0% cpu 7.241 total
head -c 100000000 0.02s user 0.03s system 0% cpu 7.240 total
tail -c +2 7.20s user 0.03s system 99% cpu 7.247 total
shasum -a 256 0.51s user 0.04s system 7% cpu 7.247 total
leijurvs-MacBook-Pro:Downloads leijurv$
Run Code Online (Sandbox Code Playgroud)
head很好。我使用 shasum 获取前 100 MB 的零head:需要 0.47 秒。
我过去常常tail -c +2跳过第一个字节,突然需要 7.2 秒。
tail在此期间固定一个CPU核心。
如何高效地跳过流的前 N 个字节?
如果您只想在输入之前跳过文件的第一个字节shasum,您可以这样做(此处使用zsh语法,因为time输出格式表明这就是您正在使用的 shell):
time cat /dev/zero | head -c 100000000 |
(LC_ALL=C read -u0 -k1 && shasum -a 256)
Run Code Online (Sandbox Code Playgroud)
read然后,这意味着没有额外的过程,第一个字节只是在开始之前从管道中读取的shasum。
这LC_ALL=C read -u0 -k1是reading1字符(k这里是key,最初read -k是从终端读取按键),这里的字符是单字节,这要归功于LC_ALL=C, 来自文件描述符unit 编号0(stdin;这里为了明确我们正在从流中读取不是来自终端)。
对于bashshell,等效的read命令是LC_ALL=C IFS= read -rd '' -n1.
zsh 的等价物read -k通常是read -N,但这对于包含 NUL 字节的输入不起作用,它bash只是read条带(另外-N,从 ksh93 复制的是一个相对较新的添加,在 macos 上找到的古老版本的 bash 中不可用)。通过将delimiter 设置为 NUL 字节(此处表示为空字符串),我们可以避免这种情况。是从第一个 NUL 分隔记录中-n1读取一个字符(再次通过 生成字节)。LC_ALL=C然而,这意味着它不能像 一样适应不同数量的字节-rd '' -n2,如果第一个字节为 0,我们只会跳过一个字节。
对于其他 shell,您可以将该read命令替换为dd bs=1 count=1 > /dev/null 2>&1(change count,不要bs跳过超过一个字节)。head -c 1 > /dev/null也可以与支持该非标准选项的一些实现一起使用,但不是全部(特别是,不是 FreeBSD 的,所以可能也不是 macos 的),因为有些实现会以固定大小的块读取输入,即使请求输出更少字节。但请注意,与上述相反,当它们无法读取该一个字节时,它们不会报告失败退出状态,因此在任何情况下都会运行。head-creadshasum
当要校验和的东西是常规文件而不是管道时,您可以通过在文件内查找而不是读取并丢弃要跳过的部分(仍然zsh语法):
zmodload zsh/system
{ sysseek 1234567 && shasum -a 256; } < some-big-file
Run Code Online (Sandbox Code Playgroud)
跳过前 1234567 个字节。
或者使用 ksh93:
shasum -a 256 < some-big-file <#((1234567))
Run Code Online (Sandbox Code Playgroud)
对于其他 shell 和一些实现dd(我不知道 macos 的实现),您可以执行以下操作:
{ dd bs=1 skip=1234567 count=0 2> /dev/null; shasum -a 256; } < some-big-file
Run Code Online (Sandbox Code Playgroud)
然而使用count=0并不便携。当计数为 0 时,并非所有dd实现都会执行此处的操作。有些甚至会将其理解为。lseek()count=infinity