【问题标题】:Print every second consequtive field in two columns - awk在两列中每隔一个连续的字段打印一次 - awk
【发布时间】:2016-02-12 17:43:27
【问题描述】:

假设以下文件

#zvview.exe
#begin Present/3
 77191.0000 189.320100          0          0          3          0111110 16    1
-8.072430+6-8.072430+6      77190          0          1         37111110 16    2
         37          2                                            111110 16    3
 8.115068+6 0.000000+0 8.500000+6 6.390560-2 9.000000+6 6.803440-1111110 16    4
 9.500000+6 1.685009+0 1.000000+7 2.582780+0 1.050000+7 3.260540+0111110 16    5
         37          2                                            111110 16   18

我想做的是在两列中打印,即第 6 行之后的字段。这可以使用NR 来完成。棘手的部分如下:每隔一个字段,应该在一列中,并在符号前添加一个E,以便输出文件看起来像这样

8.115068E+6 0.000000E+0
8.500000E+6 6.390560E-2
9.000000E+6 6.803440E-1
9.500000E+6 1.685009E+0
1.000000E+7 2.582780E+0
1.050000E+7 3.260540E+0

从您看到的输出文件中,我只想在$6 中保留length($6)=10 字符。

如何在 awk 中做到这一点?

【问题讨论】:

  • 你怎么知道第6个字段在哪里结束?指数总是只有一位吗?第 7 个字段的长度是否始终相同?还有什么?
  • @CasimiretHippolyte 感谢您的评论!我知道我的指数永远只有一位。关于第 7 个字段,它很可能总是有 2 位数字。
  • 脚本如何知道不打印第 8 行的内容?因为你只想要第 6 和第 7 或者因为关于行的内容或什么?
  • @EdMorton : 我将使用NR>= 6 && NR<=whatever 选择所需的行!

标签: awk


【解决方案1】:

可以在 awk 中完成所有操作,但使用 unix 工具集可能更容易

$ sed -n '6,7p' file | cut -c2-66 | tr ' ' '\n' | pr -2ats' '

8.115068+6 0.000000+0
8.500000+6 6.390560-2
9.000000+6 6.803440-1
9.500000+6 1.685009+0
1.000000+7 2.582780+0
1.050000+7 3.260540+0

这是awk 唯一的解决方案或比较

$ awk 'NR>=6 && NR<=7{$6=substr($6,1,10);
                      for(i=1;i<=6;i+=2) {f[++c]=$i;s[c]=$(i+1)}}
                  END{for(i=1;i<=c;i++) print f[i],s[i]}' file

8.115068+6 0.000000+0
8.500000+6 6.390560-2
9.000000+6 6.803440-1
9.500000+6 1.685009+0
1.000000+7 2.582780+0
1.050000+7 3.260540+0

也许更短的版本,

$ awk 'NR>=6 && NR<=7{$6=substr($6,1,10); 
                      for(i=1;i<=6;i+=2) print $i FS $(i+1)}' file

8.115068+6 0.000000+0
8.500000+6 6.390560-2
9.000000+6 6.803440-1
9.500000+6 1.685009+0
1.000000+7 2.582780+0
1.050000+7 3.260540+0

要将格式转换为标准科学记数法,您可以将结果通过管道传输到 sed 或在 awk 脚本中嵌入类似的内容(使用 gsub)。

... | sed 's/[+-]/E&/g'

8.115068E+6 0.000000E+0
8.500000E+6 6.390560E-2
9.000000E+6 6.803440E-1
9.500000E+6 1.685009E+0
1.000000E+7 2.582780E+0
1.050000E+7 3.260540E+0

【讨论】:

  • 非常感谢您的回答。你能解释一下命令吗,因为我不熟悉sed。谢谢!
  • sed 早于awk,这里选择第 6 行和第 7 行并打印。类似于awk 'NR&gt;=6 &amp;&amp; NR&lt;=7' 声明。
  • 感谢您的帮助!如何在每个条目的登录前添加E?
【解决方案2】:

使用 GNU awk 获取 FIELDWIDTHS:

$ cat tst.awk
BEGIN { FIELDWIDTHS="9 2 9 2 9 2 9 2 9 2 9 2" }
NR>5 && NR<8 {
    for (i=1;i<NF;i+=4) {
        print $i "E" $(i+1), $(i+2) "E" $(i+3)
    }
}

$ awk -f tst.awk file
 8.115068E+6  0.000000E+0
 8.500000E+6  6.390560E-2
 9.000000E+6  6.803440E-1
 9.500000E+6  1.685009E+0
 1.000000E+7  2.582780E+0
 1.050000E+7  3.260540E+0

如果您真的想摆脱前导空白,那么有多种方法可以做到这一点(最简单的是相关字段上的gsub(/ /,"",$&lt;field number&gt;))但我将它们留在了因为上面允许您的输出正确排列如果/何时您的数字以 - 开头,就像您在示例输入的第 4 行中所做的那样。

如果您没有 GNU awk,请获取它,因为您缺少很多非常有用的功能。

【讨论】:

    【解决方案3】:

    我尝试使用substr 结合@karafka 的答案,所以下面的方法可以解决问题!

    awk 'NR>=6 && NR<=7{$6=substr($6,1,10);for(i=1;i<=6;i+=2) print substr($i,1,8) "E" substr($i,9) FS substr($(i+1),1,8) "E" substr($(i+1),9)}' file
    

    输出是

    8.115068E+6 0.000000E+0
    8.500000E+6 6.390560E-2
    9.000000E+6 6.803440E-1
    9.500000E+6 1.685009E+0
    1.000000E+7 2.582780E+0
    1.050000E+7 3.260540E+0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-28
      • 2020-12-06
      • 1970-01-01
      • 1970-01-01
      • 2015-02-11
      相关资源
      最近更新 更多