如何在模式(标记)之前将文件的内容插入到另一个文件中?

use*_*191 48 sed awk perl text-processing

File1 内容:

line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 
Run Code Online (Sandbox Code Playgroud)

File2 内容:

line1-file2     "25"  
line2-file2     "24"  
Pointer-file2   "23"  
line4-file2     "22" 
line5-file2     "21"
Run Code Online (Sandbox Code Playgroud)

perl/shell脚本执行后,File2内容应该变成:

line1-file2     "25"  
line2-file2     "24" 
line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 
Pointer-file2   "23" 
line4-file2     "22" 
line5-file2     "21"
Run Code Online (Sandbox Code Playgroud)

File1File2包含“指针”的行之前粘贴in的内容。

jfg*_*956 55

sed 有一个功能,可以就地进行修改:

sed -i -e '/Pointer/r file1' file2
Run Code Online (Sandbox Code Playgroud)

但这会将您的指针行置于 file1 之上。把它放在下面,延迟线输出:

sed -n -i -e '/Pointer/r file1' -e 1x -e '2,${x;p}' -e '${x;p}' file2 
Run Code Online (Sandbox Code Playgroud)
使用`GNU sed`
sed '/Pointer/e cat file1' file2
Run Code Online (Sandbox Code Playgroud)

按照说明书e [command]

请注意,与 r 命令不同,该命令的输出将立即打印;r 命令将输出延迟到当前周期结束。

  • 你能解释一下 `-e 1x -e '2,${x;p}' -e '${x;p}'` 是做什么的吗?我知道您在模式缓冲区中交换了内容,然后将其打印出来,但我不知道您在开头添加了安静选项 `-n` 的原因和原因。 (11认同)

jfg*_*956 22

不使用sedawk

首先,找到您的模式所在的行:

line=$(grep -n 'Pointer' file2 | cut -d ":" -f 1)
Run Code Online (Sandbox Code Playgroud)

然后,使用 3 个命令输出想要的结果:

{ head -n $(($line-1)) file2; cat file1; tail -n +$line file2; } > new_file
Run Code Online (Sandbox Code Playgroud)

这有访问3次的缺点file2,但可能比一个更清晰sedawk解决方案。


Kev*_*vin 13

awk使这相当容易。
在文件前插入一行:

awk '/Pointer/{while(getline line<"innerfile"){print line}} //' outerfile >tmp
mv tmp outerfile
Run Code Online (Sandbox Code Playgroud)

要使内部文件打印Pointer在行后,只需切换模式的顺序(您需要添加分号以获得默认操作),您可以删除line变量:

awk '//; /Pointer/{while(getline<"innerfile"){print}}' outerfile >tmp
mv tmp outerfile
Run Code Online (Sandbox Code Playgroud)

只是因为还没有人用过perl

# insert file before line
perl -e 'while(<>){if($_=~/Pointer/){system("cat innerfile")};print}' outerfile

# after line
perl -e 'while(<>){print;if($_=~/Pointer/){system("cat innerfile")}}' outerfile
Run Code Online (Sandbox Code Playgroud)


don*_*sti 9

一个简单的工作ed

ed -s file1 <<IN
/Pointer/-r file2
,p
q
IN
Run Code Online (Sandbox Code Playgroud)

-r file1读入指定文件到寻址行之后,在本例中是匹配 的第一行之前的行Pointer。因此,file2即使Pointer出现在多行上,这也只会插入一次内容。如果要在每个匹配行之前插入它,请添加global 标志:

ed -s file1 <<IN
g/Pointer/-r file2
,p
q
IN
Run Code Online (Sandbox Code Playgroud)

更换,pw,如果你要编辑就地文件。


接受的sed答案在大多数情况下确实有效,但如果标记在最后一行,则该命令将无法按预期工作:它将File1在标记之后插入 的内容。
我最初尝试过:

sed '/Pointer/{r file1
N}' file2
Run Code Online (Sandbox Code Playgroud)

这也可以正常工作(r在循环结束时会发挥其魔力)但如果标记位于最后一行(最后N一行之后没有ext 行),则会出现相同的问题。要解决此问题,您可以在输入中添加换行符:

sed '/Pointer/{              # like the first one, but this time even if the
r file1                      # marker is on the last line in File2 it
N                            # will be on the second to last line in
}                            # the combined input so N will always work;
${                           # on the last line of input: if the line is
/^$/!{                       # not empty, it means the marker was on the last
s/\n$//                      # line in File2 so the final empty line in the
}                            # input was pulled i\n: remove the latter;
//d                          # if the line is empty, delete it
}' file2 <(printf %s\\n)
Run Code Online (Sandbox Code Playgroud)

这将file2在每个匹配行之前插入内容。要仅在第一个匹配行之前插入它,您可以使用loop 并在next 行中拉入,直到到达文件末尾:

sed '/Pointer/{
r file2
N
:l
$!n
$!bl
}
${
/^$/!{
s/\n$//
}
//d
}' file1 <(printf %s\\n)
Run Code Online (Sandbox Code Playgroud)

使用这些sed解决方案,您将无法就地编辑(但您可以重定向到另一个文件)。


dog*_*ane 8

使用循环读取 file2 中的行。如果找到以 开头的行Pointer,则打印出 file1。这如下所示:

#!/bin/bash
while IFS= read -r line
do
    if [[ "$line" =~ ^Pointer.*$ ]]
    then
        cat file1
    fi
    echo "$line"
done < file2
Run Code Online (Sandbox Code Playgroud)


小智 8

[将文件内容插入到另一个文件中 BEFORE 模式]

sed -i '/PATTERN/r file1' -e //N file2
Run Code Online (Sandbox Code Playgroud)

[图案后]

sed -i '/PATTERN/r file1' file2
Run Code Online (Sandbox Code Playgroud)


mik*_*erv 5

有几种方法可以解决这个问题sed。一种方法是延迟阅读,如已接受的答案中所建议的那样。也可以写成:

sed -e '$!N;P;/\nPointer/r file1' -e D file2
Run Code Online (Sandbox Code Playgroud)

...带有一点明确的前瞻,而不是在其他地方使用保持缓冲区实现的后视。但是,@don_crissti 指出的最后一行不可避免地会出现相同的问题,因为N 确实会增加行周期,并且read 命令是按行号应用的。

你可以绕过它:

echo | sed -e '$d;N;P;/\nPointer/r file1' -e D file2 -
Run Code Online (Sandbox Code Playgroud)

并非所有seds 都会将 the 解释-为表示标准输入,但许多会解释。POSIX 说 如果实现者想要表示标准输入,sed应该支持表示标准输入???)--

另一种方法是按顺序处理附加的内容。还有另一个命令以与ead相同的方式安排输出r,并将按照脚本编写的顺序sed应用它和read。不过它有点复杂 - 它需要使用一个sedaPointer匹配项附加到sed其脚本中另一个的输出。

sed '   /Pointer/!d                  #only operate on first match
        s/[]^$&\./*[]/\\&/g;H        #escape all metachars, Hold
        s|.*|/&/!p;//!d|p;g          #print commands, exchange
        s|.|r file1&a\\&|;q' file2|  #more commands, quit
        sed -nf - file2              #same input file
Run Code Online (Sandbox Code Playgroud)

所以基本上第一个sed写第二sed个脚本,第二个sed在标准输入(也许......)上读取并依次应用。第一个sed仅适用于Pointer找到的第一个匹配项,然后是quits 输入。它的工作是...

  1. s/[]^$&\./*[]/\\&/g;H
    • 确保所有模式字符都被安全地反斜杠转义,因为第二个字符sed将需要逐字解释它读取的每一位以使其正确。完成后,将副本放入H旧空间。
  2. s|.*|/&/!p;//!d|p; x
    • 告诉第二sedpRINT每个输入线!,但/&/一个我们刚刚图案萨法德; 然后d删除所有相同的内容。p在第二个 rint 命令sed,然后x更改h旧缓冲区和模式缓冲区以处理我们保存的副本。
  3. s|.|r file1&a\\&|p;q
    • 我们在这里使用的唯一字符是\newline,因为sed在我们H之前设置该行时会预先添加一个字符。所以我们插入命令r file1并在它后面加上我们的\newline,然后是ppend命令a\\a然后是一个\newline。我们Held 线的所有其余部分都遵循最后一条\newline。

第一个编写的脚本如下所示:

/Pointer-file2   "23"/!p;//!d
r file1
a\
Pointer-file2   "23"
Run Code Online (Sandbox Code Playgroud)

基本上,第二个sed将打印每一行,但第一个sed将其设置为append。对于特定的行2延迟写入到标准输出的计划-首先是r的EAD file1,第二个是我们之后要行的副本。sed在这种情况下,第一个的篡改甚至没有必要(看到了吗?没有反斜杠),但是每当模式匹配被重新用作输入时,以我在这里所做的方式安全地转义很重要。

无论如何,所以......有几种方法。