Dan*_*uus 5 bash pipe race-condition conditional-statements
更新:虽然没有真正解决我的管道工作中出现的原始问题,但我已经通过大大简化它解决了我的问题,并且完全抛弃了管道.这是一个概念验证脚本,可以在从磁盘,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}}"
Run Code Online (Sandbox Code Playgroud)
原始问题:
我有点害怕提出这个问题,因为我在我的搜索短语中得到了如此多的点击,在应用了使用命名管道与bash收集的知识后- 数据丢失的问题,以及另外20页的阅读,我仍然在这有点停滞不前.
所以,为了继续,我正在做一个简单的脚本,使我能够同时在文件上创建CRC32,MD5和SHA1校验和,同时只从磁盘读取一次.我正在为此目的使用cfv.
最初,我只是简单地编写了一个简单的脚本,该脚本用三个cfv命令编写文件来编写te/tmp /下的三个单独文件,然后尝试将它们写入stdout,但最后输出空输出,除非在尝试读取文件之前,我让我的脚本睡了一秒钟.考虑到这很奇怪,我认为我的脚本中是个白痴,所以我尝试通过将cfv worker输出到命名管道来做一个不同的方法.到目前为止,这是我的脚本,在应用了上述链接的技术之后:
!/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
Run Code Online (Sandbox Code Playgroud)
所以,按原样执行,我得到这个输出:
daniel@lnxsrv:~/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)
daniel@lnxsrv:~/tisso$ close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr
Run Code Online (Sandbox Code Playgroud)
但是,随着睡眠1s的注释,我获得了预期的输出,
daniel@lnxsrv:~/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
Run Code Online (Sandbox Code Playgroud)
这让我很困惑,因为我假设tee不会退出,直到每个cfv接收者之后它才会退出数据,因此回声"EOP"语句将执行,直到所有cfv子流完成,这意味着他们会已将其输出写入我的命名管道......然后将执行echo语句.
由于行为是相同的没有管道,只是使用输出临时文件,我认为这必须是一些竞争条件与tee将数据推送到其收件人的方式?我尝试了一个简单的"等待"命令,但它当然会等待我的bash子进程 - while循环 - 完成,所以我只是暂停进程.
有任何想法吗?
TIA,丹尼尔:)
一旦 tee 将最后一位输入写入最后一个输出管道并关闭它(即 bash 创建的未命名管道,而不是 fifo,又名“命名管道”),tee 就会退出。无需等待读取管道的进程完成;事实上,它甚至不知道它正在写入管道。由于管道有缓冲区,因此 tee 很可能在另一端的进程完成读取之前完成写入。因此脚本会将“EOP”写入 fifo,导致读取循环终止。这将关闭 fifo 的唯一读取器,并且所有 cfv 进程在下次尝试写入 stdout 时都会收到 SIGPIPE。
这里要问的明显问题是为什么您不只运行三个(或 N 个)独立进程来读取文件并计算不同的摘要。如果“文件”实际上是动态生成的或从某个远程站点下载的,或者是其他一些缓慢的过程,则按照您尝试的方式执行操作可能是有意义的,但如果该文件存在于本地磁盘,很可能实际上只会发生一次磁盘访问;滞后的摘要器将从缓冲区高速缓存中读取文件。如果这就是您所需要的,GNU 并行应该可以正常工作,或者您可以在 bash 中启动进程(使用 &),然后等待它们。YMMV,但我认为这些解决方案中的任何一个都比设置所有这些管道并使用 tee 模拟用户空间中的缓冲区缓存占用的资源更少。
顺便说一下,如果你想序列化多个进程的输出,你可以使用flock实用程序。仅仅使用 fifo 是不够的;无法保证写入 fifo 的进程会自动写入整行,如果您知道它们这样做,则不需要 fifo。