Разбить огромный файл в LINUX на несколько небольших файлов (каждый размером менее 100 МБ), разделенных на определенной строке с помощью pattern match

#linux #bash #sed #grep

#linux #bash #sed #grep

Вопрос:

У меня есть приведенный ниже исходный файл (~ 10 ГБ), и мне нужно разделить на несколько небольших файлов (<100 МБ каждый), и каждый файл должен иметь одинаковую запись заголовка. Сложность в том, что я не могу просто разделить файл на любую случайную строку, используя какую-либо команду split . Записи, принадлежащие агенту, не должны разделяться на несколько файлов. Для простоты я показываю здесь только 2 агента (в реальном файле их тысячи).

 Inout.csv
        Src,AgentNum,PhoneNum
        DWH,Agent_1234,phone1  
        NULL,NULL,phone2  
        NULL,NULL,phone3 
        DWH,Agent_5678,phone1 
        NULL,NULL,phone2 
        NULL,NULL,phone3
        DWH,Agent_9999,phone1 
        NULL,NULL,phone2 
        NULL,NULL,phone3

Output1.csv
        Src,AgentNum,PhoneNum
        DWH,Agent_1234,phone1  
        NULL,NULL,phone2  
        NULL,NULL,phone3
Output2.csv
        Src,AgentNum,PhoneNum
        DWH,Agent_5678,phone1 
        NULL,NULL,phone2 
        NULL,NULL,phone3
        DWH,Agent_9999,phone1 
        NULL,NULL,phone2 
        NULL,NULL,phone3

#!/bin/bash
    #Calculate filesize in bytes
    FileSizeBytes=`du -b $FileName | cut -f1`

    #Check for the file size
    if [[ $FileSizeBytes -gt 100000000 ]]
    then
        echo "Filesize is greater than 100MB"
        NoOfLines=`wc -l < $FileName` 
        AvgLineSize=$((FileSizeBytes / NoOfLines))
        LineCountInEachFile=$((100000000 / AvgLineSize))

            #Section for splitting the files

    else
        echo "Filesize is already less than 100MB. No splitting needed"
        exit 0
    fi
  

Я новичок в UNIX, но пробую этот скрипт bash самостоятельно и вроде как застрял при разделении файлов. Я не ожидаю, что кто-нибудь даст мне полный сценарий, я ищу какой-либо простой подход / рекомендацию, возможно, используя другие простые альтернативы, такие как sed или тому подобное. Заранее большое спасибо!

Комментарии:

1. Доступен ли для вас perl? Если нет, вы можете попробовать awk .

2. На данный момент я ограничен использованием только сценария оболочки в моем текущем проекте. Но если у вас есть решение pearl, я могу попробовать наверняка. Спасибо!

Ответ №1:

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

 #!/usr/bin/perl -w    
my $l=<>; chomp($l); my $header=$l;
my $agent=""; my $fh;

while ($l=<>) {    
   chomp($l);
   if ($l=~m/^s*[^,] ,(Agent_d ),[^,] /) {
       $agent="$1";
       open($fh,">","${agent}.txt") or die "$!";
       print $fh $header."n";
   }
   print $fh $l."n";
}
  

Используйте его следующим образом:

 ./perlscript.pl < inputfile.txt
  

Если у вас нет perl (проверьте наличие perl в /usr/bin/perl или в каком-либо другом подобном месте), я попытаюсь создать awk-скрипт. Дайте мне знать, если вы обнаружите проблемы, возникающие в приведенном выше скрипте.


В ответ на ваш обновленный запрос о том, что вы хотите разделить файл только с каждым выходным файлом размером менее 100 МБ, без разделения записей агента на два файла, и что этот заголовок печатается в каждом выходном файле, вот примерное представление о том, как вы можете это сделать. Это не приводит к точному вырезанию (потому что вам нужно будет вычислить перед записью). Если вы установите для $maxfilesize значение, подобное 95*1024*1024 или 99*1024*1024 , это должно позволить вам иметь файл размером менее 100 МБ (например, если максимальный размер записей агента составляет менее 5 МБ, то установите значение $maxfilesize равным 95* 1024 * 1024)

 #!/usr/bin/perl -w    
# Max file size, approximately in bytes
#
# For 99MB make it as 99*1024*1024
#
my $maxfilesize=95*1024*1024;    
#my $maxfilesize=400;

my $l=<>; chomp($l); my $header=$l;

my $fh;
my $filecounter=0;
my $filename="";
my $filesize=1000000000000; # big dummy size for first iteration

while ($l=<>) {
   chomp($l);    
   if ($l=~m/^s*[^,] ,Agent_d ,[^,] /) {
       if ($filesize>$maxfilesize) {
          print "FileSize: $filesizen";
          $filecounter  ; $filename=sprintf("outfile_d",$filecounter);
          print "Opening New File: $filenamen";
          open($fh,">","${filename}.txt") or die "$!";
          print $fh $header."n";
          $filesize=length($header);
       }
   }
   print $fh $l."n";
   $filesize =length($l);
   print "FileSize: $filesizen";
}
  

Если вы хотите более точные сокращения, чем это, я обновлю его, буферизую данные перед печатью.

Комментарии:

1. Спасибо за сценарий pearl! Если я правильно понимаю, я думаю, что скрипт разбивает файл на разные файлы для каждого агента (по одному файлу для каждого агента). Но в моем требовании каждый выходной файл может содержать сотни агентов, если он меньше 100 МБ. Я еще не пробовал скрипт, поэтому извините, если я все понял неправильно 🙂

2. Я вижу. Итак, вы просто хотите иметь строку заголовка, несколько тысяч строк в каждом выходном файле (размером менее 100 МБ)?

3. ДА. Пока размер каждого файла <100 МБ и записи для каждого агента не будут перекрываться между файлами (разделение должно происходить в конце всех записей для агента, а не случайным образом между ними). Спасибо за ваш быстрый ответ.

Ответ №2:

Шаг 1. Сохраните заголовок

Шаг 2. создайте переменную «content» в temp-сохраните то, что программа собирается прочитать

Шаг 3. начните читать следующие строки на python:

 if line.startswith("DWH"):
    if content != "":
        #if the content.len() reaches your predefined size, output_your_header   content here and reinitiate content by 'content = ""'
        #else, content.len() is still under size limit, keep adding the new agent to content by doing 'content  = line'
else:
    content  = line
  

Комментарии:

1. Спасибо @dgg32, но я ищу решение в оболочке UNIX.