Что лучше, Python или Bash для выборочного объединения большого количества файлов? - PullRequest
3 голосов
/ 12 марта 2010

У меня около 20000 файлов, поступающих с выхода какой-либо программы, и их имена следуют формату:

data1.txt
data2.txt
...
data99.txt
data100.txt
...
data999.txt
data1000.txt
...
data20000.txt

Я хотел бы написать скрипт, который получает в качестве входного аргумента число N. Затем он создает блоки из N сцепленных файлов, поэтому, если N = 5, он создает следующие новые файлы:

data_new_1.txt: it would contain (concatenated) data1.txt to data5.txt (like cat data1.txt data2.txt ...> data_new_1.txt )

data_new_2.txt: it would contain (concatenated) data6.txt to data10.txt
.....

Интересно, что, по вашему мнению, будет лучшим подходом для этого, будь то bash, python или другой, такой как awk, perl и т. Д.

Лучший подход, который я имею в виду с точки зрения простейшего кода.

Спасибо

Ответы [ 7 ]

4 голосов
/ 12 марта 2010

Вот версия Python (2.6) (если у вас есть Python 2.5, добавьте первую строку, которая говорит

from __future__ import with_statement

и скрипт тоже будет работать) ...:

import sys

def main(N):
   rN = range(N)
   for iout, iin in enumerate(xrange(1, 99999, N)):
       with open('data_new_%s.txt' % (iout+1), 'w') as out:
           for di in rN:
               try: fin = open('data%s.txt' % (iin + di), 'r')
               except IOError: return
               out.write(fin.read())
               fin.close()

if __name__ == '__main__':
    if len(sys.argv) > 1:
        N = int(sys.argv[1])
    else:
        N = 5
    main(N)

Как видно из других ответов и комментариев, мнения о производительности расходятся - некоторые полагают, что запуск Python (и импорт модулей) сделает это медленнее, чем bash (но, по крайней мере, часть импорта является поддельной: sys, единственный необходимый модуль - это встроенный модуль, не требующий «загрузки» и, следовательно, в основном незначительные накладные расходы на его импорт); Я подозреваю, что избегание повторного форка / exec cat может замедлить bash; другие считают, что ввод-вывод будет доминировать в любом случае, что делает оба решения эквивалентными. Вам нужно будет сравнить свои собственные файлы в своей системе, чтобы решить это сомнение в производительности.

1 голос
/ 13 марта 2010

как насчет одного лайнера? :)

ls data[0-9]*txt|sort -nk1.5|awk 'BEGIN{rn=5;i=1}{while((getline _<$0)>0){print _ >"data_new_"i".txt"}close($0)}NR%rn==0{i++}'
1 голос
/ 12 марта 2010

Мне нравится этот, который экономит на выполнении процессов, только 1 кот на блок

#! /bin/bash

N=5 # block size
S=1 # start
E=20000 # end

for n in $(seq $S $N $E)
do
    CMD="cat "
    i=$n
    while [ $i -lt $((n + N)) ]
    do
        CMD+="data$((i++)).txt "
    done
    $CMD > data_new_$((n / N + 1)).txt
done
1 голос
/ 12 марта 2010

Лучший в каком смысле? Bash может сделать это довольно хорошо, но вам может быть сложнее написать хороший сценарий bash, если вы более знакомы с другим языком сценариев. Хотите оптимизировать что-то конкретное?

Тем не менее, вот реализация bash:

 declare blocksize=5
 declare i=1
 declare blockstart=1
 declare blockend=$blocksize
 declare -a fileset 
 while [ -f data${i}.txt ] ; do
         fileset=("${fileset[@]}" $data${i}.txt)
         i=$(($i + 1))
         if [ $i -gt $blockend ] ; then
                  cat "${fileset[@]}" > data_new_${blockstart}.txt
                  fileset=() # clear
                  blockstart=$(($blockstart + $blocksize))
                  blockend=$(($blockend+ $blocksize))
         fi
 done

РЕДАКТИРОВАТЬ: я вижу, что вы сейчас говорите "Best" == "Простейший код", но то, что просто, зависит от вас. Для меня Perl проще, чем Python, для некоторых Awk проще, чем bash. Это зависит от того, что вы знаете лучше всего.

Снова РЕДАКТИРОВАТЬ: вдохновленный dtmilano, я изменил свой, чтобы использовать cat один раз для размера блока, поэтому теперь cat будет называться 'only' 4000 раз.

0 голосов
/ 12 марта 2010

Достаточно просто?

make_cat.py

limit = 1000
n = 5
for i in xrange( 0, (limit+n-1)//n ):
     names = [ "data{0}.txt".format(j) for j in range(i*n,i*n+n) ]
     print "cat {0} >data_new_{1}.txt".format( " ".join(names), i )

Сценарий

python make_cat.py | sh
0 голосов
/ 12 марта 2010

Допустим, если у вас есть простой скрипт, который объединяет файлы и хранит для вас счетчик, например:

#!/usr/bin/bash
COUNT=0
if [ -f counter ]; then
  COUNT=`cat counter`
fi
COUNT=$[$COUNT+1]
echo $COUNT > counter
cat $@ > $COUNT.data

Командная строка будет делать:

find -name "*" -type f -print0 | xargs -0 -n 5 path_to_the_script
0 голосов
/ 12 марта 2010

Поскольку это легко сделать в любой оболочке, я бы просто использовал это.

Это должно сделать это:

#!/bin/sh
FILES=$1
FILENO=1

for i in data[0-9]*.txt; do
    FILES=`expr $FILES - 1`
    if [ $FILES -eq 0 ]; then
        FILENO=`expr $FILENO + 1`
        FILES=$1
    fi

    cat $i >> "data_new_${FILENO}.txt"
done

Python версия:

#!/usr/bin/env python

import os
import sys

if __name__ == '__main__':
    files_per_file = int(sys.argv[1])

    i = 0
    while True:
        i += 1
        source_file = 'data%d.txt' % i
        if os.path.isfile(source_file):
            dest_file = 'data_new_%d.txt' % ((i / files_per_file) + 1)
            file(dest_file, 'wa').write(file(source_file).read())
        else:
            break
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...