我想获取命令输出的字节数和 sha1sum。
原则上,人们总是可以做这样的事情:
BYTES="$( somecommand | wc -c )"
DIGEST="$( somecommand | sha1sum | sed 's/ .*//' )"
...但是,对于我感兴趣的用例,somecommand
相当耗时,并且会产生大量输出,所以我宁愿只调用它一次。
我想到的一种方法是这样的
evil() {
{
somecommand | \
tee >( wc -c | sed 's/^/BYTES=/' ) | \
sha1sum | \
sed 's/ .*//; s/^/DIGEST=/'
} 2>&1
}
eval "$( evil )"
...这似乎有效,但让我内心有点死了。
我想知道是否有更好(更稳健、更通用)的方法来将管道不同段的输出捕获到单独的变量中。
编辑:我目前正在解决的问题是bash
,所以我最感兴趣的是这个 shell 的解决方案,但我也做了很多zsh
编程,所以我对这些解决方案也有一些兴趣。
EDIT2:我尝试将 Stéphane Chazelas 的解决方案移植到bash
,但它不太有效:
#!/bin/bash
cmd() {
printf -- '%1000s'
}
bytes_and_checksum() {
local IFS
cmd | tee >(sha1sum > $1) | wc -c | read bytes || return
read checksum rest_ignored < $1 || return
}
set -o pipefail
unset bytes checksum
bytes_and_checksum "$(mktemp)"
printf -- 'bytes=%s\n' $bytes
printf -- 'checksum=%s\n' $checksum
当我运行上面的脚本时,我得到的输出是
bytes=
checksum=96d89030c1473585f16ec7a52050b410e44dd332
的值checksum
是正确的。bytes
我不明白为什么没有设置的值。
EDIT3:好的,感谢@muru 的提示,我解决了问题:
#!/bin/bash
cmd() {
printf -- '%1000s'
}
bytes_and_checksum() {
local IFS
read bytes < <( cmd | tee >(sha1sum > $1) | wc -c ) || return
read checksum rest_ignored < $1 || return
}
set -o pipefail
unset bytes checksum
bytes_and_checksum "$(mktemp)"
printf -- 'bytes=%s\n' $bytes
printf -- 'checksum=%s\n' $checksum
现在:
bytes=1000
checksum=96d89030c1473585f16ec7a52050b410e44dd332
不幸的是...
...当产生的输出比上面的玩具示例中的情况多得多时,我的bytes_and_checksum
函数就会停止(死锁?) 。cmd
回到绘图板...