如何分割一个文件,并保持每一块的第一行?

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

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

我猜测一些混合的splithead会做的伎俩?

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

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

如果你想要mktemp ,你可以使用mktemptempfile来创build一个临时文件名,而不是使用硬编码的文件名。

编辑

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

当指定--filtersplit为每个输出文件运行命令(在这种情况下,必须导出一个函数),并在命令的环境中将variablesFILE设置为FILE名。

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

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

对于巴什夫来说,我是一个新手,但是我能够制造这个双重命令的怪物。 我相信有更优雅的解决scheme。

 $> 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 

这是假设你的input文件是file.txt ,你没有使用prefix参数来split ,而你正在一个没有任何其他文件的split的默认xa*输出格式的文件开始工作。 另外,用你想要的分割线尺寸replace“4”。

您可以在GNU coreutils split> = 8.13(2011)中使用新的–filterfunction:

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

这是丹尼斯·威廉姆森剧本的一个更强大的版本。 这个脚本创build了很多临时文件,如果运行不完整的话,他们还是躺在旁边,这将是一个耻辱。 因此,我们添加信号捕获(请参阅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 

用你想要的任何返回代码replace“13”。 呵呵,你也许应该使用mktemp(正如一些人已经build议的那样),所以继续,并从trap中删除“tmp_file”,看看信号手册中的更多信号。

我不确定直接从其他人的站点复制脚本的规则,但Geekology有一个很好的脚本来做你想做的事情,有几条评论证实它是有效的。 请务必按照底部附注中的注释做tail -n +2

我喜欢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 

我真的很喜欢罗布和丹尼斯的版本,所以我想改善他们。

这是我的版本:

 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具有更好的性能,所以使用awk而不是tail
  3. 分成100,000行文件,而不是4
  4. 分割文件名将是input文件名称附加下划线和数字(最多99999 – 从“-d -a 5”拆分参数)
  5. 使用mktemp安全地处理临时文件
  6. 使用单个head | cat head | cat线而不是两条线