我有桌子
M - A A -
- A G - -
M - - - G
我想执行:如果第一行的任何列包含“-”,则跳过打印该列
预期输出是
M A A
- G -
M - -
我尝试过类似的方法但没有成功
awk 'NR==1 && $i!="-" {print $i}'
有谁知道如何更正命令?
答案1
的一个变体艾德·莫顿的回答,它通过字段编号记住哪些字段不在-
第一行,然后根据数组中保存的索引重新形成输入中的每条记录,out
然后再打印新记录:
FNR == 1 {
for (i = 1; i <= NF; ++i)
if ($i != "-") out[++nf] = i
}
{
for (i = 1; i <= nf; ++i)
a[i] = $(out[i])
$0 = ""
for (i = 1; i <= nf; ++i)
$i = a[i]
print
}
在这里,我为了可读性牺牲了一点效率,在单独的循环中重建记录,而不是在第二个块的单个循环中打印所需的字段。
测试:
$ awk -f script.awk file
M A A
- G -
M - -
使用制表符作为输出字段分隔符运行:
$ awk -v OFS='\t' -f script.awk file
M A A
- G -
M - -
一段稍微太长的单行代码,取决于输入数据是否以制表符分隔:
$ cut -f "$(awk -v OFS=',' '{ nf=split($0,a); $0=""; for (i=1; i<=nf; ++i) if (a[i]!="-") $(++NF)=i; print; exit }' file)" file
M A A
- G -
M - -
这用于awk
输出字段编号不在-
第一行作为逗号分隔列表。然后将该列表移交给该列表,cut -f
由该列表实际输出文件中的数据。请注意,文件名(此处简称file
)在命令行上给出两次,一次 for ,awk
然后再次 for cut
。
答案2
$ cat tst.awk
NR == 1 {
for (i=1; i<=NF; i++) {
if ($i != "-") {
f[++numOutFlds] = i
}
}
}
{
for (i=1; i<=numOutFlds; i++) {
printf "%s%s", $(f[i]), (i<numOutFlds ? OFS : ORS)
}
}
$ awk -f tst.awk file
M A A
- G -
M - -
答案3
awk
隐式循环输入记录(行)和文件,但不循环必须显式执行的字段。在您的情况下,您需要循环第一行(标题行)中的字段来决定要包含哪些列,然后循环遍历每一个行(标题和非标题)以包含该行上所需的列。
您不清楚是否要查找以下标头字段等于(字符串)“-”或者可能将其作为(子)字符串。我还假设您有(所有)单个选项卡作为字段分隔符,而不是多个空格,这会更乏味(并且无法在视觉上与您的帖子区分开)。
awk -F"\t" 'NR==1{for(i=1;i<=NF;i++)s[i]=$i!="-"} {x="";for(i=1;i<=NF;i++)if(s[i])x=x FS $i;print substr(x,2)}'
# for _matches_ "-" instead of _equals_ "-" change $i!="-" to $i!~/-/
# note if a nonheader line has more fields than the header did,
# all extra fields are nonselected (as if their header field was/matched -)
# or (re)use the flags for both what to include _and_ when to terminate the line
awk -F"\t" 'NR==1{t=RS;for(i=NF;i;i--)if(s[i]=($i!="-"?t:""))t=FS} {for(i=1;i<=NF;i++)if(s[i])printf "%s%s",$i,s[i]}'
# some people may consider this too clever
答案4
我们可以使用 来完成此操作sed
,尽管代码是在扩展正则表达式模式下使用 GNU sed,但这只是作为 的治疗方法backslashitis
。
该方法是从第一行开始创建地图。要保留的字段映射为 x,其他字段映射为破折号。将此地图保存在货舱中。
然后,对于所有线路,附加此地图并在 BOL 处放置一个标记。
在循环中,如果我们看到 \n- 并且将标记前进到下一个字段,我们将继续删除当前行的前导字段。
当该标记与当前行和保留空间之间的换行符碰撞时,循环结束(由于 G 命令)。
$ sed -Ee '
1{
h
y/-/\n/
s/\S+/x/g;s/[[:blank:]]+//g
y/\n/-/
x
}
G;s/^/\n/
:a
s/\n(\S+\s*)(.*\n)x/\1\n\2/
s/\n(\S+\s*)(.*\n)-/\n\2/
/\n\n/!ba
s/\s+$//
' file
结果
M A A
- G -
M - -