使用awk或bash替换每个第m行的第n个字段

时间:2017-06-19 23:13:54

标签: bash awk

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

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用相同的元素替换每个第m行的第n个字段?

例如,如果 n = 1 m = 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解决方案也会有所帮助。我是这三个人的初学者。请解释一下你的解决方案。

2 个答案:

答案 0 :(得分:2)

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行的计算结果为true。

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

答案 1 :(得分:0)

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

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

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

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

${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=3

e=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=2

e=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=2

e=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中替换字符串重复出现的问题),但两者都需要类似的字段存在验证等。从两者中学习并让我知道你是否有任何问题。