用行范围替换行范围(sed 或其他)

用行范围替换行范围(sed 或其他)

我有两个文本文件:file1file2,都有几行。

$ 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,4in 中的行替换file13,5from 中的行file2

到目前为止我唯一能做的就是从file2with中提取所需的行

$ 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

相关内容