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


32

File1 内容:

line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 

File2 内容:

line1-file2     "25"  
line2-file2     "24"  
Pointer-file2   "23"  
line4-file2     "22" 
line5-file2     "21"

执行完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"

即,将File1in 的内容粘贴在File2包含“ Pointer”的行之前。




由于这个原因,我已经对SO问题投了赞成票,因此没有更多理由解决这个问题。顺便说一句,问题是SO的质量要差得多,因此逻辑要求关闭而不是关闭它。
彼得说恢复莫妮卡的时间

Answers:


33

sed 有一个功能,可以内联修改:

sed -i -e '/Pointer/r file1' file2

但这会将您的Pointer行放在file1上方。放在下面,延迟线输出:

sed -n -i -e '/Pointer/r file1' -e 1x -e '2,${x;p}' -e '${x;p}' file2 

8
您能解释一下该怎么-e 1x -e '2,${x;p}' -e '${x;p}'做吗?我知道您先在模式缓冲区中交换内容,然后再打印它,但我不知道为什么也不为什么-n在开始时就添加了安静选项。
hdl 2015年

@ jfg956是否可以仅替换和删除原始文件中的“指针”部分。我可以通过再次扫描sed来解决这个问题,但是是否可以一次运行呢?
Alexander Cska

17

不使用sedawk...

首先,找到您的模式行:

line=$(grep -n 'Pointer' file2 | cut -d ":" -f 1)

然后,使用3个命令输出所需的结果:

{ head -n $(($line-1)) file2; cat file1; tail -n +$line file2; } > new_file

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


10

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

awk '/Pointer/{while(getline line<"innerfile"){print line}} //' outerfile >tmp
mv tmp outerfile

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

awk '//; /Pointer/{while(getline<"innerfile"){print}}' outerfile >tmp
mv tmp outerfile

只是因为没有人用过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

它的工作,但它删除包含指针的行
user1228191 2012年

同样,如何使用awk
user1228191 2012年

@ user1228191固定了第一个,添加了第二个。
凯文(Kevin)

“ perl”版本似乎无效。system("cat innerfile")将输出innerfile到控制台。我想念什么吗?
kaartic '17

awk命令[gawk'/ <body> / {while(getline line <“ $ HOME / bin / SrunScenario.style”){print line}} //'index.html> new_index.html]仅循环并打印数百万行。gawk V4.2.0我在这里想念什么?
JESii

7

一个简单的工作ed

ed -s file1 <<IN
/Pointer/-r file2
,p
q
IN

-r file1在指定的文件中读取到所寻址的行之后的地址,在这种情况下,该行是第一行match之前的行Pointer。因此,file2即使Pointer发生在多行中,这也只会插入一次内容。如果要在每条匹配行之前插入它,请添加global标志:

ed -s file1 <<IN
g/Pointer/-r file2
,p
q
IN

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


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

sed '/Pointer/{r file1
N}' file2

它也可以正常工作(就像r在循环结束时一样神奇),但是如果标记位于最后一行(N在最后一行之后没有下一行),则会遇到相同的问题。要解决此问题,您可以在输入中添加换行符:

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)

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

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

使用这些sed解决方案,您将失去就地编辑的能力(但是您可以重定向到另一个文件)。


6

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

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

4

有几种方法可以解决此问题sed。一种方法是按照已接受的答案中的建议延迟阅读。也可以这样写:

sed -e '$!N;P;/\nPointer/r file1' -e D file2

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

您可以绕开它:

echo | sed -e '$d;N;P;/\nPointer/r file1' -e D file2 -

并非所有seds都会将s解释-为标准输入,但很多人会这样做。POSIX表示 ,如果实现者要表示标准输入,sed则应支持表示标准输入。)--

另一种方法是按顺序处理附加内容。还有另一条命令,以与ead 相同的方式安排输出r,并按照脚本编写的顺序sed应用它和read。这是一个涉及多一点,但-它需要使用一个sedaPPEND的Pointer比赛的另一个输出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

因此,基本上,第一个脚本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\\,然后再添加ewline。我们其他所有领域都遵循最后一条线。a\nH\n

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

/Pointer-file2   "23"/!p;//!d
r file1
a\
Pointer-file2   "23"

基本上,第二个sed将打印的每一行,但一个第一sed组就高达aPPEND。对于特定的行2延迟写入到标准输出的计划 -首先是r的EAD file1,第二个是我们之后要行的副本。第一次sed的刮墨甚至没有必要在这种情况下(见?没有反斜杠),但它在我当一个模式匹配时改变用途作为输入,这里做的方式逃避安全是非常重要的。

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


2

对于AWK,这非常简单:

在模式=“指针”之前将File1转换为File2

首先将File1的内容加载到变量中

f1="$(<File1)"

然后插入

awk -vf1="$f1" '/Pointer/{print f1;print;next}1' file2

(或者,如果要在“指针”之后插入File1)

awk -vf1="$f1" '/Pointer/{print;print f1;next}1' file2

2

我的首选方式:模板化

sed 's/CHANGEME/$x/g' origfile | x="$(<file2insert)" envsubst '$x' > newfile

这将取代每个CHANGEME在occurence origfile与内容file2insert。从sed中删除最后一个g,仅替换第一次出现的CHANGEME


$x当仅在第二条命令中定义时,如何在第一条命令中使用?
Totor

第一个命令中的“ $ x”仅是第二个命令中的envsubst要评估的占位符。使用sed脚本的单引号,$ x不会被您的shell评估。
nrc

2

[在模式之前将文件内容插入另一个文件]

sed -i '/PATTERN/r file1' -e //N file2

[图案后]

sed -i '/PATTERN/r file1' file2

N效果很好,但如果PATTERN与输入的最后一行匹配,则效果很好
Sundeep

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.