【问题标题】:how to print 3rd field in 3rd column itself如何在第三列本身打印第三个字段
【发布时间】:2015-06-03 20:05:26
【问题描述】:

在我的文件中,我有 3 个字段,我只想打印第三列中的第三个字段,但输出到第一行。请检查我的文件和输出:

cat 文件名

1st field     2nd field    3rd field
---------     ---------    -----------
a,b,c,d       d,e,f,g,h    1,2,3,4,5,5

q,w,e,r       t,y,g,t,i    9,8,7,6,5,5

我正在使用以下命令仅在第三列中打印第三个字段

cat filename |awk '{print $3}' |tr ',' '\n' 

输出在第一个字段位置打印第三个字段字符串,我希望它只在第三个字段区域打印

first field :-
---------------
1
2
3
4
5 
5

预期输出

1st field     2nd field    3rd field
---------     ---------    -----------
a,b,c,d       d,e,f,g,h     1
                            2
                            3
                            4
                            5 
                            5

q,w,e,r       t,y,g,t,i     9
                            8
                            7
                            6 
                            5
                            5

【问题讨论】:

  • 文件中是像1st field 这样的字段名称还是您只是为了更好地理解而包含?
  • 添加以便更好地理解
  • 字段的宽度是否始终相同?
  • 没有一些行在意义不一样的意义上有更多的值..

标签: linux awk


【解决方案1】:

输入

 [akshay@localhost tmp]$ cat file
 1st field     2nd field    3rd field
 ---------     ---------    -----------
 a,b,c,d       d,e,f,g,h    1,2,3,4,5,5

 q,w,e,r       t,y,g,t,i    9,8,7,6,5,5

脚本

 [akshay@localhost tmp]$ cat test.awk
    NR<3 || !NF{ print; next}
    { 
        split($0,D,/[^[:space:]]*/)
        c1=sprintf("%*s",length($1),"")
        c2=sprintf("%*s",length($2),"")
        split($3,A,/,/)
        for(i=1; i in A; i++)
        {   
            if(i==2)
            {
                $1 = c1
                $2 = c2
            }
            printf("%s%s%s%s%d\n",$1,D[2],$2,D[3],A[i]) 
        }
     }

输出

 [akshay@localhost tmp]$ awk -f test.awk file
 1st field     2nd field    3rd field
 ---------     ---------    -----------
 a,b,c,d       d,e,f,g,h    1
                            2
                            3
                            4
                            5
                            5

 q,w,e,r       t,y,g,t,i    9
                            8
                            7
                            6
                            5
                            5

说明

  • NR&lt;3 || !NF{ print; next}

NR 为您提供正在处理的记录总数或行号,简而言之,NR 变量具有行号。

NF 为您提供记录中的字段总数。

next 语句强制awk 立即停止处理 当前记录并继续下一条记录。

如果行号小于 3 或不是 NF(表示记录中没有字段为空行),则打印当前记录并转到下一条记录。

  • split($0,D,/[^[:space:]]*/)

由于我们有兴趣保留格式,因此我们在此处保存数组 D 上的字段之间的分隔符,如果您有 GNU awk,您可以将第 4 个参数用于 split() -它允许您将行拆分为 2 个数组,一个字段和另一个字段之间的分隔符,然后您可以对字段数组进行操作并使用每个字段数组元素之间的分隔符数组进行打印以重建原始 @987654329 @。

  • c1=sprintf("%*s",length($1),"")c2=sprintf("%*s",length($2),"")

这里sprintf函数用于填充字段($1 or $2)长度的空格字符。

  • split($3,A,/,/)

split(string, array [, fieldsep [, seps ] ])

将字符串分成由fieldsep分隔的片段并存储片段 in 数组和 seps 数组中的分隔符字符串。第一部分 存储在数组 [1] 中,第二部分存储在数组 [2] 中,依此类推。这 第三个参数的字符串值,fieldsep,是一个正则表达式,描述 在哪里拆分字符串(就像 FS 可以是描述在哪里的正则表达式 拆分输入记录)。如果省略 fieldsep,则使用 FS 的值。 split() 返回创建的元素数。

循环直到i in A 为真,我才知道i=1i++ 控制数组的遍历顺序,感谢Ed Morton

  • if(i==2) { $1 = c1 $2 = c2 }

i = 1 我们打印a,b,c,dd,e,f,g,h 时,在下一次迭代中,我们将$1$2 值修改为我们在上面创建的c1c2,因为您有兴趣只显示一次作为请求。

  • printf("%s%s%s%s%d\n",$1,D[2],$2,D[3],A[i])

最后打印field1($1),field1和field2之间的分隔符到我们上面保存的,即D[2],field2($2),field2和field3之间的分隔符和数组A元素之间只有一个我们从 (split($3,A,/,/)) 创建。

【讨论】:

  • Super Akshay,你给的脚本就是我的输出。非常感谢并请您解释一下脚本我必须更改原始文件中的一些修改
  • 不错的@AkshayHegde
  • 要创建一个length($1) 空白字符串,您不需要像c1=sprintf(sprintf("%%%ds",length($1))," ") 那样调用sprintf 两次,因为只需c1=sprintf("%*s",length($1),"") 就可以了。每次循环调用split($3,A,/,/)效率很低。
【解决方案2】:
$ cat tst.awk
NR<3 || !NF { print; next }
{
    front = gensub(/((\S+\s+){2}).*/,"\\1","")
    split($3,a,/,/)
    for (i=1;i in a;i++) {
        print front a[i]
        gsub(/\S/," ",front)
    }
}

$ awk -f tst.awk file
1st field     2nd field    3rd field
---------     ---------    -----------
a,b,c,d       d,e,f,g,h    1
                           2
                           3
                           4
                           5
                           5

q,w,e,r       t,y,g,t,i    9
                           8
                           7
                           6
                           5
                           5

上面的 gensub() 使用 GNU awk,其他 awk 使用 match()+substr()。它还使用\S\s 简写为[^[:space:]][[:space:]]

【讨论】:

  • 注意可以从n=split()获取字段数,然后从1循环到n
  • 正确,但如果不需要,我通常不会费心引入另一个全局变量。
  • 但是不会影响订单吗?
  • 不,在那种情况下,i in a 只是一个索引是否存在于数组中的测试。它周围的i=1i++ 控制着数组的遍历顺序。只有当您编写 for (i in a) 而不是 for (i=1;i in a;i++) 时,in 运算符本身才控制数组的遍历顺序。
  • @Ed Morton : 正如我常说的,你是最棒的,喜欢向传奇人物学习。
【解决方案3】:

考虑到列是制表符分隔的,我想说:

awk 'BEGIN{FS=OFS="\t"}
     NR<=2 || !NF {print; next}
     NR>2{n=split($3,a,",")
          for (i=1;i<=n; i++)
              print (i==1?$1 OFS $2:"" OFS ""), a[i]
         }' file
  • 这会正常打印第一行、第二行和空行
  • 然后,使用逗号作为分隔符对第三个字段进行切片。
  • 最后,循环遍历每次打印的张数;它第一次打印前两列,然后只打印最后一个值。

测试

$ awk 'BEGIN{FS=OFS="\t"} NR<=2 || !NF {print; next} NR>2{n=split($3,a,","); for (i=1;i<=n; i++) print (i==1?$1 OFS $2:"" OFS ""), a[i]}' a
1st field   2nd field   3rd field
---------   ---------   -----------
a,b,c,d d,e,f,g,h   1
        2
        3
        4
        5
        5

q,w,e,r t,y,g,t,i   9
        8
        7
        6
        5
        5

注意输出有点难看,因为分隔列的制表符会像这样引导它们。

【讨论】:

  • 否决票的任何理由?如果有问题,我将不胜感激
  • 对不起,输出没有达到预期,混合所有领域,请您提供更好的解决方案,我没有投反对票
  • @DasD 它是制表符分隔的,无能为力。您也许可以通过管道清除它,但这是您应该做的事情;这里我只是展示应用的逻辑,这是重要的部分。
  • 第一个字段 第二个字段 第三个字段 --------- --------- ----------- 输出只获取这个标题,请请检查一次命令
  • @DasD 回答。在它的顶部我说“考虑到列是制表符分隔的”。
猜你喜欢
  • 2010-12-08
  • 2022-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-18
相关资源
最近更新 更多