我有一个制表符分隔文件,其中包含> 10,000行,每行可变数量的列数(33-35)。
对于有34列的行,我想将第3-4列折叠为一个:
col1 col2 col3 col4 ... col34
index1 tool kit math new
到 - >
col1 col2 col3 ... col33
index1 tool kit;math new
同样,对于35列,我想将第3-5列折叠成一列。我的预感是,可能有一种方法可以利用AWK和NF来实现这一目标。任何提示或帮助?
答案 0 :(得分:0)
你可以通过在给定两个或三个条件的情况下使用for循环遍历字段来缩短它,但是长手(对于awk新手来说更容易理解)是:
awk 'NF==35{print $1,$2,$3$4$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35} NF==34{print $1,$2,$3$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34} NF==33{$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33}' yourfile.txt
这是一个满口的,但它只是根据记录中的字段数打印出不同的字段组合。
答案 1 :(得分:0)
这是另一个awk
,具有更大的灵活性。我改用了5-7列。
$ cat file
col1 col2 col3 col4 col5
1 2 3 4 5 6
1 2 3 4 5
1 2 3 4 5 6 7
$ awk -v OFS='\t' 'NR==1{print; next}
NF>5{$3=$3 $4; t=1}
NF>6{$3=$3 $5; t=2}
t{for(i=4;i<=NF;i++) $i=$(i+t);
NF=NF-t; t=0}1' file
col1 col2 col3 col4 col5
1 2 34 5 6
1 2 3 4 5
1 2 345 6 7
答案 2 :(得分:0)
在awk中:
NF>5 { # if more than 5 (33) fields
for(i=(NF-5)-1; i>=0; i--) # execute next for once or twice
for(j=3+i; j<=NF; j++)
$j=( j<4+i ? $j ";" : "" ) $(j+1) # catenate once or twice on i
NF=5
} $1=$1 # is this a problem?
运行它:
$ awk -v OFS='\t' -f program.awk karakfa\'s.txt
col1 col2 col3 col4 col5
1 2 3;4 5 6
1 2 3 4 5
1 2 3;4;5 6 7
答案 3 :(得分:0)
awk paradigm实施了270 lines of TXR Lisp:
预热:整个文件中的基本列合并:
$ txr -e '(awk ((>= nf 4) (set [f 2..4] (list `@[f 2];@[f 3]`)))
(t))'
1
1
1 2
1 2
1 2 3
1 2 3
1 2 3 4
1 2 3;4
1 2 3 4 5
1 2 3;4 5
字段是列表f
,而不是涉及美元符号的特殊语法小工具,因此它们易受切片分配的影响:(set [target-sequence n..m] source-sequence)
替换n..m
切片(来自{{ 1 {} n
,m
除m
target-sequence
之外的source-sequence
。索引是从零开始的,所以要替换第三个和第四个字段,我们表示切片2..4
:即索引2和3,不包括4。
(t)
是一个没有条件的条件 - 操作条款,类似于Awk&#39; 1
:它触发隐式(prn)
形式,打印rec
(相当于$0
); t
是一个自我评估的符号,它规范地表示布尔值为true,但nil
以外的任何值都为真。当以某种方式操纵f
时,例如通过上面的切片分配,rec
会自动从字段重新构建,方法是在它们之间插入ofs
,就像在Awk Classic中填充字段一样重组$0
。
现在,如何将第一行作为要打印的标题处理然后忽略:
$ txr -e '(awk ((= nr 1) (prn) (next))
((>= nf 4) (set [f 2..4] (list `@[f 2];@[f 3]`)))
(t))'
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 3 3;4 5
接下来,我们要求改变34对35的范围。我们如何使用条件&#34; 34或更多列&#34;。可以通过更改表达式来处理移位范围:
[f 2..4]
到
[f (if (> nf 34) 2..5 2..4))]
但是如果我们将条件范围绑定到变量然后在几个地方使用它,我们可以以统一的方式完成它。我们只需要测试一次条件。结果是:
$ txr -e '(awk ((= nr 1) (prn) (next))
((>= nf 4) (let ((r (if (> nf 34) 2..5 2..4)))
(set [f r] (list `@{[f r] ";"}`))))
(t))'
col1 col2 ...
col1 col2 ...
1 2 3 4 5 6 7 8 9 0 A B C D E F G H I J K L M N O P Q R S T U V W X Y
1 2 3;4;5 6 7 8 9 0 A B C D E F G H I J K L M N O P Q R S T U V W X Y
1 2 3 4 5 6 7 8 9 0 A B C D E F G H I J K L M N O P Q R S T U V W X
1 2 3;4 5 6 7 8 9 0 A B C D E F G H I J K L M N O P Q R S T U V W X
在反引号分隔的准字符串文字中,语法@{[seq range] string}
将使用字符串作为分隔符插入序列的一个片段。我们只是插入范围r
,它与我们删除的范围相同,并替换为结果字符串;我们有条件地根据我们是否有超过34列来切换r
,因此测试仅在一次,并且r
在两个地方使用。