我有两个文本文件:file1
和file2
,都有几行。
$ cat file1
line one
line two
line three
line four
line five
$ cat file2
line A
line B
line C
line D
line E
line F
我想替换一个范围的行file1
(从 line1_start
到 line 1_end
),其中范围的行file2
(从 line2_start
到 line 2_end
)。
例如,将2,4
in 中的行替换file1
为3,5
from 中的行file2
。
到目前为止我唯一能做的就是从file2
with中提取所需的行
$ sed -n 3,5p file2
但这对把它们放进去没有帮助file1
。有可能吗sed
?如果没有,是否可以使用类似的工具?
答案1
sed
可以打印给定范围的行,如下所示:
sed -n 'X,Yp' filename
其中X
是范围中的第一行,Y
是最后一行,包括两者。-n
告诉sed
不要打印任何内容,除非明确告知这样做,这就是p
下面的范围的作用。
因此,您可以轻松地调用此命令三次,附加到临时文件,然后将该文件移动到您想要的任何位置。您还可以使用将它们全部组合起来cat
和将它们全部组合起来流程替代如本示例所示(我使用的是我刚刚凭空提取的行号;$
是文件中的最后一行):
cat <(sed -n '1,5p' file1) <(sed -n '10,12p' file2) <(sed -n '9,$p' file1) > file1.tmp && mv file1.tmp file1
在这里,我们将 中的第 6、7 和 8 行替换file1
为 中的第 10、11 和 12 行file2
。
更新:感谢@MiniMax指出cat
通过执行以下操作可以避免这种情况和流程替换:
{ sed -n '1,5p' file1; sed -n '10,12p' file2; sed -n '9,$p' file1; } > file1.tmp && mv file1.tmp file1
毕竟,亲吻。 :)
答案2
另一种方法sed
是使用r
命令,如果-i
还必须使用就地选项,则很方便
$ sed -n '3,5p; 5q;' f2 | sed -e '2r /dev/stdin' -e '2,4d' f1
line one
line C
line D
line E
line five
$ # if /dev/stdin is not supported
$ sed -n '3,5p; 5q;' f2 > t1
$ sed -e '2r t1' -e '2,4d' f1
感谢 don_crissti 的提醒,一旦从文件 2 中获得所需的行,我们就可以退出。
答案3
对于巨大的输入文件,这可能会更快:
# replacing lines m1,m2 from file1 with lines n1,n2 from file2
m1=2; m2=4; n1=3; n2=5
{ head -n $((m1-1)); { head -n $((n1-1)) >/dev/null; head -n $((n2-n1+1));
} <file2; head -n $((m2-m1+1)) >/dev/null; cat; } <file1
它是在这里解释,唯一的区别是该特定情况下的单行范围。
答案4
我最近开始用 Python 做所有事情,所以这里有一个 Python 程序可以完成你想要的事情:
#!/usr/bin/env python2
# -*- coding: ascii -*-
"""replace_range.py"""
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
"matchfile",
help="File in which to replace lines",
)
parser.add_argument(
"matchrange",
help="Comma-separated range of Lines to match and replace",
)
parser.add_argument(
"replacementfile",
help="File from which to get replacement lines"
)
parser.add_argument(
"replacementrange",
help="Comma-separated range of lines from which to get replacement"
)
if __name__=="__main__":
# Parse the command-line arguments
args = parser.parse_args()
# Open the files
with \
open(args.matchfile, 'r') as matchfile, \
open(args.replacementfile, 'r') as replacementfile:
# Get the input from the match file as a list of strings
matchlines = matchfile.readlines()
# Get the match range (NOTE: shitf by -1 to convert to zero-indexed list)
mstart = int(args.matchrange.strip().split(',')[0]) - 1
mend = int(args.matchrange.strip().split(',')[1]) - 1
# Get the input from the replacement file as a list of strings
replacementlines = replacementfile.readlines()
# Get the replacement range (NOTE: shitf by -1 to convert to zero-indexed list)
rstart = int(args.replacementrange.strip().split(',')[0]) -1
rend = int(args.replacementrange.strip().split(',')[1]) - 1
# Replace the match text with the replacement text
outputlines = matchlines[0:mstart] + replacementlines[rstart:rend+1] + matchlines[mend+1:]
# Output the result
sys.stdout.write(''.join(outputlines))
这是它实际的样子:
user@host:~$ python replace_range.py file1 2,3 file2 2,4
line one
line B
line C
line D
line four
line five