我有一个巨大的文件(其中超过 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显示所有大文件*的相同时间感谢触碰命令。