Reputation: 24383
I am using sed
to find and replace items in a file:
sed -i "s/$pattern/$replacement/g" ./file.txt
This will replace all appearances in a file.
Is there any way to randomly replace just one of the occurrences within an entire file?
Upvotes: 2
Views: 248
Reputation: 35198
The only way to do this truly randomly would be to inventory all the locations of the PATTERN first. Otherwise there is no way to give proper weighting.
The following does the replacement via a script. It only replaces the first reference of a pattern on a specific line currently:
use strict;
use warnings;
use autodie;
my $file = '...';
my $pattern = qr/.../;
my $replace = '...';
my @linenums;
local @ARGV = $file;
while (<>) {
push @linenums, $_ if $_ =~ $pattern;
}
my $linenum = $linenums[rand @linenums];
local @ARGV = $file;
local $^I = '.bak';
while (<>) {
s/$pattern/$replace/ if $. == $linenum;
print;
}
# unlink "$file$^I"; # Optionally Delete backup
If you want to allow a pattern to be on a line more than once, the following enhancement would work:
use strict;
use warnings;
use autodie;
my $file = '...';
my $pattern = qr/.../;
my $replace = '...';
my @linenums;
local @ARGV = $file;
while (<>) {
my $count = () = $_ =~ /$pattern/g;
push @linenums, {line => $., num => $_} for 0 .. $count - 1;
}
die "No matches found" unless @linenums;
my $pick = $linenums[rand @linenums];
local @ARGV = $file;
local $^I = '.bak';
while (<>) {
if ($. == $pick->{line}) {
s/(?:$pattern.*?){$pick->{num}}\K$pattern/$replace/;
}
print;
}
# unlink "$file$^I"; # Optionally Delete backup
Upvotes: 1
Reputation: 77105
Using perl
, you can use rand
function:
perl -lane '
while ($n = rand @F) {
if ($F[$n] eq "pattern") {
$F[$n] = "replacement";
print "@F";
last
}
}
' file
As per this post, variables from the shell are available in perl's %ENV
hash. With bash
(and some other shells) you need to take the extra step of "exporting" your shell variable so it is visible to subprocesses.
So in your case, you'll have to do:
pattern="mypattern"
replacement="myreplacement"
export pattern
export replacement
perl -lane '
while ($n = rand @F) {
if ($F[$n] eq "$ENV{pattern}") {
$F[$n] = "$ENV{replacement}";
print "@F";
last
}
}
' file
Of course, this will print to STDOUT so if you need to make in-file changes, you can either use -i
command line option or redirect the output to another file.
Alternatively, if this is not part of your bash
script and you want to do it in perl
, then you can pass the pattern and replacement on the command line.
perl -lane '
BEGIN { ($patt, $repl) = splice @ARGV, 1 }
while ($n = rand @F) {
if ($F[$n] eq $patt) {
$F[$n] = $repl;
print "@F";
last
}
}' file "pattern" "replacement"
This will do replacement randomly once per line. If you wish to do it once per file, please leave a comment and I will add that version.
Upvotes: 1
Reputation: 75488
Using perl
:
#!/usr/bin/env perl
use List::Util qw(shuffle);
use strict;
use warnings;
my $search = shift @ARGV;
my $repl = shift @ARGV;
my @lines;
my @matches;
while (<>) {
push(@lines, $_);
push(@matches, $.) if /$search/;
}
my @shuffled = shuffle(@matches);
my $index = shift @shuffled;
if ($index) {
$lines[$index - 1] =~ s/$search/$repl/;
}
print @lines;
Usage:
perl script.pl string replacement file
Upvotes: 1