2012-03-09 120 views
5

更新:虽然没有真正解决带有问候我管的努力原来的问题,我已经解决了我的问题通过大大简化它,并且完全抛弃管道。这是一个概念证明脚本,它可以并行生成一次,同时从磁盘读取一次CRC32,MD5,SHA1,SHA224,SHA256,SHA384和SHA512校验和,并将它们作为JSON对象返回(将使用PHP中的输出和Ruby)。这是没有错误检查粗糙,但它的工作原理:可能的竞争条件

#!/bin/bash 

checksums="`tee <"$1" \ 
     >(cfv -C -q -t sfv -f - - | tail -n 1 | sed -e 's/^.* \([a-fA-F0-9]\{8\}\)$/"crc32":"\1"/') \ 
     >(md5sum - | sed -e 's/^\([a-fA-F0-9]\{32\}\) .*$/"md5":"\1"/') \ 
     >(sha1sum - | sed -e 's/^\([a-fA-F0-9]\{40\}\) .*$/"sha1":"\1"/') \ 
     >(sha224sum - | sed -e 's/^\([a-fA-F0-9]\{56\}\) .*$/"sha224":"\1"/') \ 
     >(sha256sum - | sed -e 's/^\([a-fA-F0-9]\{64\}\) .*$/"sha256":"\1"/') \ 
     >(sha384sum - | sed -e 's/^\([a-fA-F0-9]\{96\}\) .*$/"sha384":"\1"/') \ 
     >(sha512sum - | sed -e 's/^\([a-fA-F0-9]\{128\}\) .*$/"sha512":"\1"/') \ 
     >/dev/null`\ 
" 

json="{" 

for checksum in $checksums; do json="$json$checksum,"; done 

echo "${json:0: -1}}" 

原题:

我有点不敢问这个问题,因为我有这么多的点击了搜索短语,申请后从Using named pipes with bash - Problem with data loss收获的知识,并通过另外20页的阅读,我仍然对此有点停滞不前。

因此,继续尽管如此,我做一个简单的脚本,使我对文件同时创建CRC32,MD5,SHA1和校验,而只有从磁盘读取一次。我为此使用了cfv。最初,我只是一个简单的脚本,写了一个简单的脚本,用三个cfv命令写了三个cfv命令,然后写入/ tmp /下的三个单独的文件,然后尝试将它们提供给标准输出,但最终除非我在尝试读取文件之前让脚本睡了一秒,否则会产生空输出。这种想法很奇怪,我认为我在我的脚本中是一个白痴,所以我试图通过让cfv工作者输出到一个命名管道来做一个不同的方法。到目前为止,这是我的脚本,从forementioned链接具有应用技术后:

!/bin/bash 

# Bail out if argument isn't a file: 
[ ! -f "$1" ] && echo "'$1' is not a file!" && exit 1 

# Choose a name for a pipe to stuff with CFV output: 
pipe="/tmp/pipe.chksms" 

# Don't leave an orphaned pipe on exiting or being terminated: 
trap "rm -f $pipe; exit" EXIT TERM 

# Create the pipe (except if it already exists (e.g. SIGKILL'ed b4)): 
[ -p "$pipe" ] || mkfifo $pipe 

# Start a background process that reads from the pipe and echoes what it 
# receives to stdout (notice the pipe is attached last, at done): 
while true; do 
     while read line; do 
       [ "$line" = "EOP" ] && echo "quitting now" && exit 0 
       echo "$line" 
     done 
done <$pipe 3>$pipe & # This 3> business is to make sure there's always 
         # at least one producer attached to the pipe (the 
         # consumer loop itself) until we're done. 

# This sort of works without "hacks", but tail errors out when the pipe is 
# killed, naturally, and script seems to "hang" until I press enter after, 
# which I believe is actually EOF to tail, so it's no solution anyway: 
#tail -f $pipe & 

tee <"$1" >(cfv -C -t sfv -f - - >$pipe) >(cfv -C -t sha1 -f - - >$pipe) >(cfv -C -t md5 -f - - >$pipe) >/dev/null 

#sleep 1s 
echo "EOP" >$pipe 
exit 

因此,执行的样子,我得到这样的输出:

[email protected]:~/tisso$ ./multisfv file 
: : : quitting now 
- : Broken pipe (CF) 
close failed in file object destructor: 
sys.excepthook is missing 
lost sys.stderr 
- : Broken pipe (CF) 
close failed in file object destructor: 
sys.excepthook is missing 
lost sys.stderr 
- : Broken pipe (CF) 
[email protected]:~/tisso$ close failed in file object destructor: 
sys.excepthook is missing 
lost sys.stderr 

但是,随着注释掉睡眠1秒我一开始预期的输出,

[email protected]:~/tisso$ ./multisfv file 
3bc1b5ff125e03fb35491e7d67014a3e * 
-: 1 files, 1 OK. 0.013 seconds, 79311.7K/s 
5e3bb0e3ec410a8d8e14fef1a6daababfc48c7ce * 
-: 1 files, 1 OK. 0.016 seconds, 62455.0K/s 
; Generated by cfv v1.18.3 on 2012-03-09 at 23:45.23 
; 
2a0feb38 
-: 1 files, 1 OK. 0.051 seconds, 20012.9K/s 
quitting now 

这让我为难,因为我认为发球不会退出,直到之后的每个CFV接收者,它派生的数据已经退出,因此回声“EOP”声明将前直到所有的cfv子流完成,这意味着他们已经将他们的输出写到我命名的管道中了......然后echo语句会执行。

由于行为是没有管相同,只是用输出的临时文件,我想这一定是具有三通将数据推到它的接受者的方式做一些竞争条件?我尝试了一个简单的“等待”命令,但它当然会等待我的bash子进程 - 即while循环 - 完成,所以我只是得到一个挂起的进程。

任何想法?

TIA, 丹尼尔:)一旦写输入的最后一位到最后输出管道,并将其关闭(

+1

我期望有这些校验和的源代码可用。如何将它们组合成1个程序,并将3个值写入相应的校验和文件。我不得不相信,perl可能有这个模块,你可以一起做一个文件传递。 (只是想着这个盒子,YRMV)。祝你好运! – shellter 2012-03-10 01:25:51

+1

这会有帮助吗? 'parallel --group'cfv -C -t sfv -f {} - ; cfv -C -t sha1 -f {} - ; cfv -C -t md5 -f {} - ;' ::: file' – potong 2012-03-10 01:46:36

+0

@shelter - 我想写我自己的例程总是我的后备,但我更愿意使用尽可能多的工具。 – DanielSmedegaardBuus 2012-03-10 08:32:26

回答

2

发球将退出即通过庆典创建的命名管道,而不是你的FIFO,又名“命名管道“)。它不需要等待读取管道的过程完成;事实上,它不知道它甚至写入管道。由于管道有缓冲区,很可能在TED完成写入之前,另一端的进程完成读取。所以脚本会将'EOP'写入fifo,导致读取循环终止。这将关闭fifo的唯一读者,并且所有cfv进程在下次尝试写入stdout时都将获得SIGPIPE。

这里要问的一个显而易见的问题是为什么你不只是运行三个(或N个)独立的进程来读取文件和计算不同的摘要。如果“文件”实际上是在飞行中生成的或从某个远程站点下载的,或者是某个其他缓慢的过程,那么按照您尝试执行操作的方式执行操作可能是有意义的,但如果该文件存在于本地磁盘,很可能只有一个磁盘访问实际发生;滞后的汇总器将从缓冲区缓存中读取文件。如果这就是你所需要的,那么GNU并行应该可以正常工作,或者你可以用bash(&)启动进程,然后等待它们。 YMMV,但我认为这些解决方案中的任何一个都比建立所有这些管道并用tee模拟用户区中的缓冲区缓存资源要少。

顺便说一句,如果你想序列化来自多个进程的输出,你可以使用flock工具。仅仅使用fifo是不够的;不能保证写入fifo的流程会以原子方式编写整行代码,如果您知道他们这样做了,则不需要fifo。