输入文件
CARD SG CLASS ATT
11 0 DAS YES
CARD SG CLASS ATT
12 0 ECT YES
CARD SG CLASS ATT
13 0 VAS YES
1 DAS NO
CARD SG CLASS ATT
14 0 SAT YES
CARD SG CLASS ATT
15 0 CDT YES
1 VEG YES
2 GAT NO
期望输出:
CARD SG CLASS ATT
11 0 DAS YES
12 0 ECT YES
13 0 VAS YES
13 1 DAS NO
14 0 SAT YES
15 0 CDT YES
15 1 VEG YES
15 2 GAT NO
我做了什么:
awk ' /YES|NO/{VAL=$1};/ATT/{Print "CARD" "SG" "CLASS" "ATT" };/YES|NO/{ print VAL, $2, $3, $4} ' SCGR.txt | column -t
11 0 DAS YES
12 0 ECT YES
13 0 VAS YES
1 DAS NO
14 0 SAT YES
15 0 CDT YES
1 VEG YES
2 GAT NO
请帮帮我
答案1
试试这个(假设所有标题行完全相等):
awk ' NR==1{header=$0; count=NF; print; next}
($0~header) {next}
(NF==count) {col1=$1}
(NF<count) {printf("%s",col1)}
1 ' infile | column -t
答案2
调试你的一个ligner——几个小问题:
Print
--print
/ATT/{Print "CARD" "SG" "CLASS" "ATT" }
-/ATT/{print}
或者/ATT/;
/YES|NO/{VAL=$1}
也在3个字段记录中触发,清除之前保存的值。 (更改顺序或$4 ~ /YES|NO/
)
一个变体:
awk 'NF==4{v=$1;print} NF==3{print v,$0}'
删除额外的标题和缩进:
awk '/^CARD/ && NR>1 { next }
NF==4 { v=$1;print }
NF==3 { print v,$0 }'
答案3
如果我们不关心列对齐而只想标准化的空白分隔数据,则基本模式是:
awk -F' +' '{ $1 = ($1 ~ /^$/ ? prev : $1); prev = $1; print }'
看,如果我们设置一个不等于默认空间的自定义字段分隔符,我们就实现了真正的字段分隔。如果记录以匹配的分隔符开头,则将使用空字段进行分隔。
默认情况下,awk 不分离;它标记化:它从每个记录中提取标记,这些标记是一个或多个非空白/非换行符的序列。这意味着前导和尾随空白/换行符将被忽略。因此,如果缺少第 1 列,第 2 列中的值将变为第 1 列。
通过我们的/ +/
分隔符正则表达式,我们可以获得真正的分隔行为。带有前导空格和尾随空格的记录1 2 3 4
被视为<SEP>1<SEP>2<SEP>3<SEP>4<SEP>
。因此有六个字段:""
, "1"
, ..., "4"
, ""
。第一个之前<SEP>
和最后一个之后各有一个字段。
顺便说一句,如果第一条记录可能缺少字段,我们显然需要一个默认值prev
。我们也不希望将逻辑应用于标题。另外,让我们用 : 替换三元运算符if
,这样:
awk 'BEGIN { FS = " +"; prev = 0 }
NR == 1
NR > 1 { if ($1 == "") $1 = prev
print
prev = $1 }'
FOO BAR BAZ
FOO BAR BAZ
2 3 4
0 2 3 4
1 2 3 4
1 2 3 4
2 3 4
1 2 3 4
答案4
只为教育sed的决定
sed '
1b #output 1st line (header)
$!N #add next line to operate 2 lines altogether
s/\(.*\)\n\(CARD.*\)/\2\n\1/ #move line with CARD to first place
/^CARD/D #delete line with CARD and go to start
s/^\(\([0-9]*\s*\).*\n\)\s\s*/\1\2/
#repeat 1st field of 1st line if empty in 2nd
/\n/{P;D} #print&remove 1st line, go to start
'