代码之家  ›  专栏  ›  技术社区  ›  CrashCodes

如何分割一个文件并将第一行保存在每一个片段中?

  •  46
  • CrashCodes  · 技术社区  · 15 年前

    鉴于: 一个大文本数据文件(如csv格式),带有“特殊”的第一行(如字段名)。

    通缉犯: 相当于coreutils split -l 命令,但附加要求原始文件的标题行出现在每个结果片段的开头。

    我猜是某种混合物 split head 会成功吗?

    11 回复  |  直到 5 年前
        1
  •  39
  •   Dennis Williamson    6 年前

    这是 罗布鲁斯卡 脚本清理了一点:

    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
    

    我删除 wc , cut , ls echo 在不必要的地方。我更改了一些文件名以使它们更有意义。我把它分成多行,只是为了更容易阅读。

    如果你想得到幻想,你可以用 mktemp tempfile 创建临时文件名而不是使用硬编码文件名。

    编辑

    使用GNU 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_
    

    可读性:

    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 已指定, 分裂 为每个输出文件运行命令(在本例中是一个函数,必须导出),并设置变量 FILE 在命令的环境中,返回到文件名。

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

        2
  •  11
  •   pixelbeat    10 年前

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

    tail -n +2 FILE.in |
    split -l 50 - --filter='sh -c "{ head -n1 FILE.in; cat; } > $FILE"'
    
        3
  •  10
  •   marco    15 年前

    您可以使用[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是每个切片的行数。 它不需要临时文件,可以放在一行上。

        4
  •  4
  •   Rob Hruska MegalomanINA    15 年前

    说到巴什福,我是个新手,但我能编造出这两个命令怪物。我相信还有更优雅的解决方案。

    $> 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 ,并且您正在一个没有以任何其他文件开头的目录中工作 分裂 默认值 xa* 输出格式。另外,将“4”替换为所需的分割线大小。

        5
  •  2
  •   Sam Bisbee    15 年前

    这是一个更强大的版本 丹尼斯·威廉姆森 剧本。这个脚本创建了很多临时文件,如果运行不完整而让它们到处乱放,那就太可惜了。那么,让我们添加信号捕获(请参见 http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html 然后 http://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_文件”。有关要捕获的更多信号,请参阅信号手册页。

        6
  •  2
  •   Tim Richardson    5 年前

    这将把大的csv分成999行,每个行的顶部都有标题。

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

    基于ole tange的答案。 (关于OLE的答案:不能将行计数与pipepart一起使用)

        7
  •  1
  •   Mark Rushakoff    15 年前

    我从不确定直接从其他人的网站复制脚本的规则,但是 Geekology 有一个很好的脚本来做你想做的事情,有一些评论确认它是有效的。一定要做 tail -n +2 如底部评论所述。

        8
  •  1
  •   DreamFlasher    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
    
        9
  •  1
  •   Garren S    9 年前

    我真的很喜欢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. 在文件中是要拆分维护头的文件参数
    2. 使用 awk 而不是 tail 由于 AWK 有更好的表现
    3. 拆分为100000行文件而不是4行
    4. split file name将输入文件名,并附加下划线和数字(最多99999个,来自“-d-a 5”split参数)
    5. 使用mktemp安全地处理临时文件
    6. 使用单一 head | cat 行而不是两行
        10
  •  1
  •   Ole Tange    6 年前

    使用GNU并行:

    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
    
        11
  •  1
  •   Thyag    5 年前

    下面是一个可用于保存csv头的4行程序(使用:head、split、find、grep、xargs和sed)

    csvheader=`head -1 bigfile.csv`
    split -d -l10000 bigfile.csv smallfile_
    find .|grep smallfile_ | xargs sed -i "1s/^/$csvheader\n/"
    sed -i '1d' smallfile_00
    
    

    说明:

    • 将头捕获到名为 CSV报头
    • 将大文件拆分为多个较小的文件(带前缀smallfile_uu)
    • 发现 所有小文件并使用将csvheader插入第一行 参数代换 SED-Ⅰ . 请注意,为了使用变量,需要在“双引号”中使用sed。
    • 第一个名为smallfile_00的文件现在将在第1行和第2行(从原始数据以及步骤3中的SED头插入)上具有冗余头。我们可以使用sed-i'1d'命令删除冗余头段。