【问题标题】:Picking input record fields with AWK使用 AWK 选择输入记录字段
【发布时间】:2021-04-12 01:10:50
【问题描述】:

假设我们有一个 shell 变量$x,其中包含一个空格分隔的从 1 到 30 的数字列表:

$ x=$(for i in {1..30}; do echo -n "$i "; done)
$ echo $x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

我们可以像这样使用 AWK 打印前三个输入记录字段:

$ echo $x | awk '{print $1 " " $2 " " $3}'
1 2 3

我们如何使用 AWK 打印从第 N 个字段开始的所有字段?例如

4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

编辑:我可以使用 cut、sed 等来做同样的事情,但在这种情况下,我想知道如何使用 AWK 来做到这一点。

【问题讨论】:

  • 输出以换行符分隔。应该用空格隔开。
  • 通过刷新页面检查我更新的命令
  • 在您写的已删除答案的 cmets 中:在实际情况下,字段之间有不同数量的空格;你想保留这些不同数量的空间吗?
  • 应该是:awk '{for (i=3; i<=NF; ++i) printf "%s", $i (i<NF?OFS:ORS)}' file
  • @james-brown,我不想保留它们,但如果它使命令更短也没关系。

标签: arrays awk


【解决方案1】:

使用 GNU awk,您可以使用自 gawk 4.1 以来内置的 include 的 join function:

x=$(seq 30 | tr '\n' ' ')

echo "$x" | gawk '@include "join"   
                {split($0, arr)
                print join(arr, 4, length(arr), "|")}
                '
4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30

(为清楚起见,此处使用'|' 而不是' '...)

包含join的替代方式:

echo "$x" | gawk -i join '{split($0, arr); print join(arr, 4, length(arr), "|")}'

【讨论】:

    【解决方案2】:

    使用 gnu awk 和 gensub:

    echo $x | awk '{ print gensub(/^([[:digit:]]+[[:space:]]){3}(.*$)/,"\\2",$0)}'
    

    使用 gensub,根据正则表达式将字符串分成两部分并仅打印第二部分。

    【讨论】:

      【解决方案3】:

      版本 4:Shortest 可能是使用sub 截断前三个字段及其分隔符:

      $ echo $x | awk 'sub(/^ *([^ ]+ +){3}/,"")'
      

      输出:

      4 5 6 7 8 9 ...
      

      但是,这将保留$4 之后的所有空间:

      $ echo "1 2 3  4   5" | awk 'sub(/^ *([^ ]+ +){3}/,"")'
      4   5
      

      所以如果你想压缩空间,你需要,例如:

      $ echo "1 2 3  4   5" | awk 'sub(/^ *([^ ]+ +){3}/,"") && $1=$1'
      4 5
      

      除了如果只有 4 个字段并且第 4th 字段恰好是 0:

      $ echo "1 2 3 0" | awk 'sub(/^ *([^ ]+ +){3}/,"")&&$1=$1'
      $ [no output]
      

      在这种情况下,您需要:

      $ echo "1 2 3 0" | awk 'sub(/^ *([^ ]+ +){3}/,"") && ($1=$1) || 1'
      0
      

      版本 1:cut 更适合这项工作:

      $ cut -d\  -f 4- <<<$x
      

      版本 2:使用 awk 您可以:

      $ echo -n $x | awk -v RS=\  -v ORS=\  'NR>=4;END{printf "\n"}'
      

      版本 3:如果您想保留这些不同数量的空间,使用 GNU awk 您可以使用split 的第四个参数seps:

      $ echo "1 2 3 4  5   6    7" | 
      gawk '{
          n=split($0,a,FS,seps)                     # actual separators goes to seps
          for(i=4;i<=n;i++)                         # loop from 4th
              printf "%s%s",a[i],(i==n?RS:seps[i])  # get fields from arrays
      }'
      

      【讨论】:

      • 在实际情况下,字段之间有不同数量的空格。我将在问题中添加此注释。
      • 这改变了游戏规则。
      • 我赞成你的答案,但我选择了@anubhava 的答案,因为它更紧凑。
      • 如果您想要紧凑,cut 是最好的选择。如果你echo $x | cut($x 周围没有引号)多余的空间会被挤掉。这当然只有在分隔符是空格时才有效。 Nvm,为了 wak,你想要 awk。
      【解决方案4】:

      添加另一种方法将所有值添加到变量中,一旦完成所有字段值的读取,只需打印变量的值。根据您要从哪个字段开始获取数据,更改n= 的值。

      echo "$x" |
      awk -v n=3 '{val="";for(i=n; i<=NF; i++){val=(val?val OFS:"")$i};print val}'
      

      【讨论】:

        【解决方案5】:

        将我的评论转换为答案,以便将来的访问者轻松找到解决方案。

        你可以使用这个awk:

        awk '{for (i=3; i<=NF; ++i) printf "%s", $i (i<NF?OFS:ORS)}' file
        

        或将起始位置作为参数传递:

        awk -v n=3 '{for (i=n; i<=NF; ++i) printf "%s", $i (i<NF?OFS:ORS)}' file
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-24
          • 1970-01-01
          • 2012-04-07
          相关资源
          最近更新 更多