使用FIFO合并已排序文件

5

我有一些已排序和压缩过的文件在一个目录中。如何将其中一些组合成另一个已排序和压缩过的文件?目前我正在使用显式FIFO(命名管道)。是否有一种在bash中不需要这样做的方法?我是一个bash新手,所以请原谅我的不专业。

#!/bin/bash
# Invocation ./merge [files ... ]
# Turns an arbitrary set of sorted, gzipped files into a single sorted, gzipped file,
# printed to stdout. Redirect this script's output!
for f in $@
do
    mkfifo $f.raw
    gzcat $f > $f.raw &
    # sort -C $f.raw
done
sort -mu *.raw | gzip -c # prints to stdout.
rm -f *.raw

我想把这个转换成类似于...的东西。
sort -mu <(gzcat $1) <(gzcat $2) <(gzcat $3) ... | gzip -9c # prints to stdout.

...但是不知道该怎么做。我需要建立一个循环来将参数转换为字符串吗?有没有什么魔法快捷方式可以做到这一点?也许是 map gzcat $@

注意:每个文件的大小都超过10GB(解压后达到100GB以上)。我有一块2TB的硬盘,所以这并不是问题。此外,此程序必须在O(n)时间内运行,否则就变得不可行。


1
我看到你在我回答的时候编辑了问题 - 是的,你需要一个循环来构建命令字符串,并且在最后使用evalbash -c "$cmd"来执行它。 - Jonathan Leffler
3个回答

3
你可以在Bash中结合 eval 和 "进程替换"。假设基本文件名不包含空格(因为你使用 $@ 而不是 "$@"),那么可以这样写:
cmd="sort -mu"
for file in "$@"
do cmd="$cmd <(gzip -cd $file)"
done
eval $cmd | gzip -c9 > outputfile.gz

在最后一行,你也可以使用 bash -c "$cmd" 替代 eval $cmd。如果文件名中有空格,你需要做一些额外的处理。如果名称不包含单引号,则此方法适用:

cmd="sort -mu"
for file in "$@"
do cmd="$cmd <(gzip -cd '$file')"
done
eval $cmd | gzip -c9 > outputfile.gz

如果文件名中也有单引号,那么你需要更加努力地工作。


1

对我来说,你的问题有点不清楚,但如果我理解你的需求,可以尝试这样做:

gunzip -c file1 file2 .... | sort | gzip -9 > mergedFile.gz

如果您想在一个目录中处理某种类型的所有文件,则可以使用file*.type作为gunzip的输入列表,否则,根据我的示例,您需要明确列出每个文件。 -c选项表示“将输出发送到stdout”,然后由管道读取,发送到sort,它将其输出发送到stdout、管道和gzip中,其中它的stdout被重定向到最终文件。 -9是最高压缩级别,可以获得最小的文件(对于gzip),但需要更长时间。您可以给出-1到-9之间的显式数字,以调整压缩大小/时间以满足您的需求。
希望这可以帮助您。

我真的想使用sort -mu,但如果我们一次性gunzip,它就无法工作。这将把sort从O(nlogn)变成O(n)。 - Clark Gaebel
我通常会使用明确的 gzip -c -9,但我想那应该可以工作。 - Jonathan Leffler
你有大文件,希望通过预先排序较小的文件并在最后合并它们来并行化处理吗?并且你有多个 CPU 可以分配给每个小型排序过程吗?你是想节省时间、CPU 还是其他方面呢?在 S.O. 上有很多人对性能调优感兴趣。你可以添加基准测试、测试和性能调优标签以获得更好的建议。祝你好运。 - shellter
@shellter:好的,谢谢。但我更希望找到一种稳健的方法来构建FIFO(不能被touch [file].raw破坏),而不是调整性能。也许像命令替换这样的东西?http://www.linuxjournal.com/article/2156?page=0,1 - Clark Gaebel
好的,这是一个有趣的问题,但我希望在你最初的问题中提到过。我从未遇到过这种情况,所以我没有一个好的答案。我想,在fifo上放置600权限还不够吗?如何触发文件的“touch”操作?您能否使您的fifo名称更加独特,将进程ID($$)添加到名称中?您关于O(nlogn)与O(n)的担忧怎么样了?晚安。 - shellter
那行不通。sort -mu 只对预先排序的文件执行合并操作。如果您 gzcat 几个文件,然后将其管道传输到 sort,sort 只会看到一个非常大的文件,而 sort -mu 就变成了 nop。 - Clark Gaebel

1
如果文件名中也有单引号,你需要更加努力地工作。
以下是一种在文件名(或文件路径)中转义单引号的方法,这将在被单引号包围的变量中进行eval
(
esc="'\''"
file="/Applications/iWork '09/Pages.app"
file="${file//\'/${esc}}"
#echo "'${file}'"; ls -bdl "'${file}'"
evalstr="echo '${file}'; ls -bdl '${file}'"
#set -xv
eval "${evalstr}"
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接