【问题标题】:Replace part of line with different text from another file line by line用来自另一个文件的不同文本逐行替换部分行
【发布时间】:2023-03-14 00:57:02
【问题描述】:

我试图解决这个问题,但我可以只替换整个字符串而不是部分

这就是我目前所拥有的:

cat mock.txt | sed -i -e '/mock/{R/dev/stdin' -e 'd;}' test{1..500}.txt

我想从另一个文件中按顺序替换与行中的模式匹配的部分行,我有多达 500 个 txt 文件,结构为:

test1.txt, test2.txt , test3.txt...

11111
22222
333= mock 33
55555
77777


所以我一个接一个地读取文件,并在第一个 test1.txt 文件中用 mock.txt 文件中的第一行替换模拟模式,在第二个 test2.txt 中用 mock.txt 文件中的第二行替换模拟模式行,该文件具有结构喜欢:

mock.txt

randomText1
randomText2
randomText3
randomText4
randomText5


依此类推,直到文件夹中的最后一个 .txt 文件

【问题讨论】:

  • 1 澄清一下,是不是每个文本文件中只有 1 行会有mock 字符串,或者还有更多请告诉我们?
  • 它可能有很多
  • 好的,所以每个下一个文件都需要将第一个模拟字符串出现替换为模拟文件的第一个值?或者它应该从最后一个文件继续计数?
  • 例如每个 test{n}.txt 文件都有一个或多个模拟出现,因此第一个文件中的所有模拟模式都应该替换为 mock.txt 中的第一行,依此类推跨度>
  • 您能否;请尝试我的 EDIT 解决方案并告诉我,在 1 或 2 个文件上尝试一次(测试文件),然后看看效果如何?

标签: ubuntu awk sed


【解决方案1】:

抱歉之前的错误,请在下面重试。
使用 GNU awk v4.1.0+(请先备份您的文件):

awk -i inplace 'NR==FNR{re[FNR]=$0;print;next}match(FILENAME,/test([0-9]+)/, fi){gsub("mock",re[fi[1]])}1' mock.txt test{1..500}.txt

注意文件不能有回车\r\n结尾,如果有,请评论。

如果文件包含多行,那么为了提高效率并将其放在多行中以便于阅读:

awk -i inplace '
    NR==FNR{re[FNR]=$0;print;next}
    FNR==1{match(FILENAME,/test([0-9]+)/, fi)}
    /mock/{gsub(/mock/,re[fi[1]])}
    1' mock.txt test{1..500}.txt

【讨论】:

  • 如果我想用当前位置 + 2 或与 mock.txt 的当前位置不同的任何位置更改所有出现的模拟,代码会发生很大变化吗?与 text1.txt 一样,我们将 mock 更改为 randomText3,使用 text2.txt -> randomText5 等等
  • @Pursentiy 给定mock.txt 有冗余行,将第一个re[FNR] 更改为re[FNR-2] OR 最后一个re[fi[1]] 更改为@987654328 @ 会做的。
【解决方案2】:

编辑: 因为 OP 说每次文件替换都应该从模拟文件中第一次出现模拟字符串开始,所以现在发布。

awk '
FNR==NR{
  a[FNR]=$0
  next
}
prev!=FILENAME{
  if(prev){
    close("temp")
    system("mv  temp " prev)
  }
}
FNR==1{
  prev=FILENAME
  count=""
}
/mock/{
  $0=a[++count]
}
{
  print > "temp"
}
END{
  if(prev){
    close("temp")
    system("mv  temp " prev)
  }
}' mock.txt test[0-9].txt


请您尝试以下操作。在 shost 解释(将添加完整解释)中,这将根据 mock.text 文件的值更改字符串模拟值。我添加了一条评论,询问 OP 一个文件是否可以出现超过 1 次,也会根据 OP 的回答对其进行更新。

awk '
FNR==NR{
  a[FNR]=$0
  next
}
prev!=FILENAME{
  if(prev){
    close("temp")
    system("mv  temp " prev)
  }
}
FNR==1{
  prev=FILENAME
}
/mock/{
  $0=a[++count]
}
{
  print > "temp"
}
END{
  if(prev){
    close("temp")
    system("mv  temp " prev)
  }
}' mock.txt test[0-9].txt

【讨论】:

  • @Pursentiy,请立即尝试我的 EDIT 解决方案并告诉我?另外,我建议您尝试使用 2 或 3 个字段,一旦看起来不错,然后仅针对所有文本文件运行。
  • @Pursentiy,很酷,给个时间,当你看到很多答案时,请选择其中任何一个作为正确答案,学习愉快。
【解决方案3】:

这可能对你有用(GNU sed):

sed -i -e '/mock/R mockFile' -e '//d' file ...

注意模拟文件在 sed 脚本中命名,而不是通过标准输入作为输入。这允许 sed 为每个输入文件重置 mockFile。

这将用模拟文件中的一行顺序替换mock 的所有实例。但是,如果您想将每个文件中的所有模拟实例链接到其在模拟文件中的位置,即 file1 中的所有模拟实例到模拟文件的第 1 行等,请使用:

parallel sed -i '/mock/c\{1}' {2} :::: mockFile :::+ file{1..500}.txt

【讨论】:

  • 它会删除 mock.txt 中的所有行,并且所有 test{n}.txt 文件都保持不变,Awk 4.1.4。
  • @Pursentiy 这是一个 sed 解决方案而不是 awk 并且模拟文件是只读的,也许你可以展示你的工作来澄清。
  • 对不起,我认为@Pursentiy 是在评论我的回答,我发现它有问题,所以我暂时删除了。
  • @potong 问题已解决,谢谢帮助!
【解决方案4】:

你不能通过一步使用sed来做到这一点。

来自info sed

'R filename'
     Queue a line of FILENAME to be read and inserted into the output
     stream at the end of the current cycle, or when the next input line
     is read.

因此,为了做到这一点,您必须进行 第二次通过

cd /tmp
seq -f first-%03g 1 20 | tee test-{1..30}.txt >/dev/null 
seq -f newline-%04g 44 244 | sed -i -e '/first-004/R/dev/stdin' test-{1..30}.txt
sed -e '/first-004/{N;s/^\(.*\)rst-00\(.*\)\n\(.*\)/\1\3\2/}' -i test-{1..30}.txt

然后

head -6 test-6.txt
first-001
first-002
first-003
finewline-00494
first-005
first-006

其中rst-00 被替换为newline-0049 在线原来是first-004

从那里,匹配您的请求,但未经测试:

有两个pass:

sed -e '/mock/R/dev/stdin' -i test{1..500}.txt <mock.txt
sed -e '/mock/{N;s/^\(.*\)mock\(.*\)\n\(.*\)$/\1\3\2/}' -i test{1..500}.txt

【讨论】:

  • 嗯...现在看来是正确的,只需测试这篇文章的最后两行。
猜你喜欢
  • 2014-02-03
  • 1970-01-01
  • 2012-04-04
  • 2019-06-04
  • 1970-01-01
  • 1970-01-01
  • 2017-04-30
  • 2022-08-13
  • 2019-03-22
相关资源
最近更新 更多