Объединение отсортированных файлов с fifos - PullRequest
5 голосов
/ 13 июня 2011

У меня есть несколько отсортированных, сжатых файлов в каталоге. Как мне объединить некоторые из них в другой отсортированный файл gzip? Прямо сейчас я использую явные фифы. Есть ли способ сделать это в 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 $@?

ПРИМЕЧАНИЕ. Размер каждого из файлов превышает 10 ГБ (и 100 ГБ разархивировано). У меня есть диск 2TB, так что это не проблема. Кроме того, эта программа ДОЛЖНА работать в режиме O (n), иначе она станет невозможной.

Ответы [ 3 ]

3 голосов
/ 13 июня 2011

Вы можете комбинировать eval и «процесс замены» с Bash.Если предположить, что базовые имена файлов не содержат пробелов (что, возможно, имеет место $@ вместо "$@"), то что-то вроде:

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 голос
/ 14 июня 2011

С одинарными кавычками в именах файлов вам придется работать намного усерднее.

Вот способ избежать одинарных кавычек в именах файлов (или путях к файлам), которые будут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}"
)
1 голос
/ 13 июня 2011

Для меня ваш вопрос немного неясен, но если я понимаю вашу потребность, попробуйте это:

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

Если вы хотите сделать все файлы определенного типа в 1 dir, тогда вы можете использовать file*.type в качестве входного списка для gunzip, иначе, в моем примере, вам нужно будет явно перечислить каждый файл.

Опция -c указывает «отправлять вывод в stdout», который представляет собой чтение по каналу, отправленное в sort, который отправляет свой вывод в stdout, канал и в gzip, причем его stdout перенаправляется в финальный файл. -9 - это максимальное сжатие, которое дает наименьший файл (для gzip), но занимает больше времени. Вы можете задать явное число от -1 до -9, чтобы настроить размер / время сжатия для сжатия компромисса для ваших нужд.

Надеюсь, это поможет.

...