对于包含类似如下条目的文件:
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解决方案也会有所帮助。我是这三个人的初学者。请解释一下你的解决方案。
答案 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变量m
,n
和el
NR % m == 0
是一个模式(条件),每第m行的计算结果为true。
{ $n = el }
是关联的操作,用变量el
替换输入行的第n个字段,导致该行重建,隐式使用输出字段分隔符OFS
,默认为空格。 1
是用于打印手头(可能已修改的)输入行的常见Awk简写。
答案 1 :(得分:0)
很棒的小运动。虽然我可能倾向于awk
解决方案,但在bash中,您还可以依靠参数扩展和子字符串替换来替换每个的 nth 字段mth 行。基本上,您可以读取每一行,保留空格,然后检查您的行数,例如如果c
是您的行计数器而m
是mth
行的变量,则可以使用:
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=1
,m=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=1
,m=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=3
,m=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
中替换字符串重复出现的问题),但两者都需要类似的字段存在验证等。从两者中学习并让我知道你是否有任何问题。