我的旧答案在下面,不再适用...
您可以将此 awk 用于可能包含数百万行或数百万列的文件。基本方案是将所有值吸到一个数组中,然后在最后使用索引算法和嵌套循环来获得正确的顺序:
$ cat col.awk
{
for (i=1; i<=NF; i++) {
vals[++numVals] = $i
}
}
END {
for(col_offset=0; col_offset + cols <= NF; col_offset+=cols) {
for (i=1; i<=numVals; i+=NF) {
for(j=0; j<cols; j++) {
printf "%s%s", vals[i+j+col_offset], (j<cols-1 ? FS : ORS)
}
}
}
}
$ awk -f col.awk -v cols=2 file
4 5
m d
t 7
h 5
...
3 4
4 1
d f
5 9
q w
我的旧答案是基于在大多数具有大量行的 awk 中看到的显着减速。
有关减速的更多讨论,请参阅this question。
以下原始答案中的 cmets 不再适用。
老答案
这里只是为了保持一致性...
这里的awk 解决方案对小文件都很好。它们的共同点是文件要么需要适合 RAM,要么如果文件不适合 RAM,操作系统虚拟内存是可接受的后备。但是对于较大的文件,由于awk 的时间呈指数增长,您可能会得到非常糟糕的结果。对于您的文件的12MB 版本,内存中的 awk 变得非常慢.
如果有数百万个行而不是数百万个列,就会出现这种情况。
内存解决方案的唯一替代方法是多次读取文件或自己管理临时文件。 (或者使用在内部管理 VM 的脚本语言,例如 Perl 或 Python……Timur Shatland 的 Perl 即使处理大文件也很快。)
awk 没有一个简单的机制来多次循环文件直到一个过程完成。您需要使用 shell 来执行此操作并多次调用 awk。
这是一个python 脚本,它逐行读取文件并打印cols 列数,直到打印完所有原始列
$ cat sys.py
import sys
filename=sys.argv[1]
cols=int(sys.argv[2])
offset=0
delimiter=" "
with open(filename, "r") as f:
max_cols=len(f.readline().split())
while offset<max_cols:
with open(filename, "r") as f:
for line in f:
col_li=line.rstrip().split()
l=len(col_li)
max_cols=l if max_cols>l else l
print(delimiter.join(col_li[offset:offset+cols]))
offset+=cols
这有悖常理,但多次读取文件通常比吞下整个文件要快得多且效率更高——如果吞下会导致数据更大的错误结果。
那么与本文中的awks 之一相比,它的表现如何?让我们计时吧。
根据您的示例,内存中的 awk 可能会更快:
$ cat file
4 5 6 2 9 8 4 8
m d 6 7 9 5 4 g
t 7 4 2 4 2 5 3
h 5 6 2 5 s 3 4
r 5 7 1 2 2 4 1
4 1 9 0 5 6 d f
x c a 2 3 4 5 9
0 0 3 2 1 4 q w
$ time python pys.py file 2 >file2
real 0m0.027s
user 0m0.009s
sys 0m0.016s
$ time awk -v n=2 '{for(i=1;i<=NF;++i) {
j=int((i-1)/n); a[j] = a[j] $i (i%n==0 || i==NF ?ORS:OFS) }}
END{for(j=0;j<=int(NF/n);j++) printf "%s", a[j]}' file >file3
real 0m0.009s
user 0m0.003s
sys 0m0.003s
这是真的。但是,让我们用这个 Python 脚本将文件放大 1000 倍:
txt='''\
4 5 6 2 9 8 4 8
m d 6 7 9 5 4 g
t 7 4 2 4 2 5 3
h 5 6 2 5 s 3 4
r 5 7 1 2 2 4 1
4 1 9 0 5 6 d f
x c a 2 3 4 5 9
0 0 3 2 1 4 q w
'''
with open('/tmp/file', 'w') as f:
f.write(txt*1000) # change the 1000 to the multiple desired
# file will have 8000 lines and about 125KB
以同样的方式重新运行这些时间,你会得到:
#python
real 0m0.061s
user 0m0.044s
sys 0m0.015s
# awk
real 0m0.050s
user 0m0.043s
sys 0m0.004s
大约在同一时间...现在通过将原始文件乘以 100,000 得到 800,000 行和 12MB 并再次运行计时来使文件更大:
# python
real 0m3.475s
user 0m3.434s
sys 0m0.038s
#awk
real 22m45.118s
user 16m40.221s
sys 6m4.652s
对于 12MB 的文件,内存中的方法基本上无法使用,因为这台计算机上的 VM 系统需要进行大量磁盘交换来管理特定类型的内存分配。可能是O n**2 或更糟。这台电脑是2019 Mac Pro 16核Xeon 192GB内存,所以不是硬件...