将 file1 中的 M 行替换为 file2 中的 N 行

将 file1 中的 M 行替换为 file2 中的 N 行

M=3N=4.我想用 line Min替换file1line Nin file2。我可以使用以下命令替换字符串sed

sed -i '3s/.*/stringToReplace/' file1

我可以用它来awk获取线路Nfile2

awk 'NR==4' "file2"

我怎样才能将这两者结合起来?如果我尝试

sed -i '3s/.*/{awk 'NR==4' "file2"}/' file1

然后我M用字面意思替换 line {awk 'NR==4' "file2"}

答案1

有多种方法可以正确执行此操作以处理任意输入。
使用 GNUsed和支持以下功能的系统/dev/stdin

sed -n "${n}{p;q;}" file2 | sed -e "$m{r/dev/stdin" -e 'd;p;}' file1

或者,稍微短一点 1

sed $n'!d;q' file2 | sed -e $m'{r /dev/stdin' -e 'd;p;}' file1

使用任何sed支持进程替换的 shell

sed '1h;1d;'$((m+1))'x' <(sed ${n}'!d;q' file2) file1

也可以写成

sed ${n}'!d;q' file2 | sed '1h;1d;'$((m+1))'x' - file1

基本上,一个sed调用从中提取行,n然后file2由另一个调用sed作为第一个操作数读取:它将其保存到保持缓冲区中,删除它,然后读取第二个操作数的内容,即file1,在线时交换缓冲区m+1(组合输入) )。

任何支持通过以下sed方式读取脚本文件的人都可以运行:-fstdin

sed ${n}'!d;i\
'${m}'c\\
s/\\/&&/g
q' file2 | sed -f - file1

在这里,第一个sed将行n变成file2脚本文件,例如

${m}c\
line_n_content_here_with_any_backslash_escaped

然后由第二个用来sed处理file1(即将行替换m为以下文本...)。原始文本中存在的任何反斜杠(以及任何嵌入的换行符 - 但这里只有一行)都必须被转义,因为当使用任何a\,i\c\添加文本时

<backslash> characters in text shall be removed, and the following character shall be treated literally.

对于任何sed,您都可以使用一直流行的substitute 命令确保插入sed替换的字符串转义所有保留字符- 在这种特殊情况下,它只是一行,例如

line=$(sed ${m}'!d;s|[\/&]|\\&|g;q' file2)

然后替换:

sed ${m}'s/.*/'"$line"'/' file1

对于巨大的输入文件,您可以运行:

{ head -n $((m-1)); { head -n $((n-1)) >/dev/null; head -n 1; } <file2; head -n 1 >/dev/null; cat; } <file1

它做了这样的事情:

print (m-1) lines from file1
discard (n-1) lines from file2
print n-th line from file2
discard m-th line from file1
print the remaining lines from file1

虽然有些head人很蠢而且不会符合标准所以这并不适用于所有设置......但在它适用的情况下,它在速度方面胜过sed等。awk


1:对于某些 shell,您可能需要禁用历史扩展才能!工作...
另外,$n并且$m实际上不需要在这里引用,因为它们应该是正整数,尽管它也没有什么坏处

答案2

尝试这个:

$ cat f1
foo
bar
xyz
baz
temp
good
$ cat f2
1
2
3
4
5
6

$ awk -v m=3 -v n=4 'NR==FNR{if(FNR==n) s=$0; next} FNR==m{$0=s} 1' f2 f1
foo
bar
4
baz
temp
good
  • NR==FNR仅当第一个文件正在处理时才为真
  • if(FNR==n) s=$0如果是第n行,则保存到变量
  • next这样只要处理第一个文件就不会执行其余代码
  • FNR==m{$0=s}如果是第二个文件参数的第 m 行,则替换它
  • 1打印输入记录,包括任何修改
  • 注意文件输入参数的顺序

可以用来if(FNR==n){s=$0;nextfile}避免处理第 n 行之后的行

GNU awk 手册- 谢谢@iruvar

注意:多年来,nextfile 是一个常见的扩展名。 2012 年 9 月,它被纳入 POSIX 标准。请参阅奥斯汀集团网站。

答案3

短的sed方法:

样本文件f1

f1 line1
f1 line2
f1 line3
f1 line4
f1 line5

样本文件f2

ID1,value12,value13
ID1,value22,value23
ID1,value32,value33
ID2,/value42/,~value43~
ID3,value52,value53

工作:

sed '3 s/.*/'"$(sed -n '4{ s/\//\\\//g;p;}' f2)"'/;' f1

输出:

f1 line1
f1 line2
ID2,/value42/,~value43~
f1 line4
f1 line5

答案4

不要将 awk 与 sed 混合使用。您可以使用完整的 awk 解决方案

文件1:

 file1 1
 file1 2
 file1 3
 file1 4

文件2:

 file2 1
 file2 2
 file2 3
 file2 4


 awk -v m=3 -v n=4 'NR == FNR { filea[FNR]=$0 } FNR != NR { fileb[FNR]=$0 } END { for (i=1;i<=FNR;i++) { if ( i == m ) { print fileb[n] } else { print filea[i] } } } ' file1 file2

细分:

 NR == FNR { 
            filea[FNR]=$0 
           }
 FNR != NR { 
            fileb[FNR]=$0 
           }
 END { 
       for (i=1;i<=FNR;i++) { 
                               if ( i == m ) { 
                                         print fileb[n] 
                                             } 
                               else { 
                                          print filea[i] 
                                    } 
                            } 
      } 

我们比较 NR 和 FNR 来确定 file1 和 file2 中的记录(当 NR=FNR 时,我们知道我们在第一个文件中)。我们用 file1 中的记录设置数组 filea,用 file2 中的记录设置 fileb。然后,我们循环遍历数组 filea 中的所有记录,仅当传递的参数 m 不等于 3 时才打印内容。如果是,则打印由传递的参数 n 确定的数组 fileb 的下标

相关内容