如何根据行的第一个单词替换同一文件中的多个模式?

How to replace multiple patterns in the same file, based on the line's first word?

我在一个文件中有一个短语列表(“phrases”),每个短语都在自己的行上。

我还有另一个文件,其中包含一个单词列表,每个单词占一行 ("words")。

我希望在“phrases”中以“words”中列出的单词开头的每个短语的末尾附加一个星号。

例如:

文件“phrases”:

gone are the days
hello kitty
five and a half
these apples are green

文件“words”:

five
gone

操作后“phrases”的预期结果:

gone are the days *
hello kitty
five and a half *
these apples are green

到目前为止我所做的是:

parallel -j0 -a words -q perl -i -ne 'print " *" if /^({}\s.*)$/' phrases

但这会截断文件,有时(并非总是)会出现此错误:

Can't remove phrases: No such file or directory, skipping file.

因为编辑将同时进行,我的目的是让它只搜索和替换以单词开头的那些行,同时保持其他行不变。否则 parallel 并发执行会互相覆盖。

我也对其他并发方法持开放态度。


为什么这个问题不是并行进程调度?

想象一下价值的内部依赖链,需要在流程输出端以给定的、严格的 [SEQ] 控制方式输出。

事实 1 )
虽然使用 [=31= 很容易分拆多个流程] 语法使它在 shell 级别强制执行,这根本不意味着这种共存进程的每一种情况都允许平滑和智能的“just”-by -巧合-[CONCURRENT] 甚至是真正的-[PARALLEL] 免费流程安排。

事实号 2 )
file:phrases 必须在纯 [SERIAL] 方式,因为自然顺序 ( SEQ ) 很重要并且必须保留,即使对于基于文件的结果输出也是如此.

事实 3)
每个基于文件的设计都是纯 [SERIAL] 过程,两者都不是 "just"-[CONCURRENT], nor true-[PARALLEL],直到有人发明了一种方法,如何使硬盘读取设备的磁头在同一时刻出现在多个位置(这甚至远远超出了亚原子尺度上的量子纠缠和叠加技巧和魔法)。

事实 4 )
当然,可以想象某种 space 用于并发处理,一旦 [SEQ] - 来自 file:phrases 的读取输入是已知的,如果将处理多个([SEQ]-operated)查找,则可能会出现一些加速 - 但是,同样,基于条件,那里都是资源(为了同时进行多个查找,如果不是所有并发进程都无缝执行,则不会对进程流产生任何不利影响)并且所有这些都必须“预缓存”整个“已知” -be"-static-content of file:words(否则无济于事),从而变得有点能够逃脱下一个([SEQ]-again ) pure-[SERIAL] fileIO-[SEQ]-ordered和concurrent-capacity restricted的再处理第一个词匹配查找,现在某种形式的 语法强制要求发生在多个 words- 爬行过程中。


一个人可以很容易地支付比以往任何时候都多的钱:

不正确甚至幼稚​​的进程调度可能并且确实会引入附加成本,这在纯 [SERIAL] 代码执行中是从未见过的。即使是最轻量级的并发框架附加成本(并且这些成本确实与 N 成比例,如果许多并发代码执行似乎变得势在必行,字面上不惜一切代价

敬请read thoroughly details about Amdahl's Law, best altogether with its modern criticism, including the modern re-formulation with having both the overhead-strict add-on costs included and with atomic-units in-divisibility of code-execution, independent of number of processors available. In spite of its inital formulation as far as 50 years ago, the modern massive-parallel code-execution ecosystems still cannot learn better from this principal law's dependencies that no one can ever escape from.

因此,始终检查问题依赖链中的所有 [SEQ] 依赖关系。
因此,在梦想性能之前,请务必检查所有 [PAR]-附加开销。

perl -i -pe'
    BEGIN {
       my $words_qfn = shift(@ARGV);
       open(my $words_fh, "<", $words_qfn) or die $!;
       chomp( my @words = <$words_fh> );
       my $alt = join "|", map quotemeta, @words;
       $re = qr/^(?:$alt)\b.*\K/;
    }
    s/$re/ */;
' words phrases

这不太适合并行处理,因为到目前为止,您可以执行的最昂贵的操作(通常)是从磁盘读取。 CPU 快得多。

您的问题并不 CPU 密集,因此您不会从并行 运行 中获得太多优势。更糟糕的是 - 正如您所发现的那样 - 您会引发可能导致文件破坏的竞争条件。

实际上,磁盘 IO 是以块的形式完成的 - 多个 K - 来自磁盘,将其提取到缓存中,然后以您可以假装 read 工作的方式提供给 OS逐字节。

如果您按顺序读取文件,预测性提取可以让 OS 更加高效,并且尽可能快地将整个文件拉入缓存,从而大大加快处理速度。

尝试并行化和交织此过程充其量是没有效果的,而且会使事情变得更糟。

所以考虑到这一点,你最好不要尝试并行,而是:

#!/usr/bin/env perl

use strict;
use warnings;

open ( my $words_fh, '<', 'words' ) or die $!; 
my $words = join '|', map { s/\n//r } <$words_fh>;
   $words = qr/\b(?:$words)\b/;
close ( $words_fh );

print "Using match regex of: ", $words, "\n";

open ( my $phrases_fh, '<', 'phrases' ) or die $!;
while ( <$phrases_fh> ) { 
  if (m/$words/) {
      s/$/ */;
  }
  print;
} 

将输出重定向到所需位置。

最昂贵的部分是读取文件 - 它只执行一次。为每个搜索词的同一行重复调用正则表达式引擎也很昂贵,因为您将执行 N * M 次,其中 N 是单词数,M 是行数。

因此,我们编译一个正则表达式,并使用零宽度 \b 字边界标记匹配它(因此它不会匹配子字符串)。

注意 - 我们不引用 words 的内容 - 这可能是错误或功能,因为这意味着您可以将正则表达式添加到组合中。 (当我们编译正则表达式时,这可能会中断)。

如果你想确保它是'literal',那么:

my $words = join '"', map { quotemeta } map { s/\n//r } <$words_fh>;