在awk中打印除选择字段外的所有字段

19
我有一个包含数百列的大文件,我想从中仅删除第三和第四列,并将其余部分输出到另一个文件。我的最初想法是创建一个像这样的 awk 脚本:awk '{print $1, $2, for (i=$5; i <= NF; i++) print $i }' file > outfile。但是,这段代码不起作用。
然后我尝试了:
awk '{for(i = 1; i<=NF; i++)
if(i == 3 || i == 4) continue
else
print($i)}' file > outfile

但这只是把所有内容都打印出来放在一个字段中。虽然可以将其拆分为两个脚本并使用Unix的paste组合它们,但这似乎应该能够在一行命令中完成。


1
如果您感兴趣,我在这里有一堆awk列过滤函数 https://github.com/mhitza/inflated-shell/blob/master/src/filters/input/column - mhitza
6个回答

20

你的第一次尝试非常接近。将其修改为使用printf并包括字段分隔符对我有用:

awk '{printf $1FS$2; for (i=5; i <= NF; i++) printf FS$i; print NL }'

4
NL并不是awk的任何标准功能(甚至在我可以访问的4个实现中都不是非标准功能)。它只是一个常规变量,这里未初始化。print NL最终会打印一个换行符,因为它被解释为print ""print sjskjsdsj也会产生相同的结果。 - dubiousjim

15

如果是这样的话,可以考虑类似如下的方法:

cat SOURCEFILE | cut -f1-2,5- >> DESTFILE

它打印第一列和第二列,跳过第三列和第四列,然后从第五列开始打印到末尾。


1
请翻译以下与编程有关的内容,从英文到中文。仅返回翻译文本:不需要用cat管道输出。请见我的回答:https://dev59.com/yWw15IYBdhLWcg3wtNuO#6458705 - matchew
这就是正解,谢谢!我的意思是,虽然问题要求使用awk,但cut是完成这个任务的绝佳工具。 - Thismatters
如果你手头有一个带有特定列标记的文件,那么这个答案是完全正确的。但是,如果你手头的是一个命令的输出结果,它具有可见的列但没有列标记(即它是空格对齐/填充的文本,对人类来说看起来整齐而且表格化,但不是csv等),那么这个答案就完全错误了,因为它只适用于内容长度相同且“列”落在正确位置的行。cut -c也可以工作,但你需要计算字符数。awk可以与空格对齐的文件完美配合。 - IBBoard

10

把第三列和第四列设置为空字符串,如何呢:

echo 1 2 3 4 5 6 7 8 9 10 |
awk -F" " '{ $3="";  $4=""; print}'

5
但你仍然需要使用分隔符:echo 1:2:3:4:5:6:7:8:9:10 | awk -F: 'BEGIN{OFS=FS} { $3=""; $4=""; print}' - glenn jackman
如果您想保留列数但只是想忽略其中的数据(例如,使CSV文件变小),则此方法非常有用。 - Bjinse

7
假设您有一个以制表符分隔的文件,看起来像下面这样:
temp.txt
field1 field2 field3 field4 field5 field6 field1 field2 field3 field4 field5 field6 field1 field2 field3 field4 field5 field6
运行以下命令将删除字段3和4并输出到行末。
awk '{print $1"\t"$2"\t"substr($0, index($0,$5))}' temp.txt
输出如下:
field1 field2 field5 field6 field1 field2 field5 field6 field1 field2 field5 field6
本示例将打印到stdout。"> newFile"将stdout发送到newFile,">> newFile"将附加到newFile。
因此,您可能需要使用以下命令:
awk '{print $1"\t"$2"\t"substr($0, index($0,$5))}' temp.txt > newFile.txt
有人会主张使用cut
cut -f1,2,5- temp.txt
它产生相同的输出,并且对于简单性而言,cut非常好,但是无法处理不一致的定界符。例如混合使用不同的空格。但是,在这种情况下,cut可能是您想要的。
您还可以在perl、python、ruby和许多其他语言中完成此操作,但是这里是最简单的awk解决方案。

awk 工作正常,直到第 5 个字段的内容在前 4 个字段中的一个中出现。在这种情况下,您会过早地截断。 - NeronLeVelu

4

是的,只需将第三列和第四列设为空字符串即可;但是,此外,需要将字段$1设置为它本身 ($1=$1),以使awk实际上在一次操作中处理整个当前行$0的输入字段分隔符(定界符):

echo 1:2:3:4:5:6:7:8:9:10 | awk -F: '{ $1=$1; $3=""; $4=""; print $0}'

只要您不介意输出中有多个连续的分隔符,这种方法就可以工作。在某些情况下可能没问题,但在其他情况下可能不太合适。 - IBBoard

0

这是一种困难但通用的方式(对于一个简单的一行代码来说,可以忘记它)

awk -v "Exclude=3:4:5" '
   # load exclusion
   BEGIN{
      Count=split(Exclude, aTmp, ":")
      for( i = 1; i <= Count; i++) aExc[ aTmp[ i]]=1
      }

   # treat each line, taking only wanted field
   {
    Result=""
    for( i = 1; i <= NF; i++) {
       # field to take ?
       if( ! aExc[ i]) {
         # first element or add a separator before
         if( Result != "") Result=Result OFS $i
          else Result=$i
         }
       }

    print Result
   }' YourFile
  • 您可以指定要排除的任何字段
    • 在第一行中使用变量 Exclude 分隔符:填充字段索引
  • 分隔符正确放置和数量正确
  • 代码已“扩展”以更好地理解
  • 最终结果与输入不完全相同(无排除字段),因为使用输出分隔符而不是原始分隔符(例如,2个空格或制表符将改为默认行为下的1个空格)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接