鉴于:一个带有“特殊”第一行的大文本数据文件(例如CSV格式)(例如,字段名称)。
通缉:相当于coreutils split -l
命令,但附加要求原始文件中的标题行显示在每个结果片段的开头。
我猜测split
和head
的混合物可以解决这个问题吗?
答案 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
我在不需要的地方删除了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
时,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
的差异:
awk
效果更佳,使用tail
代替awk
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
说明:
答案 11 :(得分:1)
受到@Arkady对单线的评论的启发。
--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或正在读取文件的任何文件重新加入每个文件的标题。 像这样: