我熟悉Linux中的split命令。如果我有一个100行的文件,
split -l 5 myfile.txt
...将myfile.txt文件分成20个文件,每个文件有5行,并将它们写入文件。
我的问题是,如果给定一个100列的制表符分隔的文件,是否有类似的命令可以将该文件分成20个较小的文件,每个文件有5列和所有行?
我知道如何使用cut命令,但我希望有一个我从未听说过的简单的UNIX命令,可以在不用perl或其他东西包装cut的情况下完成此操作。
提前感谢。
我熟悉Linux中的split命令。如果我有一个100行的文件,
split -l 5 myfile.txt
...将myfile.txt文件分成20个文件,每个文件有5行,并将它们写入文件。
我的问题是,如果给定一个100列的制表符分隔的文件,是否有类似的命令可以将该文件分成20个较小的文件,每个文件有5列和所有行?
我知道如何使用cut命令,但我希望有一个我从未听说过的简单的UNIX命令,可以在不用perl或其他东西包装cut的情况下完成此操作。
提前感谢。
#!/bin/bash
(($# == 2)) || { echo -e "\nUsage: $0 <file to split> <# columns in each split>\n\n"; exit; }
infile="$1"
inc=$2
ncol=$(awk 'NR==1{print NF}' "$infile")
((inc < ncol)) || { echo -e "\nSplit size >= number of columns\n\n"; exit; }
for((i=0, start=1, end=$inc; i < ncol/inc + 1; i++, start+=inc, end+=inc)); do
cut -f$start-$end "$infile" > "${infile}.$i"
done
for
参数中扣除0.02美元符号,0.02花括号包围infile
,0.04使用AWK而不是类似于read -r -a arr <“$ infile”;ncol = $ {#arr [@]}
的东西,另外0.02为echo
而不是printf
。);) - Dennis Williamson 文件1:
col_1 col_2
文件2:
col_1 col_3
文件3:
col_1 col_4
- Waqas Khokhar如果你只需要一个QAD(快速且简单)的解决方案,用于我这种情况下一个固定的8列分隔符csv文件
#!/bin/bash
# delimiter is ;
cut -d';' -f1 "$1" > "${1}.1"
cut -d';' -f2 "$1" > "${1}.2"
cut -d';' -f3 "$1" > "${1}.3"
cut -d';' -f4 "$1" > "${1}.4"
cut -d';' -f5 "$1" > "${1}.5"
cut -d';' -f6 "$1" > "${1}.6"
cut -d';' -f7 "$1" > "${1}.7"
cut -d';' -f8 "$1" > "${1}.8"
感谢帮助。我希望有一个类似于split的Unix命令,但最终我通过SiegeX的建议使用perl包装了cut命令。
#!/usr/bin/perl
chomp(my $pwd = `pwd`);
my $help = "\nUsage: $0 <file to split> <# columns in each split>\n\n";
die $help if @ARGV!=2;
$infile = $ARGV[0];
chomp($ncol = `head -n 1 $infile | wc -w`);
$start=1;
$inc = $ARGV[1];
$end = $start+$inc-1;
die "\nSplit size >= number of columns\n\n" if $inc>=$ncol;
for($i=1 ; $i<$ncol/$inc +1 ; $i++) {
if ($end>$ncol) {$end=$ncol;}
`cut -f $start-$end $infile > $infile.$i`;
$start += $inc;
$end += $inc;
}
这是我的解决方案:
首先是一个输入生成器:
1 #!/usr/bin/env ruby
2 #
3 def usage(e)
4 puts "Usage #{__FILE__} <n_rows> <n_cols>"
5 exit e
6 end
7
8 usage 1 unless ARGV.size == 2
9
10 rows, cols = ARGV.map{|e| e.to_i}
11 (1..rows).each do |l|
12 (1..cols).each {|c| printf "%s ", c }
13 puts ""
14 end
分割工具:
1 #!/usr/bin/env ruby
2 #
3
4 def usage(e)
5 puts "Usage #{__FILE__} <column_start> <column_end>"
6 exit e
7 end
8
9 usage 1 unless ARGV.size == 2
10
11 c_start, c_end = ARGV.map{|e| e.to_i}
12 i = 0
13 buffer = []
14 $stdin.each_line do |l|
15 i += 1
16 buffer << l.split[c_start..c_end].join(" ")
17 $stderr.printf "\r%d", i if i % 100000 == 0
18 end
19 $stderr.puts ""
20 buffer.each {|l| puts l}
$ time ./gen.data.rb 1000 10 | ./split.rb 0 4 > ./out
生成1000行,每行有10列,并拆分前5列。我使用time(1)来测量运行时间。
我们可以使用一个简单的一行代码来完成您请求的拆分(顺序执行)。在单个节点中并行处理非常容易(检查bash构建命令wait),或者将它们发送到集群中。
$ ruby -e '(0..103).each {|i| puts "cat input.txt | ./split.rb #{i-4} #{i} > out.#{i/4}" if i % 4 == 0 && i > 0}' | /bin/bash
这基本上生成:
cat input.txt | ./split.rb 0 4 > out.1
cat input.txt | ./split.rb 4 8 > out.2
cat input.txt | ./split.rb 8 12 > out.3
cat input.txt | ./split.rb 12 16 > out.4
cat input.txt | ./split.rb 16 20 > out.5
cat input.txt | ./split.rb 20 24 > out.6
cat input.txt | ./split.rb 24 28 > out.7
cat input.txt | ./split.rb 28 32 > out.8
cat input.txt | ./split.rb 32 36 > out.9
cat input.txt | ./split.rb 36 40 > out.10
cat input.txt | ./split.rb 40 44 > out.11
cat input.txt | ./split.rb 44 48 > out.12
cat input.txt | ./split.rb 48 52 > out.13
cat input.txt | ./split.rb 52 56 > out.14
cat input.txt | ./split.rb 56 60 > out.15
cat input.txt | ./split.rb 60 64 > out.16
cat input.txt | ./split.rb 64 68 > out.17
cat input.txt | ./split.rb 68 72 > out.18
cat input.txt | ./split.rb 72 76 > out.19
cat input.txt | ./split.rb 76 80 > out.20
cat input.txt | ./split.rb 80 84 > out.21
cat input.txt | ./split.rb 84 88 > out.22
cat input.txt | ./split.rb 88 92 > out.23
cat input.txt | ./split.rb 92 96 > out.24
cat input.txt | ./split.rb 96 100 > out.25
并被导向到bash。
请注意并行计算的进程(或作业)数量,因为它会淹没您的存储空间(除非您有独立的存储卷)。
希望这有所帮助。让我们知道它对您运行的速度如何。
-drd
没有直接类似的东西可以按列拆分文件。然而,你可以用AWK以一种简单直接的方式来实现:
以下代码将input_file
按照NUMBER
列拆分成多个输出文件。
awk 'BEGIN{FS="\t"; m=NUMBER }
{ for(i=1;i<=NF;++i) {
s = (i%m==1 ? $i : s FS $i);
if (i%m==0 || i==NF) {print s > (sprintf("out.%0.5d",int(i/m)+(i%m!=0)))}
}}' input_file
input_file
文件分割成CHUNKS
个输出文件。awk 'BEGIN{FS="\t"; n=CHUNKS}
(NR==1){ m=int(NF/n)+(NF%n==0) }
{ for(i=1;i<=NF;++i) {
s = (i%m==1 ? $i : s FS $i);
if (i%m==0 || i==NF) {print s > (sprintf("out.%0.5d",int(i/m)+(i%m!=0)))}
}}' input_file
# do something smarter with output files (& clear on start)
XIFS="${IFS}"
IFS=$'\t'
while read -a LINE; do
for (( i=0; i< ${#LINE[@]}; i++ )); do
echo "${LINE[$i]}" >> /tmp/outfile${i}
done
done < infile
IFS="${XIFS}"
尝试使用文件名“infile”进行上述操作...
请注意保存和恢复IFS(是否有更好的想法?子shell?)
还要注意,如果您正在第二次运行,则会追加 - 您需要删除之前运行的输出...
while IFS=$'\t' read -a LINE; do
即可。 - SiegeX使用一些预处理,Split 实际上可以做到你想要的。
sed -E $'s/(([^\t]+\t){4}[^\t]+)\t/\\1\\n/g' myfile.txt | split -nr/20
这将使用 x
前缀写出二十个文件(在我的 split 版本中)。您可以通过以下方式验证此操作是否成功:
paste x* | cmp - myfile.txt
sed
将每行切分为二十行,然后使用循环块轮流将每行写入适当的文件。要使用不同的分隔符,请在表达式中交换标签。数字4应该是每个文件的列数减1,而split末尾的20则是文件数。可以使用split的其他参数来修改所写的文件名。此示例使用bash转义扩展将制表符写入sed表达式以及一个可以使用+
运算符的sed版本,但如果您的系统上不存在这些效果,则可以采用其他方式实现。我从核心工具邮件列表中的Reuti那里得到了此解决方案的变体。