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

时间:2009-09-11 15:42:55

标签: linux bash file shell text

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

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

我猜测splithead的混合物可以解决这个问题吗?

13 个答案:

答案 0 :(得分:45)

这是 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

我在不需要的地方删除了wccutlsecho。我改变了一些文件名,使它们更有意义。我把它分成多行只是为了让它更容易阅读。

如果您想获得幻想,可以使用mktemptempfile创建临时文件名,而不是使用硬编码文件名。

修改

使用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时,split为每个输出文件运行命令(在这种情况下为一个必须导出的函数),并在命令的环境中设置变量FILE ,到文件名。

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

答案 1 :(得分:12)

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

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

答案 2 :(得分:10)

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

答案 3 :(得分:4)

当谈到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的{​​{1}}参数,而你正在一个没有任何其他目录的工作以split的默认split输出格式开头的文件。另外,将“4”替换为所需的分割线尺寸。

答案 4 :(得分:3)

这会将大的csv分成999行,每行的标题都放在顶部

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

基于Ole Tange的回答。 (关于Ole的回答:您不能在pipepart中使用行数)

答案 5 :(得分:2)

这是 Denis Williamson 脚本的更强大版本。该脚本会创建大量临时文件,如果运行不完整,它们会被遗忘,这将是一种耻辱。所以,让我们添加信号捕获(参见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_file'。请参阅信号手册页以获取更多信号。

答案 6 :(得分:1)

我不确定直接从其他人的网站复制脚本的规则,但Geekology有一个很好的脚本来做你想做的事情,有一些评论确认它有效。请务必按照底部附近的评论中的说明tail -n +2

答案 7 :(得分:1)

我喜欢marco的awk版本,从这个简化的单行程中采用,您可以根据需要轻松指定分割分数:

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

答案 8 :(得分:1)

我真的很喜欢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行代替两行

答案 9 :(得分:1)

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

答案 10 :(得分:1)

下面是一个4衬线,可用于保留csv标头(使用:标头,拆分,查找,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

说明:

  • 将标头捕获到名为 csvheader
  • 的变量中
  • 将大文件拆分为多个较小的文件(前缀为smallfile _)
  • 查找所有小文件,然后使用 xargs sed -i 将csvheader插入FIRST行。请注意,您需要在“双引号”中使用sed才能使用变量。
  • 第一个名为smallfile_00的文件现在将在第1行和第2行具有冗余头(来自原始数据以及步骤3中的sed头插入)。我们可以使用sed -i'1d'命令删除冗余标头。

答案 11 :(得分:1)

受到@Arkady对单线的评论的启发。

  • MYFILE变量只是为了减少样板
  • --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

当然还有orders.city才能看到标题。

答案 12 :(得分:0)

一种简单但可能不那么优雅的方法:事先剪掉标题,分割文件,然后使用cat或正在读取文件的任何文件重新加入每个文件的标题。 像这样:

  1. head -n1 file.txt> header.txt
  2. 分割-l file.txt
  3. cat header.txt f1.txt