【问题标题】:awk: Print $1 with varying number of additional fields on the same lineawk:在同一行打印带有不同数量的附加字段的 $1
【发布时间】:2019-08-09 17:11:36
【问题描述】:

我有一个输入文件,每行有 ~100 行和 ~100 个字段。每个字段代表一个正值或负值。我希望打印 $1 后跟每行中的正 负字段。每行的正负场数是随机的。

样本输入

0 x 9 8 7 -1 -2 -3
2 x 7 6 -2 -3 -4 -5
4 x 4 3 2 1 -6 -7

想要的输出

积极的

0 9 8 7
2 7 6
4 4 3 2 1

否定

0 -1 -2 -3
2 -2 -3 -4 -5
4 -6 -7

上下文和尝试

上面的输出打印 $1,后跟与 $1 在同一行的剩余字段中的正值或负值。

我尝试的当前代码(对于正值,从我输入的第 6 行开始):

awk 'NR>5{for(i=3; i<=NF; i++) if ( $i > 0 ) print $1, $i}' input > output

这工作正常,除了我打印输出如下:

 0 9
 0 8
 0 7
 2 7
 2 6
 4 4
 4 3
 4 2
 4 1

我也试过了:

awk 'BEGIN {ORS="\t"} NR>5 {print $1} {for(i=3;i<=NF;i++) if ( $i > 0 && i <= NF} {print $i}}' input > output

但是我从不移动到输出中的新行。如果我通过一些 'else if (i = NF) {ORS=...}' 条件将 ORS 改回 \n,那么它将在新行上打印每个 i 的所有字段输出,就像 BEGIN 语句没有效果一样。

问题

我如何告诉 awk 打印 $1,然后将同一输入行的所有其他输出打印到同一输出行,然后在输出中前进 1 个新行并为下一个输入行重复该过程?

谢谢。

回应 Tiw 的回答

我尝试为我的两个文件循环执行此操作:

for j in 1 2; do
positive=ofile.p0
negative=ofile.m0

awk 'NR>5{
    printf $1>"positive";
    printf $1>"negative";
    for(i=3;i<=NF;i++)
       if($i~/[-+]?[0-9]+/)
           if ($i>0) printf OFS $i>"positive";
           else if($i<0) printf OFS $i>"negative";
    print "">"positive";
    print "">"negative";
}'ofile.0$j

mv positive $positive$j
mv negative $negative$j

done

但它挂起。编辑:Tiw 的答案用 printf 中的 %s 更新。它适用于此更改。

【问题讨论】:

  • 输出到不同的文件。
  • @Blaisem,不断地+值和-值都来了,但是为什么有些行打印为+而有些为-?你能解释一下吗?
  • @RavinderSingh13 我的目标是将输入分为正值和负值。我想要一个包含原始输入的输出,但只有具有正值的字段。第二个输出还包含原始输入,但仅包含具有负值的字段。我计划使用 awk 代码来分隔正值,并使用第二个 awk 代码来分隔负值。这有帮助吗?谢谢。
  • @Blaisem.. Perl 解决方案适合你吗?..
  • 0 应该被认为是正面的还是负面的,或者根本不打印?

标签: awk


【解决方案1】:

试试这个:

awk 'NF>5{printf "%s",$1>"positive";printf "%s",$1>"negative"; for(i=2;i<=NF;i++) if($i~/^[-+]?[0-9]+$/) if ($i>0) printf "%s",OFS $i>"positive"; else if($i<0) printf "%s",OFS $i>"negative"; print "">"positive";print "">"negative";}' input

使用名为input的文件:

0 x 9 8 7 -1 -2 -3
2 x 7 6 -2 -3 -4 -5
4 x 4 3 2 1 -6 -7

它将创建两个文件,
一位positive

0 9 8 7
2 7 6
4 4 3 2 1

一个negative:

0 -1 -2 -3
2 -2 -3 -4 -5
4 -6 -7

插入多行以提高可读性:

awk 'NF>5{
    printf "%s",$1>"positive";
    printf "%s",$1>"negative"; 
    for(i=2;i<=NF;i++) 
        if($i~/^[-+]?[0-9]+$/)     ## Another and better way is $i == $i + 0 
            if ($i>0) printf "%s",OFS $i>"positive"; 
            else if($i<0) printf "%s",OFS $i>"negative"; 
    print "">"positive";
    print "">"negative";
}' input

这很简单,所以我想你很容易理解。
注意我没有使用{} 来引用forifs 之后的块,因为它们后面只有一个命令,所以可以保存引号。
print 将打印一个换行符字符 \n 最后,printf 不会。
还有NR表示NRecords,即行号,我改成NF,表示N个F领域,我想这就是你想要的。

if($i~/^[-+]?[0-9]+$/)是测试字段是数字。
如果该字段不为空,则$i==$+0 是更好的方法。
并结合测试字段不是0或者为空,使用$i &amp;&amp; ($i==$i+0)

【讨论】:

  • 谢谢!我尝试以几种不同的方式实现它,但代码挂起。我用 bash -xv 运行它,但没有看到错误的原因。还是想看看。为了回复您,我将脚本编辑为原始问题。
  • $i~/[-+]?[0-9]+/ 将匹配 Joe90 和其他非数字。给定非空$i 的数字的测试是$i == $i+0。对于打印,而不是 2 个非常相似的行,只考虑一个:printf "%s",OFS $i&gt;($i&gt;=0 ? "positive" : "negative")(假设 OP 希望 0 被认为是积极的)
  • 对,但由于您不想打印零,请将其更改为 ($i != 0) &amp;&amp; ($i == $i+0)。不,print ($i&gt;=0 ? "positive" : "negative") 不会将正数打印到负输出。请参阅https://en.wikipedia.org/wiki/%3F: 了解三元表达式的工作原理。
  • @EdMorton 忘记添加 ^$,谢谢提醒 :)
  • @Blaisem 更新了答案,将^$ 添加到正则表达式中,我的疏忽,对此感到抱歉。而那部分也可以换成if ($i &amp;&amp; ($i==$i+0))
【解决方案2】:

如果 Perl 是一个选项,

输入:

$ cat blaisem.txt
0 x 9 8 7 -1 -2 -3
2 x 7 6 -2 -3 -4 -5
4 x 4 3 2 1 -6 -7

$

+ve 和 -ve 单独运行

$ perl -ne ' @p=/(\S+)(?<=\d)/g;print "$p[0] "; for(@p[1..$#p]) { print "$_ " if $_ >=0 } print "\n" ' blaisem.txt
0 9 8 7
2 7 6
4 4 3 2 1

$ perl -ne ' @p=/(\S+)(?<=\d)/g;print "$p[0] "; for(@p[1..$#p]) { print "$_ " if $_ < 0 } print "\n" ' blaisem.txt
0 -1 -2 -3
2 -2 -3 -4 -5
4 -6 -7

$

+ve 和 -ve 在一个脚本中

$  perl -ne ' open(POS,">>pos.txt"); open(NEG,">>neg.txt"); @p=/(\S+)(?<=\d)/g; 
          print POS "$p[0] "; print NEG "$p[0] "; 
           for(@p[1..$#p]) { print NEG "$_ " if $_ < 0; print POS "$_ " if $_>=0  } 
             print POS "\n"; print NEG "\n" ' blaisem.txt

$ cat pos.txt
0 9 8 7
2 7 6
4 4 3 2 1

$ cat neg.txt
0 -1 -2 -3
2 -2 -3 -4 -5
4 -6 -7

$

【讨论】:

    【解决方案3】:

    你需要做的第一件事是检查该字段是否为数字,如果是这种情况,你可以进行检查。在awk中,你可以通过给它加零来检查一个变量是否是一个数字,并检查它是否返回相同的值。

    对于正数,您可以这样做:

    awk '{for(i=1;i<=NF;++i) if ($i+0 == $i && $i >= 0) printf $i OFS; printf ORS}' file
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-14
      • 2013-03-13
      • 1970-01-01
      相关资源
      最近更新 更多