如何分割文件并保留每个部分的第一行?

2024-11-12 08:36:00
admin
原创
27
摘要:问题描述:给定:一个具有“特殊”第一行(例如字段名称)的大型文本数据文件(例如 CSV 格式)。想要: coreutils 命令的等效命令split -l,但另外要求原始文件的标题行出现在每个结果片段的开头。我猜想某种混合物split会head起作用?解决方案 1:这是经过稍微清理的robhruska脚本:t...

问题描述:

给定:一个具有“特殊”第一行(例如字段名称)的大型文本数据文件(例如 CSV 格式)。

想要: coreutils 命令的等效命令split -l,但另外要求原始文件的标题行出现在每个结果片段的开头。

我猜想某种混合物splithead起作用?


解决方案 1:

这是经过稍微清理的robhruska脚本:

tail -n +2 file.txt | split -l 4 - split_
for file in split_*
do
    head -n 1 file.txt > tmp_file
    cat "$file" >> tmp_file
    mv -f tmp_file "$file"
done

我删除了不必要的地方的 、 和wccut我更改了一些文件名,使它们更有意义。我将其分成多行只是为了让它更容易阅读。ls`echo`

如果您想要更高级的功能,您可以使用mktemptempfile创建一个临时文件名,而不是使用硬编码的文件名。

编辑

使用 GNUsplit可以做到这一点:

split_filter () { { head -n 1 file.txt; cat; } > "$FILE"; }; export -f split_filter; tail -n +2 file.txt | split --lines=4 --filter=split_filter - split_

为了便于阅读,分解如下:

split_filter () { { head -n 1 file.txt; cat; } > "$FILE"; }
export -f split_filter
tail -n +2 file.txt | split --lines=4 --filter=split_filter - split_

--filter指定时,split为每个输出文件运行命令(在本例中为函数,必须导出),并将FILE命令环境中的变量设置为文件名。

过滤脚本或函数可以对输出内容甚至文件名进行任何操作。后者的一个例子可能是输出到变量目录中的固定文件名:> "$FILE/data.dat"例如。

解决方案 2:

此行代码将把大 csv 拆分成 999 条记录,并保留每条记录顶部的标题行(因此 999 条记录 + 1 个标题 = 1000 行)

cat bigFile.csv | parallel --header : --pipe -N999 'cat >file_{#}.csv'

根据 Ole Tange 的回答。

请参阅评论以获取有关安装并行的一些提示

解决方案 3:

您可以使用 GNU coreutils split >= 8.13 (2011) 中的新 --filter 功能:

tail -n +2 FILE.in | split -l 50 - --filter='sh -c "{ head -n1 FILE.in; cat; } > $FILE"'

解决方案 4:

您可以使用[mg] awk:

awk 'NR==1{
        header=$0; 
        count=1; 
        print header > "x_" count; 
        next 
     } 

     !( (NR-1) % 100){
        count++; 
        print header > "x_" count;
     } 
     {
        print $0 > "x_" count
     }' file

100 是每个切片的行数。它不需要临时文件,可以放在一行上。

解决方案 5:

我对 Bash-fu 还只是个新手,但我能够编写出这个只有两个命令的怪物。我相信还有更优雅的解决方案。

$> tail -n +2 file.txt | split -l 4
$> for file in `ls xa*`; do echo "`head -1 file.txt`" > tmp; cat $file >> tmp; mv -f tmp $file; done

这是假设您的输入文件是file.txt,您没有使用 的prefix参数split,并且您正在一个目录中工作,该目录中没有任何其他以 的split默认xa*输出格式开头的文件。此外,将“4”替换为您所需的分割线大小。

解决方案 6:

使用 GNU Parallel:

parallel -a bigfile.csv --header : --pipepart 'cat > {#}'

如果您需要对每个部分运行一个命令,那么 GNU Parallel 也可以帮助您做到这一点:

parallel -a bigfile.csv --header : --pipepart my_program_reading_from_stdin
parallel -a bigfile.csv --header : --pipepart --fifo my_program_reading_from_fifo {}
parallel -a bigfile.csv --header : --pipepart --cat my_program_reading_from_a_file {}

如果您想要将每个 CPU 核心分成 2 个部分(例如 24 个核心 = 48 个大小相等的部分):

parallel --block -2 -a bigfile.csv --header : --pipepart my_program_reading_from_stdin

如果要拆分成 10 MB 的块:

parallel --block 10M -a bigfile.csv --header : --pipepart my_program_reading_from_stdin

解决方案 7:

下面是一个 4 行代码,可用于将 bigfile.csv 拆分为多个较小的文件,并保留 csv 标头。仅使用内置 Bash 命令(head、split、find、grep、xargs 和 sed),这些命令应适用于大多数 *nix 系统。如果您安装了 mingw-64 / git-bash,也应适用于 Windows。


csvheader=`head -1 大文件.csv`
split -d -l10000 bigfile.csv smallfile_
查找 .|grep smallfile_ | xargs sed -i "1s/^/$csvheader
/"
sed -i ‘1d’ smallfile_00

逐行解释:

  1. 将标头捕获到名为csvheader的变量中

  2. bigfile.csv拆分为多个以smallfile_为前缀的小文件

  3. 查找所有小文件并使用xargssed -i将 csvheader 插入第一行。请注意,您需要在“双引号”内使用 sed 才能使用变量。

  4. 第一个名为 smallfile_00 的文件现在在第 1 行和第 2 行有冗余标头(来自原始数据以及第 3 步中的 sed 标头插入)。我们可以使用 sed -i '1d' 命令删除冗余标头。

解决方案 8:

这是Denis Williamson脚本的更强大版本。该脚本会创建大量临时文件,如果运行不完整,这些文件就被遗弃了,这真是太可惜了。因此,让我们添加信号捕获(请参阅http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.htmlhttp://tldp.org/LDP/abs/html/debugging.html)并删除临时文件;无论如何,这都是最佳做法。

trap 'rm split_* tmp_file ; exit 13' SIGINT SIGTERM SIGQUIT 
tail -n +2 file.txt | split -l 4 - split_
for file in split_*
do
    head -n 1 file.txt > tmp_file
    cat $file >> tmp_file
    mv -f tmp_file $file
done

将“13”替换为您想要的任何返回代码。哦,无论如何,您可能都应该使用 mktemp(正如一些人已经建议的那样),因此继续从陷阱行中的 rm 中删除“tmp_file”。请参阅信号手册页以了解更多要捕获的信号。

解决方案 9:

我喜欢 awk 版本的 marco,它采用了简化的单行代码,您可以在其中轻松指定所需的分割分数:

awk 'NR==1{print $0 > FILENAME ".split1";  print $0 > FILENAME ".split2";} NR>1{if (NR % 10 > 5) print $0 >> FILENAME ".split1"; else print $0 >> FILENAME ".split2"}' file

解决方案 10:

我真的很喜欢 Rob 和 Dennis 的版​​本,所以我想改进它们。

这是我的版本:

in_file=$1
awk '{if (NR!=1) {print}}' $in_file | split -d -a 5 -l 100000 - $in_file"_" # Get all lines except the first, split into 100,000 line chunks
for file in $in_file"_"*
do
    tmp_file=$(mktemp $in_file.XXXXXX) # Create a safer temp file
    head -n 1 $in_file | cat - $file > $tmp_file # Get header from main file, cat that header with split file contents to temp file
    mv -f $tmp_file $file # Overwrite non-header containing file with header-containing file
done

差异:

  1. in_file 是要拆分维护标题的文件参数

  2. 由于性能更好,awk因此使用tail`awk`

  3. 拆分为 100,000 行文件,而不是 4 行

  4. 分割文件名将是输入文件名附加下划线和数字(最多 99999 - 来自“-d -a 5”分割参数)

  5. 使用 mktemp 安全地处理临时文件

  6. 使用head | cat单行而不是双行

解决方案 11:

受到@Arkady 对一句话的评论的启发。

  • MYFILE 变量只是为了减少样板

  • split不显示文件名,但该--additional-suffix选项允许我们轻松控制预期内容

  • 通过删除中间文件rm $part(假设没有具有相同后缀的文件)

MYFILE=mycsv.csv && for part in $(split -n4 --additional-suffix=foo $MYFILE; ls *foo); do cat <(head -n1 $MYFILE) $part > $MYFILE.$part; rm $part; done

证据:

-rw-rw-r--  1 ec2-user ec2-user  32040108 Jun  1 23:18 mycsv.csv.xaafoo
-rw-rw-r--  1 ec2-user ec2-user  32040108 Jun  1 23:18 mycsv.csv.xabfoo
-rw-rw-r--  1 ec2-user ec2-user  32040108 Jun  1 23:18 mycsv.csv.xacfoo
-rw-rw-r--  1 ec2-user ec2-user  32040110 Jun  1 23:18 mycsv.csv.xadfoo

当然,还可以head -2 *foo看到标题被添加了。

解决方案 12:

一种简单但可能不那么优雅的方法:预先切断标题,拆分文件,然后使用 cat 或正在读取它的任何文件重新连接每个文件的标题。因此类似于:

  1. head -n1 文件.txt > 标题.txt

  2. 拆分-l 文件.txt

  3. 猫头.txt f1.txt

解决方案 13:

使用以下代码我得到了更好的结果,每个拆分文件都会有一个标题,并且生成的文件都会有一个规范化的名称。

export F=input.csv && LINES=3 &&\nexport PF="${F%.*}_" &&\nsplit -l $LINES "${F}" "${PF}" &&\nfor fn in $PF*
do
  mv "${fn}" "${fn}.csv"
done &&\nexport FILES=($PF*) && for file in "${FILES[@]:1}"
do
  head -n 1 "${F}" > tmp_file
  cat "$file" >> tmp_file
  mv -f tmp_file "${file}"
done

输出

$ wc -l input*
  22 input.csv
   3 input_aa.csv
   4 input_ab.csv
   4 input_ac.csv
   4 input_ad.csv
   4 input_ae.csv
   4 input_af.csv
   4 input_ag.csv
   2 input_ah.csv
  51 total

解决方案 14:

迟到了...

阅读完所有这些内容后,我将其添加到我的 bashrc 中:

split_csv_file() {
    _split_csv_filter() {
        { head -n 1 "${1}"; cat; } >| "$FILE";
    }
    export -f _split_csv_filter

    local file_name="${1##*/}"
    local file_base="${file_name%.*}"
    local chunks="${2}"
    echo "$file_name $file_base $chunks"
    tail -n +2 "${1}" | split -n l/"${chunks}" -d --filter="_split_csv_filter ${1}" - "${file_base}." --additional-suffix=.csv
}

这使我能够将# split_csv_file sample.csv 25csv 文件拆分成 25 个部分。感谢之前的所有回答,它们让我找到了这个非常方便的 bash 函数。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用