获取除尾随零字节之外的文件大小

获取除尾随零字节之外的文件大小

我想获取正在下载的文件的大小。由于文件是预先分配的,因此 usingdu -sd仅返回其最终的完整大小。我想知道已下载了多少内容,因此我不希望对那些尾随零字节进行计数。我如何获得这个尺寸?

这应该是可能的,因为aria2c可以轻松恢复停止的下载,并且它似乎没有将下载的长度存储在它的控制(会话)文件。我写了一个脚本total_length.aria2控制文件中读取。但这是总长度,而不是下载长度。您可以轻松地使用该脚本和技术规格获取 aria2 商店的任何其他财产。

评论更新:

正如 ilkkachu 所暗示的那样,.aria2 文件中的 BITFIELD 似乎实际上是一个映射:每个位对应一个文件块,1 表示“已下载”(0 表示“未下载”)。位字段长度为您提供块的数量(块大小可能只是文件的大小除以块编号)。我很确定下载进度是由 1 秒与 BITFIELD 中块数的比率给出的。不幸的是,AFAICT,.aria2 文件似乎在一段延迟后或下载中断后立即更新。

答案1

仅考虑了解下载进度的问题aria2,有几种选择。

正如评论中所讨论的,该信息位于控制文件 ( filename.aria2) 的位图中。它记录在https://aria2.github.io/manual/en/html/technical-notes.html。对于 HTTP 下载来说,拥有位图没有多大意义,因为 HTTP 下载从一开始就是线性的,但我认为对于 BitTorrent 下载等来说更有意义。

以下是特定下载的控制文件的十六进制转储,其中重要字段标记为 ( od -tx1 file.aria2):

0000000 00 01 00 00 00 00 00 00 00 00 00 10 00 00 00 00
                                      ^^^^^^^^^^^ ^^^^^^  
0000020 00 00 82 9d c0 00 00 00 00 00 00 00 00 00 00 00 
        ^^^^^^^^^^^^^^^^^                         ^^^^^^
0000040 01 06 ff ff ff ff ff ff ff ff ff ff ff ff ff ff
        ^^^^^ ^^^... 
0000060 ff ff ff ff ff ff ff ff ff fe 00 00 00 00 00 00


offset 10: 00 10 00 00 => piece length = 0x100000 = 1 MiB
offset 14: 00 00 00 00 
           82 9d c0 00 => file length = 0x829dc000 = 2191376384 (~ 2 GiB)
offset 30: 00 00 01 06 => size of bitmap = 0x0106 = 262 bytes, could fit 2096 pieces
offset 34: ff ff ...   => bitmap

计算位图中的设置位,该特定下载在下载了至少 191 个 1 MiB(200278016 字节)的文件后被中断,这与我得到的结果文件大小(201098200 字节)非常匹配。 (实际文件比 MiB 还要大,控制文件中的正在运行的片段的记录可能会标记这一点,但我不在乎。我没有启用预分配,只是这样我可以与文件系统上的大小进行交叉检查。)

默认情况下aria2c每 60 秒保存一次控制文件,但我们可以--auto-save-interval=<secs>更改它:

--auto-save-interval=<SEC>
       Save a control file(*.aria2) every SEC seconds.  If 0 is
       given, a control file is not saved during download. aria2
       saves  a  control  file  when  it stops regardless of the
       value.  The possible values are between 0 to 600. 
       Default: 60

或者,我想您可以使用aria2c --log=<logfile>并从日志中获取下载进度。尽管进度似乎仅在DEBUG级别消息中显示写入缓存条目,并且启用这些条目后,日志相当详细。

另外,您可以使用--summary-interval=1将一些进度输出打印到stdout,可能重定向到某个日志文件(并且可能--show-console-readout=false隐藏实时读数)。虽然它似乎只给出了四舍五入的数字:

 *** Download Progress Summary as of Wed May 13 12:57:11 2020 ***
=================================================================
[#b56779 1.7GiB/2.0GiB(86%) CN:1 DL:105MiB ETA:2s]
FILE: /work/blah.iso
-----------------------------------------------------------------

答案2

有一种方法。

你想要匹配的是行尾的零,这个正则表达式:

\0*$

将匹配该值,前提是执行正则表达式的工具不会因 NUL 字节 ( \0) 而阻塞并理解\0转义。带有 PCRE 正则表达式的 GNU grep 确实如此(-a允许二进制文件,-o打印仅匹配的部分-P适用于 PCRE 正则表达式):

grep -aPo '\0*$' file

这将在每行末尾输出所有零字节(加上每个换行符)。

要仅提取最后一行,我们可以使用 sed (GNU sed 已记录,可以处理包含 NUL 的文件(考虑该-z选项))(某些工具不喜欢 NUL 字节):

sed -n '$p' file | grep -aPo '\0*$'

需要做的就是计算它们:

zerobytes=$(( $( sed -n '$p' file | grep -aPo '\0*$' | wc -c ) - 1 ))

当然,此时需要做的就是从整个文件长度中减去该值即可得到下载的文件大小。

未经测试的代码

# alias ggrep and gdu to GNU grep and GNU du or install coreutils from Homebrew
filesize() {
    local filename="$1"
    test -e "$filename" || return 1

    local filesize="$(gdu -sb "$filename" | awk '{ print $1 }')"
    echo "$filesize"
}
filesizereal() {
    local file="$1"
    local zerobytes=$(( $( gsed -n '$p' "$file" | ggrep -aPo '\0*$' | wc -c ) - 1 ))
    echo "$(( ${$(filesize "$file"):-0} - $zerobytes ))"
}

答案3

我编写了一个 Rust 脚本来计算尾随零。它相当快,但会加载整个文件。看到这个问题

要运行此脚本,您需要在系统上安装它rustscriptisto我已经trailingzeroes.rs在我的系统上命名了这个脚本。

#!/usr/bin/env scriptisto

// scriptisto-begin
// script_src: src/main.rs
// build_cmd: cargo build --release
// target_bin: ./target/release/script
// files:
//  - path: Cargo.toml
//    content: |
//     package = { name = "script", version = "0.1.0", edition = "2018"}
//     [dependencies]
// scriptisto-end

// https://users.rust-lang.org/t/count-trailing-zero-bytes-of-a-binary-file/42503/4

use std::env;
use std::fs;

fn main() {
    let filename = env::args().nth(1).unwrap();
    let buffer = fs::read(filename).unwrap();
    let count = buffer.iter().rev().take_while(|b| **b == 0).count();
    println!("{}", count);
}

现在,

# gdu is GNU du
# ggrep is GNU grep

function filesize() {
    # '<file> ; returns size in bytes.'

    local FILENAME="$1"
    test -e "$FILENAME" || { echo "File $FILENAME doesn't exist." >&2 ; return 1 }

    local SIZE="$(gdu -sb $FILENAME | awk '{ print $1 }')"
    ec $SIZE
}
function filesizereal() {
    local file="$1"
    test -e "$file" || { echo "File $file doesn't exist." >&2 ; return 1 }
    local zerobytes
    # zerobytes=$(( $( ggrep -aPo '\0*$' $file | wc -c ) - 1 ))
    zerobytes="${$(trailingzeroes.rs $file)}"
    echo $(( ${$(filesize $file):-0} - $zerobytes )) 
}

相关内容