根据Linux中的大小将较大的CSV文件拆分为多个文件

dsf9zpds  于 2023-10-16  发布在  Linux
关注(0)|答案(1)|浏览(164)

我有一个CSV文件是巨大的,我想分裂成多个文件的基础上的大小。
例如,我有一个名为TEST-REPORT-YYYYMMDDHHMMSS.CSV的CSV文件,大小为150MB
我想把这个文件分成多个文件的大小是5MB。这意味着分割后我们会得到30个文件。
我需要输出文件名为TEST-REPORT-[SEQUENCE]-YYYYMMDDHHMMSS.CSV
序列= 1、2、3、4、......
我已经尝试过这个split -d -a3 -b 5M --additional-suffix=.csv sample.csv test_和分裂是完美的工作,但只有第一个文件包含头(3行),但我需要它在所有的文件后,分裂产生的
分割后,我们需要持久化头部

pw136qt2

pw136qt21#

如果您使用的是基于BSD的Unix(例如macOS)或Linux上的GNU工具版本,则split实用程序会有所不同。您可以通过运行split --help快速区分它们;该选项仅由GNU的split支持,否则将抛出错误。这两个版本都不支持你想要的命名方案,它们总是会使用字母(例如,cab,aab,aac等)生成一个“序列”标记,并且无论你的前缀或后缀是什么,都会包含这个标记。
在CSV文件中,我也有一个头和尾,在我们拆分的文件中,需要持久化头,在尾中,需要更新计数
两个版本都支持-b 5m开关,用于将文件分块为单独的5 MB文件,但它们这样做与行尾无关。如果你想保留整行,你必须用line_count来分割它们。如果你想包含页眉和页脚行,split不支持这种操作。简单地说,这不是它的目的。
对于保留CSV行的 * 近似 * 每个文件大小,以及固定的页眉和可变的页脚,您需要使用更全面的脚本来解析和分发您想要的文件。Bash并不是最有效的方法,但是如果你对平均行长度有一个“猜测”,你可以粗略地计算一下,目标是每个文件固定的CSV行,而不是原始的字节数。
例如:如果平均行长度约为140个字符,则

1024 bytes x 1024 x 5 = 5 megabytes
5 megabytes / 140 chars per line ≈ 37500 lines per file

有了目标行长度,您应该“预处理”目标CSV文件,以裁剪标题行和脚注行。然后拆分结果,最后将每个文件后处理为一个命名为您实际需要的文件。
这里有一个例子,基于一个单行的标题行和一个多行的尾部页脚,这个脚本将用avg分块一个CSV。将140字符的行长度转换为1 mb的文件(相应地调整目标csv长度)。

#!/usr/bin/env bash

raw_file="${1}"
target_csv_length=7500

header="$(head -1 "$raw_file")"
footer_lines=3
footer="$(tail -$footer_lines "$raw_file")"

# Automating the readout of the trailing lines to produce a template is not recommended. Use `%d` for the count of lines or sequence number; if there are any `%` signs in the footer change these to `%%` to preserve them below.
footer_template='This CSV file has %d lines.
Produced on Friday, '"$(date)"' 
Copyright (c) Foobar, Inc. 2021.'

# Same goes for your filenames:
filename_template='TEST-REPORT-[%03d]-YYYYMMDDHHMMSS.CSV'

# Set up a scratch space for your files during processing
splitsville="$(mktemp -d)"
printf 'Wrote scratch directory: %s\n\n' "$splitsville"

# Now preprocess the CSV file and split it
footerless_length="$(( $(grep -cE . "$raw_file") - $footer_lines ))"
(
  cd "$splitsville"
  cat "$raw_file" \
  | head -$footerless_length \
  | tail +2 \
  | split -l $target_csv_length - raw.
)

# Now do the post-processing.
(
  cd "$splitsville" &&
  count=0
  for chunk in raw.*
  do
    chunk_len="$(grep -cE . "$chunk")" # grep is faster than wc
    csv_seq_filename="$(printf "$filename_template" "$count")"
    ((count++))

    printf '%s\n' "$header" > "$csv_seq_filename"
    cat "$chunk" >> "$csv_seq_filename"
    printf "$footer_template" "$chunk_len" >> "$csv_seq_filename"
    rm $chunk
  done
)
# Resulting files and sizes ...
du -hs "$splitsville"/*

相关问题