awk:在同一行上打印$ 1,并显示不同数量的其他字段

时间:2019-03-19 08:33:42

标签: awk

我有一个输入文件,每行有〜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。可以进行此更改。

3 个答案:

答案 0 :(得分:2)

尝试一下:

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

这很简单,所以我想您很容易理解。
请注意,在{}for之后,我没有使用if来引用块,因为它们之后都只有一个命令,因此可以保存引号。
print将在末尾打印换行符\n,而printf则不会。
同样,NR表示 R 个索引的 N 个,即行号,我改为NF,表示 N F 的孩子,我想这就是您想要的。

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

答案 1 :(得分:2)

您需要做的第一件事是检查字段是否为数字,如果是这种情况,则可以进行检查。在<?xml version="1.0" encoding="UTF-8"?> <Response xmlns="http://tempuri.org/Response.xsd"> <ResponseStatusDescription /> <EntityPaymentReceiptNumber /> <Description>Test</Description> <OperationName>CheckPayment</OperationName> <BankID>39</BankID> <EntityPaymentDate /> <CheckPaymentID>188721103486</CheckPaymentID> <ResponseStatusCode>INFO2</ResponseStatusCode> </Response> 中,可以通过将变量加零来检查变量是否为数字,并检查变量是否返回相同的值。

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

awk

答案 2 :(得分:1)

如果选择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

$