将 pv 与 md5sum 一起使用

将 pv 与 md5sum 一起使用

我使用md5sumwithpv检查同一目录中的 4 GiB 文件:

md5sum dir/* | pv -s 4g | sort

该命令在大约 28 秒内成功完成,但pv输出全部错误。这是整个过程中显示的输出类型:

219 B 0:00:07 [ 125 B/s ] [>                                ]  0% ETA 1668:01:09:02

没有-s 4gand| sort也是这样。我也用不同的文件尝试过。

我尝试过使用pvwithcat并且输出很好,所以问题似乎是由md5sum.

答案1

pv实用程序是“奇特的cat”,这意味着您可以pv在大多数需要使用的情况下使用cat

使用catwith md5sum,您可以计算单个文件的 MD5 校验和

cat file | md5sum

或者,与pv,

pv file | md5sum

但不幸的是,这不允许md5sum将文件名正确插入到其输出中。

现在,幸运的pv是,有一个真的fancy cat,并且在某些系统(Linux)上,它能够手表数据正在通过另一个进程传递。这是通过将其-d选项与其他进程的进程 ID 一起使用来完成的。

这意味着你可以做类似的事情

md5sum dir/* | sort >sums &
sleep 1
pv -d "$(pgrep -n md5sum)"

这将允许pv观看该md5sum过程。是sleep为了让md5sum在后台运行的 正确启动。 将返回您拥有的pgrep -n md5sum最近启动的进程的 PID 。一旦它正在监视的进程终止,它将立即退出。md5sumpv

我已经测试了这种特殊的运行方式pv几次,它似乎通常运行良好,但有时它似乎停止输出任何内容,因为md5sum切换到下一个文件。有时,它似乎会在 shell 中产生虚假的后台任务。

运行它可能是最安全的

md5sum dir/* >sums &
sleep 1
pv -W -d "$!"
sort -o sums sums

-W选项将导致pv等待,直到有实际数据正在传输,尽管这似乎并不总是可靠地工作。

答案2

您通过管道提供的数据不是md5sum正在处理的文件的数据,而是md5sum输出,对于每个文件,输出由一行组成:MD5 哈希、两个空格和文件名。由于我们事先知道这一点,因此可以进行pv相应通知,从而使其能够显示准确的进度指示器。有两种方法可以做到这一点。

第一种首选方法(由霜舒茨建议)利用每个处理的文件生成一行的事实,以及具有对行而不是字节进行计数的行模式的md5sum事实。pv在此模式下,pv仅当遇到吞吐量中的换行符(即每个文件由 完成)时才会移动进度条md5sum。在 Bash 中,第一个方法可能如下所示:

set -- *.iso; md5sum "$@" | pv --line-mode -s $# | sort

内置set函数用于设置要处理的文件的位置参数(*.isoshell 模式由 shell 扩展)。md5sum然后被告知处理这些文件($@扩展到位置参数),并且pv在行模式下,每次处理文件/输出一行时都会移动进度指示器md5sum。值得注意的是,由于特殊的 shell 参数扩展为位置参数的数量,因此pv会得知它可以预期的总行数 ( )。-s $#$#

第二种方法不是基于行而是基于字节。这样md5sum就不必要地复杂了,但是其他一些程序可能不会产生行,而是产生连续的数据,那么这种方法可能更实用。我用md5sum虽然来说明它。这个想法是计算md5sum(或其他程序)将产生的数据量,并用它来通知pv。在 Bash 中,这可能如下所示:

os=$(( $( ls -1 | wc -c ) + $( ls -1 | wc -l ) * 34 ))
md5sum * | pv -s $os | sort

第一行计算输出大小 ( os) 估计:第一项是编码文件名(包括换行符)所需的字节数,第二项是用于编码 MD5 哈希值的字节数(每个 32 个字节),加2个空格。在第二行中,我们告诉pv预期的数据量以字节为os单位,以便它可以显示高达 100% 的准确进度指示器(该指示器根据完成的 md5summed 文件更新)。

显然,这两种方法仅在要处理多个文件的情况下才实用。另外,应该注意的是,由于 的输出与程序必须花费在处理基础数据上md5sum的时间量无关md5sum,因此进度指示器可能被认为有些误导。例如,在第二种方法中,名称最短的文件将产生最低进度更新,即使它实际上可能是最大的。话又说回来,如果所有文件都有相似的大小和名称,这应该没什么关系。

答案3

这是一个获取每个文件进度的肮脏技巧:

for f in iso/*
do
    pv "$f" | (
        cat > /dev/null &
        md5sum "$f"
        wait
    )
done

它看起来像什么:

4.15GiB 0:00:32 [ 130MiB/s] [================================>] 100%            
0db0b36fc7bad7b50835f68c369e854c  iso/KNOPPIX_V7.6.1DVD-2016-01-16-EN.iso
 792MiB 0:00:06 [ 130MiB/s] [================================>] 100%            
97537db63e61d20a5cb71d29145b2937  iso/archlinux-2016.10.01-dual.iso
 843MiB 0:00:06 [ 129MiB/s] [================================>] 100%            
1b5dc31e038499b8409f7d4d720e3eba  iso/lubuntu-16.04-desktop-i386.iso
 259MiB 0:00:02 [ 130MiB/s] [=========>                        ] 30% ETA 0:00:04
...

现在,这做出了几个假设。首先,读取数据比散列数据慢。其次,该操作系统将缓存 I/O,因此即使数据是完全独立的读取器pv,也不会(物理)读取两次md5sum

这种肮脏的黑客的好处是,您可以轻松地调整它以在所有数据(而不仅仅是一个文件)上制作进度条。并且仍然会做一些奇怪的事情,比如事后对输出进行排序。

pv iso/* | (
    cat > /dev/null &
    md5sum iso/* | sort
    wait
)

它看起来像什么(正在进行):

15.0GiB 0:01:47 [ 131MiB/s] [===========================>      ] 83% ETA 0:00:21

它看起来像什么(完成):

18.0GiB 0:02:11 [ 140MiB/s] [================================>] 100%            
0db0b36fc7bad7b50835f68c369e854c  iso/KNOPPIX_V7.6.1DVD-2016-01-16-EN.iso
155603390e65f2a8341328be3cb63875  iso/systemrescuecd-x86-4.2.0.iso
1b5dc31e038499b8409f7d4d720e3eba  iso/lubuntu-16.04-desktop-i386.iso
1b6ed6ff8d399f53adadfafb20fb0d71  iso/systemrescuecd-x86-4.4.1.iso
25715326d7096c50f7ea126ac20eabfd  iso/openSUSE-13.2-KDE-Live-i686.iso
...

现在,这是为了黑客。检查其他答案以获得正确的解决方案。 ;-)

答案4

正如评论和其他答案中已经指出的:

  1. 您正在通过管道输入pvonlymd5sum的输出:校验和和文件名;因此,pv进度条无法显示md5sum正在读取的数据量。
  2. 4 GB 的大小当然太大了。另外,提供pv要导入的文件的大小(手动,使用-s)也很不方便。

将文件内容通过管道输入pv然后再输入md5sum会给您一个进度条,但文件名会丢失。

此代码并不是一种兼具有意义的进度条和带有校验和的文件名的优雅方式:

#!/bin/sh

for file in "$@"; do
    pv -- "$file" |
    md5sum |
    sed 's/-$//' |
    printf '%s%s\n' "$(cat -)" "$file"
done

该脚本的调用方式如下:

./script dir/*

您当然可以将其声明为函数,以避免必须键入其路径来调用它(或将其添加到您的PATH):

function pvsum () {
    for file in "$@"; do
        pv -- "$file" |
          md5sum |
          sed 's/-$//' |
          printf '%s%s\n' "$(cat -)" "$file"
    done
}

这样,该命令pvsum dir/* | sort将相当于您的md5sum dir/* | pv -s <size> | sort.

其输出:

$ ./testscript testdir/*
4.00GiB 0:00:09 [ 446MiB/s] [==============================>] 100%            
9dab5f8add1f699bca108f99e5fa5342  testdir/file1
1.00GiB 0:00:02 [ 447MiB/s] [==============================>] 100%            
06a738a71e3fd3119922bdac259fe29a  testdir/file2

它能做什么:

  • 它循环遍历给定的文件,并且对于每个文件:
    • 将文件从管道传输pvmd5sum,显示默认进度条。
    • sed用于删除-打印的md5sum(从标准输入读取);这也试图使输出适合被消耗md5sum -c(感谢弗罗斯特舒茨指出这一点)1
    • 在标准输出上打印校验和,后跟文件名。

关于sort

我不确定你的预期结果,所以我忽略了它。由于将pv其进度条写入标准错误,因此将所有内容通过管道传输会将的输出与的输出sort分离。 无论如何,您可以在上面的代码后面附加并检查结果是否适合您。pvmd5sum
| sortdone


1md5sum -c请注意,如果文件名包含换行符,则上面显示的代码的输出将不适合。处理换行符是可能的,但某些版本md5sum在这方面的行为有所不同(例如,参见答案这个问题),制定通用解决方案并不容易(并且超出了本答案的范围)。

假设使用最新版本的md5sum,解决此问题的尝试可能是:

for file in "$@"; do
    pv -- "$file" |
    md5sum |
    sed 's/-$//' |
    printf '%s%s\n' "$(cat -)" "$file" |
    sed -n 'H; 1h; $!d; g; s/\\/\\\\/g; s/\n/\\n/g; t x; p; q; :x s/^/\\/; p;'
done

唯一的添加,即最后的sed,将:

  • 将当前文件的整个输入、校验和和名称放入模式空间,因为它可能包含换行符:H将换行符和当前模式空间附加到保留空间;1h覆盖 previous H,仅针对第一行,执行相同的操作但不附加换行符;$!d如果当前行不是最后一行,则开始新的循环;g将保留空间的内容放入模式空间中。
  • 使用反斜杠 ( \) 转义生成的模式空间中的任何反斜杠。
  • 替换为\n结果模式空间中的任何换行符。
  • 仅当至少一个反斜杠或换行符已被替换(t x: 分支到标签x)时,才会在校验和的开头添加一个反斜杠以表明md5sum -c某些内容必须不转义;否则就退出。在这两种情况下,p在退出之前将模式空间打印 ( ) 到标准输出(该选项-n禁用自动打印)。

相关内容