Разделить большой файл на основе контекста заголовка - PullRequest
1 голос
/ 08 декабря 2011

У меня большой файл (более 20 МБ), и мне нужно разбить его на более мелкие стволы.Файл input.txt выглядит следующим образом:

Rate: AAAA
.....
.....
....

Rate: AAAB

.....
.....
....

Rate: AAAC
.....

, и я хотел бы, чтобы вывод был:

AAAA.txt:
Rate: AAAA
.....
.....
....

AAAB.txt:
Rate: AAAB

.....
.....
....

AAAC.txt:
Rate: AAAC
.....

Мой сценарий оболочки очень медленный, поскольку он читает строкулиния, как я могу улучшить это.

INPUT=input.txt; key="Rate"
cat $INPUT | while read line
do
    if [[ "$line" == *"$key"*  ]]; then
        name=`echo "$line" | cut -d" " -f2`
    fi
    echo "$line" >> "./tmp/$name"
done

Ответы [ 3 ]

2 голосов
/ 08 декабря 2011
awk '/^Rate: / {
  if (fn) close(fn)
  fn = $2 ".txt"
  }
{ print > fn }' infile

Исправлено.

Редактировать: Предполагая, что temp_dir существует (см. Комментарии ниже):

awk '/^Rate: / {
  if (fn) close(fn)
  fn = "temp_dir/" $2 ".txt"
  }
{ print > fn }' infile
1 голос
/ 08 декабря 2011

Ваш процесс не очень медленный, потому что он читает файл построчно, а потому что он порождает два процесса в строке. Разделите работу по-другому, и все будет хорошо. Например, наличие одного процесса для определения линий «Скорость» и одного процесса на скорость должно значительно ускорить процесс:

for rate in $( sed -n 's/^Rate: \(.*\)/\1/p' $INPUT )
do
  sed -n "/^Rate: $rate\$/,/^Rate/ {/^Rate: / {/$rate/!d}; p}" $INPUT >$rate.txt
done

Если вы позволите себе использовать настоящие языки сценариев (или сохраните их с помощью bash, но больше не будете вызывать дочерние процессы), то вы сможете просмотреть файл только один раз. Например, в чистом bash это должно сократить это:

file=/dev/null
while read line
do
  rate=${line#Rate: }
  if [[ $line != $rate ]]; then file=$rate.txt
  else echo "$line" >> $file; fi
done <$INPUT
0 голосов
/ 08 декабря 2011

Я думаю, что отчасти проблема в том, что каждая строка включает выполнение команд echo и cut:

    name=`echo "$line" | cut -d" " -f2`

(По крайней мере, я думаю, что echo вызвал команду /bin/echo вместо встроенной оболочки echo. Я знаю cut есть внешняя программа.)

Кроме того, файлы назначения повторно открываются и закрываются (и записываются без использования стандартной буферизации ввода-вывода C, если только эти строки не равны огромные ) для каждой отдельной строки.

Переключение на другой язык, позволяющий вам выполнять эти операции без fork(2), execve(2), open(2), write(2) и close(2) для каждой отдельной строки, было бы улучшением. Вот мой снимок в Ruby:

$ cat split.rb 
#!/usr/bin/ruby

output = nil

File.open("input.txt").each { |line|
    if (line.match(/Rate: (.+)$/)):
        output = File.new("tmp/#{$1}", "a")
    end
    output.write(line)
}
$ 

Я проверил это на игрушечном вводе, который вы дали, похоже, все правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...