【问题标题】:Replace the nth field of every mth line using awk or bash使用 awk 或 bash 替换每 m 行的第 n 个字段
【发布时间】:2017-11-22 07:19:36
【问题描述】:

对于包含类似以下条目的文件:

foo  1  6  0
fam  5  11  3
wam  7  23  8
woo  2  8  4
kaz  6  4  9
faz  5  8  8

如何使用 bash 或 awk 将 行的第 n 个字段替换为相同的元素?

例如,如果 n = 1m = 3 并且元素 = wot,则输出将是:

foo  1  6  0
fam  5  11  3
wot  7  23  8
woo  2  8  4
kaz  6  4  9
wot  5  8  8

我知道您可以使用例如调用/打印每 m 行

awk 'NR%7==0' file

到目前为止,我已尝试将其保留在内存中,但无济于事...我还需要保留文件的其余部分。

我更喜欢使用 bash 或 awk 的答案,但 sed 解决方案也会有所帮助。我是这三个方面的初学者。请解释您的解决方案。

【问题讨论】:

    标签: bash awk


    【解决方案1】:
    awk -v m=3 -v n=1 -v el='wot' 'NR % m == 0 { $n = el } 1' file
    

    但是,请注意,不能保证字段间的空白保持原样,因为awk 将一行通过任何运行的空白分割成字段;正如所写,修改后的行的输出字段将由单个空格分隔。
    但是,如果您的输入字段始终2 个空格 分隔,您可以通过将 -F' ' -v OFS=' ' 添加到 awk 调用来有效地保留输入空格。

    • -v m=3 -v n=1 -v el='wot' 定义 Awk 变量 mnel

    • NR % m == 0 是一个模式(条件),每第 m 行计算为真。

      • { $n = el } 是关联的action,它用变量el 替换输入行的第n 个字段,导致该行被重建,隐式使用OFS,即输出字段分隔符,默认为空格。
    • 1 是常用的 Awk 简写,用于打印手头的(可能已修改的)输入行。

    【讨论】:

      【解决方案2】:

      很棒的小练习。虽然我可能倾向于awk 解决方案,但在 bash 中,您还可以依靠 使用子字符串替换的参数扩展 来替换每个 mthnth 字段/em> 行。本质上,您可以读取每一行,保留空格,然后检查您的行数,例如如果c 是您的行计数器,m 是您的变量mth 行,您可以使用:

          if (( $((c % m )) == 0))    ## test for mth line
      

      如果该行是替换行,您可以在恢复默认分词后将每个单词读入一个数组,然后使用您的数组元素索引n-1 来提供替换(例如${line/find/replace}${line/"${array[$((n-1))]}"/replace})。

      如果它不是替换行,只需输出该行不变。一个简短的示例可能类似于以下内容(您可以根据需要添加其他验证)

      #!/bin/bash
      
      [ -n "$1" -a -r "$1" ] || {    ## filename given an readable
          printf "error: insufficient or unreadable input.\n"
          exit 1
      }
      
      n=${2:-1}       ## variables with default n=1, m=3, e=wot
      m=${3:-3}
      e=${4:-wot}
      c=1             ## line count
      
      while IFS= read -r line; do    
          if (( $((c % m )) == 0))    ## test for mth line
          then
              IFS=$' \t\n'
              a=( $line )             ## split into array
              IFS=
              echo "${line/"${a[$((n-1))]}"/$e}"  ## nth replaced with e
          else
              echo "$line"   ## otherwise just output line
          fi
          ((c++))         ## advance counter
      done <"$1"
      

      使用/输出示例

      n=1m=3e=wot

      $ bash replmn.sh dat/repl.txt
      foo  1  6  0
      fam  5  11  3
      wot  7  23  8
      woo  2  8  4
      kaz  6  4  9
      wot  5  8  8
      

      n=1m=2e=baz

      $ bash replmn.sh dat/repl.txt 1 2 baz
      foo  1  6  0
      baz  5  11  3
      wam  7  23  8
      baz  2  8  4
      kaz  6  4  9
      baz  5  8  8
      

      n=3m=2e=99

      $ bash replmn.sh dat/repl.txt 3 2 99
      foo  1  6  0
      fam  5  99  3
      wam  7  23  8
      woo  2  99  4
      kaz  6  4  9
      faz  5  99  8
      

      awk 解决方案更短(并且避免了在$line 中重复出现替换字符串的问题),但两者都需要类似的字段存在验证等。从两者中学习并告诉我你是否有任何问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-04
        • 2012-04-25
        • 2015-09-13
        • 2020-08-16
        • 2021-10-16
        • 2016-12-02
        • 2014-06-24
        • 2023-02-11
        相关资源
        最近更新 更多