将文件拆分为 10 个文件,每个文件中包含唯一的项目,并且每个文件中的最大行数限制

将文件拆分为 10 个文件,每个文件中包含唯一的项目,并且每个文件中的最大行数限制

我有一个巨大的文件(其中超过 2M 条记录)。这是我的要求:

  • 首先,将一个大文件分成 10 个较小的文件。
  • 文件格式应如下: <File_name>- <timestamp>-xx
    • <timestamp>每个文件中的时间相同
    • xx将代表它是从 1 到 10 的哪个文件
  • 文件的项目之间必须有清晰的分割。也就是说,我们不能在多个文件中包含相同的项目。

例如,如果我有如下文件:

ITEM,PARENT_PARTNUMBER,STORE_NUMBER,QUANTITY,BUYABLE,AVAILABILITYCODE,STORENAME,PHONENUMBER
400000209333,400000209333P,ALL,1297,1,2,,
400000209333,400000209333P,A-80007838,1297,1,2,,
400009664058,400009664058P,ALL,499,1,1,,
400009664058,400009664058P,A-80007838,477,1,1,,
400009664058,400009664058P,13806529,104,0,0,WDW - FLOWER & GARDEN,8-224-6122/5866
400000276151,400000276151P,ALL,0,0,0,,
400000276151,400000276151P,A-80007823,0,0,0,,
400000209692,400000209692P,ALL,8,1,1,,

然后我想把文件分成这样的。第一个文件(假设第一个文件已达到 20000 个限制,并且在 19999 年有项目编号更改,由于最大文件限制为 20000,因此不能位于同一文件中,我们需要维护文件中的唯一项目编号:

400000209333,400000209333P,ALL,1297,1,2,,
400000209333,400000209333P,A-80007838,1297,1,2,,
400009664058,400009664058P,ALL,499,1,1,,
400009664058,400009664058P,A-80007838,477,1,1,,
400009664058,400009664058P,13806529,104,0,0,WDW - FLOWER & GARDEN,8-224-6122/5866

第二个文件:

400000276151,400000276151P,ALL,0,0,0,,
400000276151,400000276151P,A-80007823,0,0,0,,
400000209692,400000209692P,ALL,8,1,1,,

以此类推,直到文件 10。

答案1

#!/bin/bash

file_name="huge.file"

#get file mask
my_mask="$(date +"$file_name-%F-")"

#collect lines with same item in one string separated by unexpected symbol
sed ':1;N;/^\([^,]\+,\).*\n\1/s/\n/×/;t1;P;D' "$file_name" > tmp.file

#divide tmp.file for 10 pieces without line splitting
split -dn l/10 "tmp.file" "$my_mask"

#split lines with same item back
sed -i 's/×/\n/g' "$my_mask"*

#remove tmp.file if need it
rm tmp.file

答案2

最终版本,我发誓......:)

假设:

  • 第一行包含标题,
  • ITEM 始终是第一列,
  • 所有具有相同 ITEM 编号的行都是连续的,
  • 具有相同 ITEM 编号的行数小于每个较小文件中的行数减 1。

此版本保证较小的文件将包含低于或等于指定上限的行数(即线路)。

#!/usr/bin/awk -f
BEGIN {
    numberOfLinesInCurrentFile=0;
    numberOfLinesInBuffer=0;
    filenumber=0;
    header="";
    previousITEM="";
    FS=",";
    timestamp=ENVIRON["TIMESTAMP"];
    numberOfLinesPerFile=ENVIRON["LINES"];
    currentFilename=FILENAME "-" timestamp "-00";
}
{
  if (NR == 1) {
    header=$0;
    print header >> currentFilename;
    numberOfLinesInCurrentFile=1;
  } else {
    currentITEM=$1;
    if (previousITEM != currentITEM) {
      for (i=0; i<numberOfLinesInBuffer; i++) {
        print bufferOfLines[i] >> currentFilename;
      }
      numberOfLinesInCurrentFile+=numberOfLinesInBuffer;
      numberOfLinesInBuffer=0;
      bufferOfLines[1]=$0
    }
    if ((numberOfLinesInCurrentFile+numberOfLinesInBuffer) >= numberOfLinesPerFile) {
        filenumber++;
        currentFilename=sprintf("%s-%s-%02d", FILENAME, timestamp, filenumber);
        print header >> currentFilename;
        numberOfLinesInCurrentFile=1;
    }
    bufferOfLines[numberOfLinesInBuffer++]=$0
    previousITEM=$1;
  }
}

线路用于指定每个较小文件的最大行数。

时间戳用于指定时间戳。

大文件是要分割的文件。

测试如下:

LINES=4000 TIMESTAMP=20160320101538 ./scriptv2.awk bigfile

ls bigfile*
bigfile                    bigfile-20160320101538-02  bigfile-20160320101538-04  bigfile-20160320101538-06  bigfile-20160320101538-08
bigfile-20160320101538-01  bigfile-20160320101538-03  bigfile-20160320101538-05  bigfile-20160320101538-07

----


供参考的第二个版本:它不保证每个较小文件中的行数将低于指定的限制。

又一个版本很快得到了测试。假设第一行包含标题,并且 ITEM 始终是第一列。所有具有相同 ITEM 编号的行都是连续的。

cat script.awk
#!/usr/bin/awk -f
BEGIN {
    filenumber=0;
    header="";
    previousITEM="";
    FS=",";
    timestamp=ENVIRON["TIMESTAMP"];
    numberOfLinesPerFile=ENVIRON["LINES"];
    currentFilename=FILENAME "-" timestamp "-00";
    changeFilenameWhenPossible=0;
}
{
  if (NR == 1) {
    header=$0;
  } else {
    currentITEM=$1;
    if (NR % numberOfLinesPerFile == 0) {
      if (previousITEM != currentITEM) {
        filenumber=filenumber+1;
        filenamberString=sprintf("%02d",filenumber);
        currentFilename=FILENAME "-" timestamp "-" filenamberString;
        changeFilenameWhenPossible=0;
        print header >> currentFilename;
      } else {
        changeFilenameWhenPossible=1;
      } 
    } else if (changeFilenameWhenPossible == 1 && previousITEM != currentITEM) {
      filenumber=filenumber+1;
      filenamberString=sprintf("%02d",filenumber);
      currentFilename=FILENAME "-" timestamp "-" filenamberString;
      changeFilenameWhenPossible=0;
      print header >> currentFilename;
    }
    previousITEM=$1;
  }
  print $0 >> currentFilename;
}

线路应设置为每个较小文件所需的行数。

时间戳应设置为指定的时间戳。

大文件是2M行文件。

测试如下:

chmod +x script.awk
LINES=200000 TIMESTAMP=20160318101538 ./script.awk bigfile

ls -1 bigfile-*
bigfile-20160318101538-01
bigfile-20160318101538-02
bigfile-20160318101538-03
bigfile-20160318101538-04
bigfile-20160318101538-05
bigfile-20160318101538-06
bigfile-20160318101538-07
bigfile-20160318101538-08
bigfile-20160318101538-09
bigfile-20160318101538-10

第一个答案供参考...

我注意到您想删除带有标题的第一行,对吧?

#!/bin/bash --
nblines=$(wc -l "${1}" | cut -d\  -f1)
nblines=$(((nblines - 1)/10))
tail -n +2 "${1}" | split -l $nblines -d -- - "${1}"-"${2}"-
touch -r "${1}" ./"${1}"?*

请务必使用大文件其中包含 11 行或更多。

触碰命令用于应用时间大文件到所有刚刚创建的较小的。消除触碰如果不需要的话。

新编辑尚未测试:

ls

bigfile
script.sh

chmod +x ./script.sh
./script.sh bigfile 20160309144430

ls -l bigfile*
-rw-r--r-- 1 jay stackgrp 556 Mar 16 17:03 bigfile
-rw-r--r-- 1 jay stackgrp 92 Mar 16 17:03 bigfile-20160309144430-00
-rw-r--r-- 1 jay stackgrp 42 Mar 16 17:03 bigfile-20160309144430-01

ETC。

注意LS显示所有大文件*的相同时间感谢触碰命令。

相关内容