我有一个文件低于模式
HDR1|20160101|1234|
N1|ABC|
XXX|21431415|3522352352|ITEM|
FORE|20140508|20140214|
SD|0|0039 - data|data|data|data|
SD|0|0211 - data|data|data|data|
SD|0|0039 - data|data|data|data|
SD|0|0211 - data|data|data|data|
FORE|20140508|20140214|
SD|0|0039 - data|data|data|data|
SD|0|0039 - data|data|data|data|
SD|0|0211 - data|data|data|data|
我想根据大小拆分文件,但也需要注意以下内容。
前3行是标题,我需要在我创建的每个分割文件中包含它。 从FORE开始的行与从SD开始的下面的行有关,所以我必须将它们保持在一起。
输出应如下所示。
拆分文件1:
HDR1|20160101|1234|
N1|ABC|
XXX|21431415|3522352352|ITEM|
FORE|20140508|20140214|
SD|0|0039 - data|data|data|data|
SD|0|0211 - data|data|data|data|
SD|0|0039 - data|data|data|data|
SD|0|0211 - data|data|data|data|
拆分文件2:
HDR1|20160101|1234|
N1|ABC|
XXX|21431415|3522352352|ITEM|
FORE|20140508|20140214|
SD|0|0039 - data|data|data|data|
SD|0|0039 - data|data|data|data|
SD|0|0211 - data|data|data|data|
我已经构建了一个伪代码,如下所示。我可以将多组这样的FORE和SD作为一组保存在一起,所以我已经设置了一个循环
create $file
create $line_num=5
create $file_size
create $top_size=20mb
read the first 4 lines of the original file and copy it in a temphdr file
Loop until last $line_num is encountered
read the header details and Append the header from the temphdr to the $file
for each $record starting the head -$line_num (5,6,7...etc) that contains FORE| in the first part
if the $file size is < $top_size
append the $record in the $file
increment $line_num
For each $record in head -$line_num that contains SD| in the first part
append the $record in the $file
increment $line_num
else
create a $file=$file+1
fi
end loop
end loop
除了上面提到的高级逻辑之外,有没有人让我知道是否还有其他有效的方法可以使用awk和sed等来实现这一点。
答案 0 :(得分:1)
没有什么比这更复杂了。这可以在纯shell中实现,根本没有外部命令(没有head
,awk
等)。
#!/usr/bin/env ksh
max_size=$(( 20 * 1024 * 1024 ))
# Read our three fixed header lines
headers=''
read -r line; headers+="$line"$'\n'
read -r line; headers+="$line"$'\n'
read -r line; headers+="$line"$'\n'
splitNum=1 # variable to track file number
splitFileName=$(printf 'split.%04d' "$splitNum") # generate first filename
exec >"$splitFileName" # and redirect stdout to that file
printf '%s' "${headers}" # print our headers...
cur_size=$(( ${#headers} )) # and set cur_size to their length
while IFS= read -r line; do # For each line:
# check for and manage rotation
if [[ $line = "FORE|"* ]]; then # If it's a FORE...
if (( cur_size > max_size )); then # ...and over size: start a new file
(( ++splitNum )) # increment the split number
splitFileName=$(printf 'split.%04d' "$splitNum") # generate a new filename
exec >"$splitFileName" # redirect stdout to that file
printf '%s' "${headers}" # print headers to stdout
cur_size=$(( ${#headers} )) # reset size to size of headers
fi
fi
# whether or not we had to do any of that:
printf '%s\n' "$line" # print the line we just read
cur_size=$(( cur_size + ${#line} + 1 )) # and increment cur_size
done
请注意,如果您将此移植到bash,则可能需要将splitFileName=$(printf 'split.%04d' "$splitNum")
更改为printf -v splitFileName 'split.%04d' "$splitNum"
。 ksh93足够聪明,可以自动优化命令替换中涉及的子shell; bash需要明确的语法来避免开销。
答案 1 :(得分:1)
您可以使用此<allow-navigation href="market://*" />
命令:
awk
在一行中:
awk -F '|' 'NR<=3{
hdr = hdr $0 RS
}
$1=="FORE"{
close(fn)
fn="split-" ++n
printf "%s%s", hdr, $0 RS > fn
}
$1=="SD"{
print > fn
}
END{close(fn)}' file
答案 2 :(得分:0)
使用
这样的行可以更轻松地回答这个问题FORE|20140508|20140214|\rSD|0|0039 - data|data|data|data|\rSD|0|0211 - data|data|data|data|\rSD|0|0039 - data|data|data|data|\rSD|0|0211 - data|data|data|data|
FORE|20140508|20140214|\rSD|0|0039 - data|data|data|data|\rSD|0|0039 - data|data|data|data|\rSD|0|0211 - data|data|data|data|
首先使用awk
预处理文件,将标头保存在临时文件中,并加入以SD
开头的行。
现在使用您喜欢的其他参数调用split -C 20m filename
。
接下来tr "\r" "\n"
到不同的行,并在所有文件中添加标题。
编辑:连接线的预处理可以使用
完成awk 'NR<=3 { print >> "filename.head" }
/^FORE/ { printf("%s%s",skipFirstNewline, $0); skipFirstNewline="\n" }
/^SD/ { printf("\r%s",$0) }
END{printf "\n" }' filename
当你检查结果时,你会对回车\r
感到困惑。因此,当您要检查输出时,请将\r
临时替换为rr
。